From 84725100cffbd5ea77f8395f75176c055e9cba0a Mon Sep 17 00:00:00 2001 From: Jay Yu Date: Sun, 25 Oct 2026 11:59:58 -0400 Subject: [PATCH 1/3] chore(tooling): adopt stable TypeScript 7 and Biome 2.6 --- biome.json | 4 +- package-lock.json | 721 +++++++++++++---- package.json | 14 +- packages/agent/package.json | 2 +- packages/agent/src/harness/reducer.ts | 4 +- packages/agent/src/harness/session/state.ts | 2 +- packages/ai/src/api/constrained-sampling.ts | 4 +- packages/ai/src/api/mistral-conversations.ts | 6 +- .../ai/src/api/openai-responses-shared.ts | 4 +- .../ai/test/anthropic-opus-4-8-smoke.test.ts | 2 +- .../ai/test/bedrock-thinking-payload.test.ts | 57 +- .../ai/test/cross-provider-handoff.test.ts | 2 +- packages/ai/test/image-tool-result.test.ts | 4 +- packages/ai/test/interleaved-thinking.test.ts | 2 +- ...i-completions-cache-control-format.test.ts | 6 +- ...enai-responses-foreign-toolcall-id.test.ts | 2 +- ...nai-responses-partial-json-cleanup.test.ts | 4 +- ...nai-responses-reasoning-replay-e2e.test.ts | 2 +- ...penai-responses-tool-result-images.test.ts | 2 +- .../test/openrouter-cache-write-repro.test.ts | 109 ++- packages/ai/test/total-tokens.test.ts | 735 +++++++++--------- packages/ai/test/unicode-surrogate.test.ts | 39 +- .../examples/extensions/minimal-mode.ts | 12 +- .../coding-agent/install-lock/package.json | 4 +- packages/coding-agent/package.json | 7 +- .../coding-agent/src/client/transcript.ts | 2 +- .../coding-agent/src/core/extensions/types.ts | 2 +- packages/coding-agent/src/core/keybindings.ts | 2 +- packages/coding-agent/src/core/sdk.ts | 16 +- .../src/core/subagents/controller.ts | 2 +- .../interactive/components/config-selector.ts | 2 +- .../src/modes/interactive/components/diff.ts | 4 +- .../interactive/components/model-selector.ts | 2 +- .../src/modes/interactive/interactive-mode.ts | 2 +- .../coding-agent/src/utils/clipboard-image.ts | 2 +- .../src/utils/owned-shell-processes.ts | 2 +- .../test/streaming-render-debug.ts | 2 +- packages/evals/package.json | 2 +- packages/evolution/test/skill-curator.test.ts | 15 +- .../src/transports/websocket/listener.ts | 2 +- .../sqlite-node/src/sqlite/repo.ts | 2 +- packages/telemetry/src/index.ts | 43 +- scripts/check-ts-relative-imports.mjs | 94 ++- scripts/check-ts-relative-imports.test.mjs | 145 ++++ tsconfig.base.json | 4 +- 45 files changed, 1319 insertions(+), 778 deletions(-) create mode 100644 scripts/check-ts-relative-imports.test.mjs diff --git a/biome.json b/biome.json index c306633ce..67b3e1c29 100644 --- a/biome.json +++ b/biome.json @@ -1,9 +1,9 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.5/schema.json", + "$schema": "https://biomejs.dev/schemas/2.6.3/schema.json", "linter": { "enabled": true, "rules": { - "recommended": true, + "preset": "recommended", "style": { "noNonNullAssertion": "off", "useConst": "error", diff --git a/package-lock.json b/package-lock.json index 3410298b8..0838cdb7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,16 +17,16 @@ ], "devDependencies": { "@anthropic-ai/sandbox-runtime": "0.0.26", - "@biomejs/biome": "2.3.5", + "@biomejs/biome": "2.6.3", "@opentelemetry/api": "1.10.0", "@types/node": "26.6.5", "@typescript/native-preview": "7.0.0-dev.20260120.1", - "esbuild": "0.28.1", + "esbuild": "0.28.2", "husky": "9.1.7", "jiti": "2.7.0", "shx": "0.4.0", - "tsx": "4.22.1", - "typescript": "5.9.3" + "tsx": "4.23.15", + "typescript": "7.0.2" }, "engines": { "node": ">=22.19.0" @@ -249,9 +249,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.5.tgz", - "integrity": "sha512-HvLhNlIlBIbAV77VysRIBEwp55oM/QAjQEin74QQX9Xb259/XP/D5AGGnZMOyF1el4zcvlNYYR3AyTMUV3ILhg==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.6.3.tgz", + "integrity": "sha512-va/0xPku7wnfzv+D8Zpdm5uKiGJWiV8neqoEPnRjvNQQwhl4tVXS4fETs4H0CCB2rGeqJ4cu9y4iOZOnOYKAUA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -265,20 +265,56 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.3.5", - "@biomejs/cli-darwin-x64": "2.3.5", - "@biomejs/cli-linux-arm64": "2.3.5", - "@biomejs/cli-linux-arm64-musl": "2.3.5", - "@biomejs/cli-linux-x64": "2.3.5", - "@biomejs/cli-linux-x64-musl": "2.3.5", - "@biomejs/cli-win32-arm64": "2.3.5", - "@biomejs/cli-win32-x64": "2.3.5" + "@biomejs/cli-android-arm64": "2.6.3", + "@biomejs/cli-android-x64": "2.6.3", + "@biomejs/cli-darwin-arm64": "2.6.3", + "@biomejs/cli-darwin-x64": "2.6.3", + "@biomejs/cli-linux-arm64": "2.6.3", + "@biomejs/cli-linux-arm64-musl": "2.6.3", + "@biomejs/cli-linux-x64": "2.6.3", + "@biomejs/cli-linux-x64-musl": "2.6.3", + "@biomejs/cli-win32-arm64": "2.6.3", + "@biomejs/cli-win32-x64": "2.6.3" + } + }, + "node_modules/@biomejs/cli-android-arm64": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-android-arm64/-/cli-android-arm64-2.6.3.tgz", + "integrity": "sha512-5JD7QQDEHxdeEb4sftEHG+lsuUyu8Cob/hVLqRELbgGlFl68Rldv/PPi2k4dRjERZ/EItJ3O6Xmv8lqsEtUBiw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-android-x64": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-android-x64/-/cli-android-x64-2.6.3.tgz", + "integrity": "sha512-70WOGKCgpT+Uqg6LFdTLqefV54/DypdZg+LnBQDO6AWvkgXO+9xhVClajQyDsIHYjnXMtI1pRO5sP+zMXlS/bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.5.tgz", - "integrity": "sha512-fLdTur8cJU33HxHUUsii3GLx/TR0BsfQx8FkeqIiW33cGMtUD56fAtrh+2Fx1uhiCsVZlFh6iLKUU3pniZREQw==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.6.3.tgz", + "integrity": "sha512-ezf+XEKeyJ2yYlBhrzyEEHLvfRYrhtNKc6maLzR/WcAcBccb4RVDeFxsymjBve2LO0bpIMRUAiS90D4bUBRT0A==", "cpu": [ "arm64" ], @@ -293,9 +329,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.5.tgz", - "integrity": "sha512-qpT8XDqeUlzrOW8zb4k3tjhT7rmvVRumhi2657I2aGcY4B+Ft5fNwDdZGACzn8zj7/K1fdWjgwYE3i2mSZ+vOA==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.6.3.tgz", + "integrity": "sha512-Lg2tFdg4elJo+7i707PpLMs6N1hATVt4kqDo6MD/q/VnPs8rsKReKXsz1TA5GNdfRVAQNJ5GRiyK2EwNptK+MA==", "cpu": [ "x64" ], @@ -310,9 +346,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.5.tgz", - "integrity": "sha512-u/pybjTBPGBHB66ku4pK1gj+Dxgx7/+Z0jAriZISPX1ocTO8aHh8x8e7Kb1rB4Ms0nA/SzjtNOVJ4exVavQBCw==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.6.3.tgz", + "integrity": "sha512-8Stcvq98n1gs9VyLKuaWrnsd8kbXE2nKDAHtuLHGSYdGyNWN5JIIm5a28xdnmgdKzKHmiDgYyATetkEv7+zGow==", "cpu": [ "arm64" ], @@ -327,9 +363,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.5.tgz", - "integrity": "sha512-eGUG7+hcLgGnMNl1KHVZUYxahYAhC462jF/wQolqu4qso2MSk32Q+QrpN7eN4jAHAg7FUMIo897muIhK4hXhqg==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.6.3.tgz", + "integrity": "sha512-dYU70A89OFHzakQPE9VOPbE60JNcQt4mtxvUGQl+EgTkGtqCFxLD2RUn1SbiMu8Mu5xFscS1bfYDpoasaUrqNA==", "cpu": [ "arm64" ], @@ -344,9 +380,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.5.tgz", - "integrity": "sha512-XrIVi9YAW6ye0CGQ+yax0gLfx+BFOtKaNX74n+xHWla6Cl6huUmcKNO7HPx7BiKnJUzrxXY1qYlm7xMvi08X4g==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.6.3.tgz", + "integrity": "sha512-CShmx0bO6BngywT3minv8K1xsQX1ig1vsBqYTLnmDY3mBiDYHlaLRIsdHoM1pEU9dV3BN5a0uiH0IGDtYh9TWA==", "cpu": [ "x64" ], @@ -361,9 +397,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.5.tgz", - "integrity": "sha512-awVuycTPpVTH/+WDVnEEYSf6nbCBHf/4wB3lquwT7puhNg8R4XvonWNZzUsfHZrCkjkLhFH/vCZK5jHatD9FEg==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.6.3.tgz", + "integrity": "sha512-j6eFjUcWSphmgVTbrobEMA0wdbfNHKrGi8hRsFkc17jeS1n7nHwFACxp6jWqe9sxQqyFAeyOgt2/1bOGVcbATA==", "cpu": [ "x64" ], @@ -378,9 +414,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.5.tgz", - "integrity": "sha512-DlBiMlBZZ9eIq4H7RimDSGsYcOtfOIfZOaI5CqsWiSlbTfqbPVfWtCf92wNzx8GNMbu1s7/g3ZZESr6+GwM/SA==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.6.3.tgz", + "integrity": "sha512-00SthDxBPwNiT7IL/mOneif6fiEU1PXK875zhr+InpeZjovY1mF5tCuDhXo1Hh0GYyiKHKzLbItzn58SAyLhJQ==", "cpu": [ "arm64" ], @@ -395,9 +431,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.5.tgz", - "integrity": "sha512-nUmR8gb6yvrKhtRgzwo/gDimPwnO5a4sCydf8ZS2kHIJhEmSmk+STsusr1LHTuM//wXppBawvSQi2xFXJCdgKQ==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.6.3.tgz", + "integrity": "sha512-GhtOPphBWlnCepqpNk/w5eGN5oGRCzL20EbAkL1XCIpyboeVC9R642noyT43l8LSenGS4qz9IL8sKPfwY37V/Q==", "cpu": [ "x64" ], @@ -468,9 +504,9 @@ "link": true }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -485,9 +521,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -502,9 +538,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -519,9 +555,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -536,9 +572,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -553,9 +589,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -570,9 +606,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -587,9 +623,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -604,9 +640,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -621,9 +657,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -638,9 +674,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -655,9 +691,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -672,9 +708,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -689,9 +725,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -706,9 +742,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -723,9 +759,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -740,9 +776,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -757,9 +793,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -774,9 +810,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -791,9 +827,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -808,9 +844,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -825,9 +861,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -842,9 +878,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -859,9 +895,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -876,9 +912,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -893,9 +929,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -1673,13 +1709,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/diff": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz", - "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -1868,6 +1897,346 @@ "win32" ] }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@vitest/coverage-v8": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.12.tgz", @@ -2589,9 +2958,9 @@ } }, "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2602,32 +2971,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escape-html": { @@ -5278,9 +5647,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.1.tgz", - "integrity": "sha512-TvncJykhxAzFCk0VQZKBTClall4Pm7qXDSodb6uxi8QFa8X8mT6ABjxxsQ2opDRYxG7AzcRWXaFtruz5HJKuWg==", + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", "dev": true, "license": "MIT", "dependencies": { @@ -5347,17 +5716,38 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", "dev": true, "license": "Apache-2.0", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "tsc": "bin/tsc" }, "engines": { - "node": ">=14.17" + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" } }, "node_modules/undici-types": { @@ -5701,7 +6091,7 @@ "devDependencies": { "@types/node": "26.6.5", "@vitest/coverage-v8": "4.1.12", - "typescript": "5.9.3", + "typescript": "7.0.2", "vitest": "4.1.12" }, "engines": { @@ -6238,14 +6628,13 @@ }, "devDependencies": { "@types/cross-spawn": "6.0.6", - "@types/diff": "7.0.2", "@types/hosted-git-info": "3.0.5", "@types/ms": "2.1.0", "@types/node": "26.6.5", "@types/proper-lockfile": "4.1.4", "@types/semver": "7.8.0", "shx": "0.4.0", - "typescript": "5.9.3", + "typescript": "7.0.2", "vitest": "4.1.12" }, "engines": { @@ -6584,7 +6973,7 @@ "@diffexai/diffex-ai": "^0.3.2", "@types/node": "26.6.5", "shx": "0.4.0", - "typescript": "5.9.3", + "typescript": "7.0.2", "vitest": "4.1.12", "vitest-evals": "0.17.0" } diff --git a/package.json b/package.json index 087219032..ae7c22ef3 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "clean": "npm run clean --workspaces", "build": "cd packages/harness-state && npm run build && cd ../tui && npm run build && cd ../telemetry && npm run build && cd ../harness-trace && npm run build && cd ../evolution && npm run build && cd ../ai && npm run build && cd ../agent && npm run build && cd ../session-backends/sqlite-node && npm run build && cd ../../protocol && npm run build && cd ../client && npm run build && cd ../server && npm run build && cd ../coding-agent && npm run build && cd ../coding-agent-server && npm run build", "build:offline": "cd packages/harness-state && npm run build && cd ../tui && npm run build && cd ../telemetry && npm run build && cd ../harness-trace && npm run build && cd ../evolution && npm run build && cd ../ai && npm run build:offline && cd ../agent && npm run build && cd ../session-backends/sqlite-node && npm run build && cd ../../protocol && npm run build && cd ../client && npm run build && cd ../server && npm run build && cd ../coding-agent && npm run build && cd ../coding-agent-server && npm run build", - "check": "biome check --write --error-on-warnings . && npm run check:identities && npm run check:pinned-deps && npm run check:ts-imports && npm run check:shrinkwrap && npm run check:install-lock:coding-agent && tsgo --noEmit && npm run check:browser-smoke", + "check": "biome check --write --error-on-warnings . && npm run check:identities && npm run check:pinned-deps && npm run check:ts-imports && npm run check:shrinkwrap && npm run check:install-lock:coding-agent && tsc --noEmit && npm run check:browser-smoke", "check:browser-smoke": "node scripts/check-browser-smoke.mjs", "check:pinned-deps": "node scripts/check-pinned-deps.mjs", "check:shrinkwrap": "node scripts/generate-coding-agent-shrinkwrap.mjs --check", @@ -52,16 +52,16 @@ }, "devDependencies": { "@anthropic-ai/sandbox-runtime": "0.0.26", - "@biomejs/biome": "2.3.5", + "@biomejs/biome": "2.6.3", "@opentelemetry/api": "1.10.0", "@types/node": "26.6.5", "@typescript/native-preview": "7.0.0-dev.20260120.1", - "esbuild": "0.28.1", + "esbuild": "0.28.2", "husky": "9.1.7", "jiti": "2.7.0", "shx": "0.4.0", - "tsx": "4.22.1", - "typescript": "5.9.3" + "tsx": "4.23.15", + "typescript": "7.0.2" }, "engines": { "node": ">=22.19.0" @@ -70,9 +70,9 @@ "overrides": { "nanoid": "3.3.18", "protobufjs": "7.6.5", - "rimraf": "6.1.2", + "rimraf": "6.1.3", "gaxios": { - "rimraf": "6.1.2" + "rimraf": "6.1.3" } } } diff --git a/packages/agent/package.json b/packages/agent/package.json index 0ec570b02..9bebb86ce 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -68,7 +68,7 @@ "devDependencies": { "@types/node": "26.6.5", "@vitest/coverage-v8": "4.1.12", - "typescript": "5.9.3", + "typescript": "7.0.2", "vitest": "4.1.12" }, "private": true diff --git a/packages/agent/src/harness/reducer.ts b/packages/agent/src/harness/reducer.ts index c2228f648..810a0a6dc 100644 --- a/packages/agent/src/harness/reducer.ts +++ b/packages/agent/src/harness/reducer.ts @@ -248,7 +248,7 @@ function validateToolStart( invocations.add(invocation); const assistantEntry = entriesById.get(record.assistantEntryId); - if (!assistantEntry || assistantEntry.type !== "message" || assistantEntry.message.role !== "assistant") { + if (assistantEntry?.type !== "message" || assistantEntry.message.role !== "assistant") { corrupt("tool_call_mismatch", `Tool start ${record.id} does not reference an assistant entry`); } const toolCalls = assistantEntry.message.content.filter((content) => content.type === "toolCall"); @@ -457,7 +457,7 @@ function deriveToolBatch( entry.message.role === "assistant" && entry.message.content.some((content) => content.type === "toolCall"), ); - if (!assistantEntry || assistantEntry.type !== "message" || assistantEntry.message.role !== "assistant") return null; + if (assistantEntry?.type !== "message" || assistantEntry.message.role !== "assistant") return null; const toolCalls = assistantEntry.message.content.filter( (content): content is AgentToolCall => content.type === "toolCall", diff --git a/packages/agent/src/harness/session/state.ts b/packages/agent/src/harness/session/state.ts index edc16f85b..36d12ff52 100644 --- a/packages/agent/src/harness/session/state.ts +++ b/packages/agent/src/harness/session/state.ts @@ -268,7 +268,7 @@ export class SessionState { let targetId: string | null = null; if (selectedEntryId !== null) { const entry = this.getEntry(selectedEntryId); - if (!entry || entry.type !== "message") { + if (entry?.type !== "message") { throw new SessionError("invalid_fork_target", `Fork target is not a message entry: ${selectedEntryId}`); } const position = options.position ?? (options.entryId === undefined ? "at" : "before"); diff --git a/packages/ai/src/api/constrained-sampling.ts b/packages/ai/src/api/constrained-sampling.ts index ec961a123..bf57db016 100644 --- a/packages/ai/src/api/constrained-sampling.ts +++ b/packages/ai/src/api/constrained-sampling.ts @@ -83,7 +83,7 @@ function inferGrammarInputProperty(tool: Tool): string { export function resolveJsonSchemaStrictSampling(tool: Tool, supportsStrictMode: boolean): boolean | undefined { const config = tool.constrainedSampling; - if (!config || config.type !== "json_schema") { + if (config === false || config?.type !== "json_schema") { return undefined; } @@ -103,7 +103,7 @@ export function resolveGrammarConstrainedSampling( supportsOpenAIGrammarTools: boolean, ): GrammarConstrainedSampling | undefined { const config = tool.constrainedSampling; - if (!config || config.type !== "grammar") { + if (config === false || config?.type !== "grammar") { return undefined; } diff --git a/packages/ai/src/api/mistral-conversations.ts b/packages/ai/src/api/mistral-conversations.ts index d142b9be1..aa7ae9fda 100644 --- a/packages/ai/src/api/mistral-conversations.ts +++ b/packages/ai/src/api/mistral-conversations.ts @@ -368,7 +368,7 @@ async function consumeChatStream( for (const item of contentItems) { if (typeof item === "string") { const textDelta = sanitizeSurrogates(item); - if (!currentBlock || currentBlock.type !== "text") { + if (currentBlock?.type !== "text") { finishCurrentBlock(currentBlock); currentBlock = { type: "text", text: "" }; output.content.push(currentBlock); @@ -391,7 +391,7 @@ async function consumeChatStream( .join(""); const thinkingDelta = sanitizeSurrogates(deltaText); if (!thinkingDelta) continue; - if (!currentBlock || currentBlock.type !== "thinking") { + if (currentBlock?.type !== "thinking") { finishCurrentBlock(currentBlock); currentBlock = { type: "thinking", thinking: "" }; output.content.push(currentBlock); @@ -409,7 +409,7 @@ async function consumeChatStream( if (item.type === "text") { const textDelta = sanitizeSurrogates(item.text); - if (!currentBlock || currentBlock.type !== "text") { + if (currentBlock?.type !== "text") { finishCurrentBlock(currentBlock); currentBlock = { type: "text", text: "" }; output.content.push(currentBlock); diff --git a/packages/ai/src/api/openai-responses-shared.ts b/packages/ai/src/api/openai-responses-shared.ts index cbbcc347e..73c4bb806 100644 --- a/packages/ai/src/api/openai-responses-shared.ts +++ b/packages/ai/src/api/openai-responses-shared.ts @@ -650,14 +650,14 @@ export async function processResponsesStream( } } else if (event.type === "response.custom_tool_call_input.delta") { const slot = getSlot(event.output_index, "toolCall"); - if (!slot || !slot.block.customInput) continue; + if (!slot?.block.customInput) continue; pushToolCallDelta( slot, appendCustomToolCallInput(slot.block, getCustomToolCallInput(slot.block) + event.delta, false), ); } else if (event.type === "response.custom_tool_call_input.done") { const slot = getSlot(event.output_index, "toolCall"); - if (!slot || !slot.block.customInput) continue; + if (!slot?.block.customInput) continue; pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, event.input, true)); } else if (event.type === "response.output_item.done") { const item = event.item; diff --git a/packages/ai/test/anthropic-opus-4-8-smoke.test.ts b/packages/ai/test/anthropic-opus-4-8-smoke.test.ts index 44fa5aebb..48d94bc63 100644 --- a/packages/ai/test/anthropic-opus-4-8-smoke.test.ts +++ b/packages/ai/test/anthropic-opus-4-8-smoke.test.ts @@ -51,7 +51,7 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY)("Anthropic Opus 4.8 smoke", () = const thinkingBlock = response.content.find((block) => block.type === "thinking"); expect(thinkingBlock?.type).toBe("thinking"); - if (!thinkingBlock || thinkingBlock.type !== "thinking") { + if (thinkingBlock?.type !== "thinking") { throw new Error("Expected thinking block from Claude Opus 4.8"); } expect(typeof thinkingBlock.thinkingSignature).toBe("string"); diff --git a/packages/ai/test/bedrock-thinking-payload.test.ts b/packages/ai/test/bedrock-thinking-payload.test.ts index acc370574..b90aff1d6 100644 --- a/packages/ai/test/bedrock-thinking-payload.test.ts +++ b/packages/ai/test/bedrock-thinking-payload.test.ts @@ -163,36 +163,35 @@ describe("Bedrock thinking payload", () => { }); describe.skipIf(!hasBedrockCredentials())("Bedrock Claude max tokens E2E", () => { - it( - "uses the model maxTokens cap instead of Bedrock's 4096-token default for adaptive Claude models", - { retry: 2, timeout: 180000 }, - async () => { - const baseModel = getModel("amazon-bedrock", "global.anthropic.claude-sonnet-4-6"); - const model: Model<"bedrock-converse-stream"> = { - ...baseModel, - maxTokens: 6000, - }; - - const response = await streamBedrock( - model, - { - systemPrompt: "You are a deterministic text generator. Follow the requested output format exactly.", - messages: [ - { - role: "user", - content: - "Output exactly 5200 repetitions of the token alpha, separated by single spaces. Do not number them. Do not use markdown. Do not add any other text.", - timestamp: Date.now(), - }, - ], - }, - { reasoning: "low" }, - ).result(); + it("uses the model maxTokens cap instead of Bedrock's 4096-token default for adaptive Claude models", { + retry: 2, + timeout: 180000, + }, async () => { + const baseModel = getModel("amazon-bedrock", "global.anthropic.claude-sonnet-4-6"); + const model: Model<"bedrock-converse-stream"> = { + ...baseModel, + maxTokens: 6000, + }; - expect(response.stopReason, response.errorMessage).not.toBe("error"); - expect(response.usage.output).toBeGreaterThan(4096); - }, - ); + const response = await streamBedrock( + model, + { + systemPrompt: "You are a deterministic text generator. Follow the requested output format exactly.", + messages: [ + { + role: "user", + content: + "Output exactly 5200 repetitions of the token alpha, separated by single spaces. Do not number them. Do not use markdown. Do not add any other text.", + timestamp: Date.now(), + }, + ], + }, + { reasoning: "low" }, + ).result(); + + expect(response.stopReason, response.errorMessage).not.toBe("error"); + expect(response.usage.output).toBeGreaterThan(4096); + }); }); describe("Application inference profile support", () => { diff --git a/packages/ai/test/cross-provider-handoff.test.ts b/packages/ai/test/cross-provider-handoff.test.ts index ebe103a2c..07d9a053e 100644 --- a/packages/ai/test/cross-provider-handoff.test.ts +++ b/packages/ai/test/cross-provider-handoff.test.ts @@ -280,7 +280,7 @@ async function generateContext( } const toolCall = assistantResponse.content.find((c) => c.type === "toolCall"); - if (!toolCall || toolCall.type !== "toolCall") { + if (toolCall?.type !== "toolCall") { console.log(` No tool call in response (stopReason: ${assistantResponse.stopReason})`); return { messages: [userMessage, assistantResponse], diff --git a/packages/ai/test/image-tool-result.test.ts b/packages/ai/test/image-tool-result.test.ts index 408f28c4a..3b59ccd6c 100644 --- a/packages/ai/test/image-tool-result.test.ts +++ b/packages/ai/test/image-tool-result.test.ts @@ -66,7 +66,7 @@ async function handleToolWithImageResult(model: Model, o // Find the tool call const toolCall = firstResponse.content.find((b) => b.type === "toolCall"); expect(toolCall).toBeTruthy(); - if (!toolCall || toolCall.type !== "toolCall") { + if (toolCall?.type !== "toolCall") { throw new Error("Expected tool call"); } expect(toolCall.name).toBe("get_circle"); @@ -158,7 +158,7 @@ async function handleToolWithTextAndImageResult( // Find the tool call const toolCall = firstResponse.content.find((b) => b.type === "toolCall"); expect(toolCall).toBeTruthy(); - if (!toolCall || toolCall.type !== "toolCall") { + if (toolCall?.type !== "toolCall") { throw new Error("Expected tool call"); } expect(toolCall.name).toBe("get_circle_with_description"); diff --git a/packages/ai/test/interleaved-thinking.test.ts b/packages/ai/test/interleaved-thinking.test.ts index b4da12835..37f008b85 100644 --- a/packages/ai/test/interleaved-thinking.test.ts +++ b/packages/ai/test/interleaved-thinking.test.ts @@ -93,7 +93,7 @@ async function assertSecondToolCallWithInterleavedThinking( const firstToolCall = firstResponse.content.find((block) => block.type === "toolCall"); expect(firstToolCall?.type).toBe("toolCall"); - if (!firstToolCall || firstToolCall.type !== "toolCall") { + if (firstToolCall?.type !== "toolCall") { throw new Error("Expected first response to include a tool call"); } diff --git a/packages/ai/test/openai-completions-cache-control-format.test.ts b/packages/ai/test/openai-completions-cache-control-format.test.ts index cbe71170f..e9eb8320e 100644 --- a/packages/ai/test/openai-completions-cache-control-format.test.ts +++ b/packages/ai/test/openai-completions-cache-control-format.test.ts @@ -1,3 +1,4 @@ +import assert from "node:assert/strict"; import { Type } from "typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts"; @@ -110,8 +111,9 @@ function getInstructionMessage(params: CapturedParams) { function expectAnthropicCacheMarkers(params: CapturedParams): void { const instructionMessage = getInstructionMessage(params); expect(instructionMessage).toBeDefined(); - expect(Array.isArray(instructionMessage?.content)).toBe(true); - expect((instructionMessage?.content as TextPart[])[0]?.cache_control).toEqual({ type: "ephemeral" }); + assert(instructionMessage !== undefined, "Expected instruction message"); + expect(Array.isArray(instructionMessage.content)).toBe(true); + expect((instructionMessage.content as TextPart[])[0]?.cache_control).toEqual({ type: "ephemeral" }); expect(params.tools).toHaveLength(1); expect(params.tools?.[0]?.cache_control).toEqual({ type: "ephemeral" }); diff --git a/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts b/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts index b82316582..478f18454 100644 --- a/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts +++ b/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts @@ -54,7 +54,7 @@ describe("OpenAI Responses foreign tool call ID normalization", () => { expect(functionCall).toBeDefined(); expect(functionCall?.type).toBe("function_call"); - if (!functionCall || functionCall.type !== "function_call") { + if (functionCall?.type !== "function_call") { throw new Error("Expected function_call item"); } diff --git a/packages/ai/test/openai-responses-partial-json-cleanup.test.ts b/packages/ai/test/openai-responses-partial-json-cleanup.test.ts index d01800c48..89d29f381 100644 --- a/packages/ai/test/openai-responses-partial-json-cleanup.test.ts +++ b/packages/ai/test/openai-responses-partial-json-cleanup.test.ts @@ -88,7 +88,7 @@ describe("openai responses partialJson cleanup", () => { expect(output.content).toHaveLength(1); const persistedToolCall = output.content[0]; expect(persistedToolCall?.type).toBe("toolCall"); - if (!persistedToolCall || persistedToolCall.type !== "toolCall") { + if (persistedToolCall?.type !== "toolCall") { throw new Error("Expected toolCall block"); } expect(persistedToolCall.arguments).toEqual({ path: "README.md", content: "updated" }); @@ -97,7 +97,7 @@ describe("openai responses partialJson cleanup", () => { const emittedEvents = pushSpy.mock.calls.map(([event]) => event as AssistantMessageEvent); const toolCallEnd = emittedEvents.find((event) => event.type === "toolcall_end"); expect(toolCallEnd).toBeDefined(); - if (!toolCallEnd || toolCallEnd.type !== "toolcall_end") { + if (toolCallEnd?.type !== "toolcall_end") { throw new Error("Expected toolcall_end event"); } expect(toolCallEnd.toolCall).toBe(persistedToolCall); diff --git a/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts b/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts index 68b2326c0..5a05d883c 100644 --- a/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts +++ b/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts @@ -46,7 +46,7 @@ describe.skipIf(!process.env.OPENAI_API_KEY || !process.env.ANTHROPIC_API_KEY)( const thinkingBlock = assistantResponse.content.find( (block) => block.type === "thinking" && block.thinkingSignature, ); - if (!thinkingBlock || thinkingBlock.type !== "thinking") { + if (thinkingBlock?.type !== "thinking") { throw new Error("Missing thinking signature from OpenAI Responses"); } diff --git a/packages/ai/test/openai-responses-tool-result-images.test.ts b/packages/ai/test/openai-responses-tool-result-images.test.ts index 4408a21ba..3754f9350 100644 --- a/packages/ai/test/openai-responses-tool-result-images.test.ts +++ b/packages/ai/test/openai-responses-tool-result-images.test.ts @@ -63,7 +63,7 @@ async function verifyToolResultImagesStayInFunctionCallOutput( const toolCall = firstResponse.content.find((block) => block.type === "toolCall"); expect(toolCall).toBeTruthy(); - if (!toolCall || toolCall.type !== "toolCall") { + if (toolCall?.type !== "toolCall") { throw new Error("Expected tool call"); } diff --git a/packages/ai/test/openrouter-cache-write-repro.test.ts b/packages/ai/test/openrouter-cache-write-repro.test.ts index 2292ec91e..445b22ca4 100644 --- a/packages/ai/test/openrouter-cache-write-repro.test.ts +++ b/packages/ai/test/openrouter-cache-write-repro.test.ts @@ -11,67 +11,66 @@ function createLongSystemPrompt(): string { } describe.skipIf(!process.env.OPENROUTER_API_KEY)("OpenRouter cache_write repro E2E", () => { - it( - "regression: preserves cache_write_tokens on openai-completions stream path", - { retry: 2, timeout: 90000 }, - async () => { - const model = getModel("openrouter", "google/gemini-2.5-flash"); - const context = { - systemPrompt: createLongSystemPrompt(), - messages: [ - { - role: "user" as const, - content: "Reply with exactly: OK", - timestamp: Date.now(), - }, - ], - }; + it("regression: preserves cache_write_tokens on openai-completions stream path", { + retry: 2, + timeout: 90000, + }, async () => { + const model = getModel("openrouter", "google/gemini-2.5-flash"); + const context = { + systemPrompt: createLongSystemPrompt(), + messages: [ + { + role: "user" as const, + content: "Reply with exactly: OK", + timestamp: Date.now(), + }, + ], + }; - const options = { - apiKey: process.env.OPENROUTER_API_KEY!, - maxTokens: 32, - temperature: 0, - onPayload: (payload: unknown) => { - const params = payload as { - messages?: Array<{ - role?: string; - content?: string | Array<{ type?: string; text?: string; cache_control?: { type: string } }>; - }>; - }; - const messages = params.messages; - if (!Array.isArray(messages)) return payload; + const options = { + apiKey: process.env.OPENROUTER_API_KEY!, + maxTokens: 32, + temperature: 0, + onPayload: (payload: unknown) => { + const params = payload as { + messages?: Array<{ + role?: string; + content?: string | Array<{ type?: string; text?: string; cache_control?: { type: string } }>; + }>; + }; + const messages = params.messages; + if (!Array.isArray(messages)) return payload; - for (let i = messages.length - 1; i >= 0; i--) { - const msg = messages[i]; - if (msg.role !== "user") continue; - if (typeof msg.content === "string") { - msg.content = [{ type: "text", text: msg.content, cache_control: { type: "ephemeral" } }]; + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i]; + if (msg.role !== "user") continue; + if (typeof msg.content === "string") { + msg.content = [{ type: "text", text: msg.content, cache_control: { type: "ephemeral" } }]; + break; + } + if (!Array.isArray(msg.content)) continue; + for (let j = msg.content.length - 1; j >= 0; j--) { + const part = msg.content[j]; + if (part.type === "text") { + part.cache_control = { type: "ephemeral" }; break; } - if (!Array.isArray(msg.content)) continue; - for (let j = msg.content.length - 1; j >= 0; j--) { - const part = msg.content[j]; - if (part.type === "text") { - part.cache_control = { type: "ephemeral" }; - break; - } - } - break; } - return payload; - }, - }; + break; + } + return payload; + }, + }; - const first = await completeSimple(model, context, options); - expect(first.stopReason, first.errorMessage).toBe("stop"); + const first = await completeSimple(model, context, options); + expect(first.stopReason, first.errorMessage).toBe("stop"); - const second = await completeSimple(model, context, options); - expect(second.stopReason, second.errorMessage).toBe("stop"); + const second = await completeSimple(model, context, options); + expect(second.stopReason, second.errorMessage).toBe("stop"); - // Regression expectation: cache_write_tokens from provider usage must be preserved. - // With the cache_control marker above, at least one of the two calls should create cache. - const hasCacheWrite = first.usage.cacheWrite > 0 || second.usage.cacheWrite > 0; - expect(hasCacheWrite).toBe(true); - }, - ); + // Regression expectation: cache_write_tokens from provider usage must be preserved. + // With the cache_control marker above, at least one of the two calls should create cache. + const hasCacheWrite = first.usage.cacheWrite > 0 || second.usage.cacheWrite > 0; + expect(hasCacheWrite).toBe(true); + }); }); diff --git a/packages/ai/test/total-tokens.test.ts b/packages/ai/test/total-tokens.test.ts index 1cc3ef777..26c8f337a 100644 --- a/packages/ai/test/total-tokens.test.ts +++ b/packages/ai/test/total-tokens.test.ts @@ -104,26 +104,25 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.ANTHROPIC_API_KEY)("Anthropic (API Key)", () => { - it( - "claude-sonnet-4-5 - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("anthropic", "claude-sonnet-4-5"); + it("claude-sonnet-4-5 - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("anthropic", "claude-sonnet-4-5"); - console.log(`\nAnthropic / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.ANTHROPIC_API_KEY }); + console.log(`\nAnthropic / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.ANTHROPIC_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); - // Anthropic should have cache activity - const hasCache = second.cacheRead > 0 || second.cacheWrite > 0 || first.cacheWrite > 0; - expect(hasCache).toBe(true); - }, - ); + // Anthropic should have cache activity + const hasCache = second.cacheRead > 0 || second.cacheWrite > 0 || first.cacheWrite > 0; + expect(hasCache).toBe(true); + }); }); describe("Anthropic (OAuth)", () => { @@ -154,67 +153,64 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.OPENAI_API_KEY)("OpenAI Completions", () => { - it( - "gpt-4o-mini - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const { compat: _compat, ...baseModel } = getModel("openai", "gpt-4o-mini")!; - void _compat; - const llm: Model<"openai-completions"> = { - ...baseModel, - api: "openai-completions", - }; + it("gpt-4o-mini - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const { compat: _compat, ...baseModel } = getModel("openai", "gpt-4o-mini")!; + void _compat; + const llm: Model<"openai-completions"> = { + ...baseModel, + api: "openai-completions", + }; + + console.log(`\nOpenAI Completions / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm); - console.log(`\nOpenAI Completions / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm); - - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); describe.skipIf(!process.env.OPENAI_API_KEY)("OpenAI Responses", () => { - it( - "claude-haiku-4.5 - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("openai", "gpt-4o"); + it("claude-haiku-4.5 - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("openai", "gpt-4o"); - console.log(`\nOpenAI Responses / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm); + console.log(`\nOpenAI Responses / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); describe.skipIf(!hasAzureOpenAICredentials())("Azure OpenAI Responses", () => { - it( - "gpt-4o-mini - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("azure-openai-responses", "gpt-4o-mini"); - const azureDeploymentName = resolveAzureDeploymentName(llm.id); - const azureOptions = azureDeploymentName ? { azureDeploymentName } : {}; + it("gpt-4o-mini - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("azure-openai-responses", "gpt-4o-mini"); + const azureDeploymentName = resolveAzureDeploymentName(llm.id); + const azureOptions = azureDeploymentName ? { azureDeploymentName } : {}; - console.log(`\nAzure OpenAI Responses / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, azureOptions); + console.log(`\nAzure OpenAI Responses / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, azureOptions); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -222,22 +218,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.GEMINI_API_KEY)("Google", () => { - it( - "Google Gemini text model - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getGoogleTextModel(); + it("Google Gemini text model - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getGoogleTextModel(); - console.log(`\nGoogle / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm); + console.log(`\nGoogle / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -264,22 +259,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.GROQ_API_KEY)("Groq", () => { - it( - "openai/gpt-oss-120b - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("groq", "openai/gpt-oss-120b"); + it("openai/gpt-oss-120b - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("groq", "openai/gpt-oss-120b"); - console.log(`\nGroq / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.GROQ_API_KEY }); + console.log(`\nGroq / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.GROQ_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -287,22 +281,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.CEREBRAS_API_KEY)("Cerebras", () => { - it( - "gpt-oss-120b - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("cerebras", "gpt-oss-120b"); + it("gpt-oss-120b - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("cerebras", "gpt-oss-120b"); - console.log(`\nCerebras / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.CEREBRAS_API_KEY }); + console.log(`\nCerebras / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.CEREBRAS_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -310,24 +303,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!hasCloudflareWorkersAICredentials())("Cloudflare Workers AI", () => { - it( - "@cf/moonshotai/kimi-k2.6 - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("cloudflare-workers-ai", "@cf/moonshotai/kimi-k2.6"); + it("@cf/moonshotai/kimi-k2.6 - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("cloudflare-workers-ai", "@cf/moonshotai/kimi-k2.6"); - console.log(`\nCloudflare Workers AI / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.CLOUDFLARE_API_KEY, - }); + console.log(`\nCloudflare Workers AI / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.CLOUDFLARE_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -335,24 +327,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!hasCloudflareAiGatewayCredentials())("Cloudflare AI Gateway", () => { - it( - "workers-ai/@cf/moonshotai/kimi-k2.6 - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("cloudflare-ai-gateway", "workers-ai/@cf/moonshotai/kimi-k2.6"); + it("workers-ai/@cf/moonshotai/kimi-k2.6 - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("cloudflare-ai-gateway", "workers-ai/@cf/moonshotai/kimi-k2.6"); - console.log(`\nCloudflare AI Gateway / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.CLOUDFLARE_API_KEY, - }); + console.log(`\nCloudflare AI Gateway / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.CLOUDFLARE_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -379,25 +370,24 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.TOGETHER_API_KEY)("Together AI", () => { - it( - "Together Kimi K3 should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("together", "moonshotai/Kimi-K3"); + it("Together Kimi K3 should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("together", "moonshotai/Kimi-K3"); - console.log(`\nTogether AI / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.TOGETHER_API_KEY, - reasoningEffort: "high", - }); + console.log(`\nTogether AI / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.TOGETHER_API_KEY, + reasoningEffort: "high", + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -446,22 +436,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.MISTRAL_API_KEY)("Mistral", () => { - it( - "devstral-medium-latest - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("mistral", "devstral-medium-latest"); + it("devstral-medium-latest - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("mistral", "devstral-medium-latest"); - console.log(`\nMistral / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.MISTRAL_API_KEY }); + console.log(`\nMistral / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.MISTRAL_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -469,22 +458,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax", () => { - it( - "MiniMax-M2.7 - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("minimax", "MiniMax-M2.7"); + it("MiniMax-M2.7 - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("minimax", "MiniMax-M2.7"); - console.log(`\nMiniMax / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.MINIMAX_API_KEY }); + console.log(`\nMiniMax / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.MINIMAX_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -492,22 +480,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.XIAOMI_API_KEY)("Xiaomi MiMo (API billing)", () => { - it( - "mimo-v2.5-pro - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("xiaomi", "mimo-v2.5-pro"); + it("mimo-v2.5-pro - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("xiaomi", "mimo-v2.5-pro"); - console.log(`\nXiaomi MiMo / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.XIAOMI_API_KEY }); + console.log(`\nXiaomi MiMo / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.XIAOMI_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -515,24 +502,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.XIAOMI_TOKEN_PLAN_CN_API_KEY)("Xiaomi MiMo Token Plan (CN)", () => { - it( - "mimo-v2.5-pro - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("xiaomi-token-plan-cn", "mimo-v2.5-pro"); + it("mimo-v2.5-pro - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("xiaomi-token-plan-cn", "mimo-v2.5-pro"); - console.log(`\nXiaomi MiMo Token Plan CN / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.XIAOMI_TOKEN_PLAN_CN_API_KEY, - }); + console.log(`\nXiaomi MiMo Token Plan CN / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.XIAOMI_TOKEN_PLAN_CN_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -540,24 +526,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.XIAOMI_TOKEN_PLAN_AMS_API_KEY)("Xiaomi MiMo Token Plan (AMS)", () => { - it( - "mimo-v2.5-pro - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("xiaomi-token-plan-ams", "mimo-v2.5-pro"); + it("mimo-v2.5-pro - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("xiaomi-token-plan-ams", "mimo-v2.5-pro"); - console.log(`\nXiaomi MiMo Token Plan AMS / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.XIAOMI_TOKEN_PLAN_AMS_API_KEY, - }); + console.log(`\nXiaomi MiMo Token Plan AMS / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.XIAOMI_TOKEN_PLAN_AMS_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -565,24 +550,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.XIAOMI_TOKEN_PLAN_SGP_API_KEY)("Xiaomi MiMo Token Plan (SGP)", () => { - it( - "mimo-v2.5-pro - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("xiaomi-token-plan-sgp", "mimo-v2.5-pro"); + it("mimo-v2.5-pro - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("xiaomi-token-plan-sgp", "mimo-v2.5-pro"); - console.log(`\nXiaomi MiMo Token Plan SGP / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.XIAOMI_TOKEN_PLAN_SGP_API_KEY, - }); + console.log(`\nXiaomi MiMo Token Plan SGP / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.XIAOMI_TOKEN_PLAN_SGP_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -590,24 +574,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan", () => { - it( - "qwen3.7-max - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("qwen-token-plan", "qwen3.7-max"); + it("qwen3.7-max - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("qwen-token-plan", "qwen3.7-max"); - console.log(`\nQwen Token Plan / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.QWEN_TOKEN_PLAN_API_KEY, - }); + console.log(`\nQwen Token Plan / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.QWEN_TOKEN_PLAN_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -615,24 +598,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan Individual", () => { - it( - "qwen3.8-max - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("qwen-token-plan-individual", "qwen3.8-max"); + it("qwen3.8-max - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("qwen-token-plan-individual", "qwen3.8-max"); - console.log(`\nQwen Token Plan Individual / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.QWEN_TOKEN_PLAN_API_KEY, - }); + console.log(`\nQwen Token Plan Individual / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.QWEN_TOKEN_PLAN_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -640,24 +622,23 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN)", () => { - it( - "qwen3.7-max - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); + it("qwen3.7-max - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); - console.log(`\nQwen Token Plan CN / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.QWEN_TOKEN_PLAN_CN_API_KEY, - }); + console.log(`\nQwen Token Plan CN / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.QWEN_TOKEN_PLAN_CN_API_KEY, + }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -665,22 +646,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.KIMI_API_KEY)("Kimi For Coding", () => { - it( - "kimi-for-coding - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("kimi-coding", "kimi-for-coding"); + it("kimi-for-coding - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("kimi-coding", "kimi-for-coding"); - console.log(`\nKimi For Coding / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.KIMI_API_KEY }); + console.log(`\nKimi For Coding / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.KIMI_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -688,22 +668,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.AI_GATEWAY_API_KEY)("Vercel AI Gateway", () => { - it( - "google/gemini-2.5-flash - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("vercel-ai-gateway", "google/gemini-2.5-flash"); + it("google/gemini-2.5-flash - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("vercel-ai-gateway", "google/gemini-2.5-flash"); - console.log(`\nVercel AI Gateway / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.AI_GATEWAY_API_KEY }); + console.log(`\nVercel AI Gateway / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.AI_GATEWAY_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -711,90 +690,85 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.OPENROUTER_API_KEY)("OpenRouter", () => { - it( - "anthropic/claude-sonnet-4.6 - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("openrouter", "anthropic/claude-sonnet-4.6"); + it("anthropic/claude-sonnet-4.6 - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("openrouter", "anthropic/claude-sonnet-4.6"); - console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); + console.log(`\nOpenRouter / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); - it( - "deepseek/deepseek-chat - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("openrouter", "deepseek/deepseek-chat"); + it("deepseek/deepseek-chat - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("openrouter", "deepseek/deepseek-chat"); - console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); + console.log(`\nOpenRouter / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); - it( - "mistralai/mistral-small-3.2-24b-instruct - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("openrouter", "mistralai/mistral-small-3.2-24b-instruct"); + it("mistralai/mistral-small-3.2-24b-instruct - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("openrouter", "mistralai/mistral-small-3.2-24b-instruct"); - console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); + console.log(`\nOpenRouter / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); - it( - "google/gemini-2.5-flash - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("openrouter", "google/gemini-2.5-flash"); + it("google/gemini-2.5-flash - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("openrouter", "google/gemini-2.5-flash"); - console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); + console.log(`\nOpenRouter / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); - it( - "deepseek/deepseek-chat - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("openrouter", "deepseek/deepseek-chat"); + it("deepseek/deepseek-chat - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("openrouter", "deepseek/deepseek-chat"); - console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); + console.log(`\nOpenRouter / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= @@ -844,22 +818,21 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!hasBedrockCredentials())("Amazon Bedrock", () => { - it( - "claude-sonnet-4-5 - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("amazon-bedrock", "global.anthropic.claude-sonnet-4-5-20250929-v1:0"); + it("claude-sonnet-4-5 - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("amazon-bedrock", "global.anthropic.claude-sonnet-4-5-20250929-v1:0"); - console.log(`\nAmazon Bedrock / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm); + console.log(`\nAmazon Bedrock / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= diff --git a/packages/ai/test/unicode-surrogate.test.ts b/packages/ai/test/unicode-surrogate.test.ts index 82fdef9c0..17ad25e01 100644 --- a/packages/ai/test/unicode-surrogate.test.ts +++ b/packages/ai/test/unicode-surrogate.test.ts @@ -656,13 +656,12 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => { await testRealWorldLinkedInData(llm); }); - it( - "should handle unpaired high surrogate (0xD83D) in tool results", - { retry: 3, timeout: 30000 }, - async () => { - await testUnpairedHighSurrogate(llm); - }, - ); + it("should handle unpaired high surrogate (0xD83D) in tool results", { + retry: 3, + timeout: 30000, + }, async () => { + await testUnpairedHighSurrogate(llm); + }); }, ); @@ -679,13 +678,12 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => { await testRealWorldLinkedInData(llm); }); - it( - "should handle unpaired high surrogate (0xD83D) in tool results", - { retry: 3, timeout: 30000 }, - async () => { - await testUnpairedHighSurrogate(llm); - }, - ); + it("should handle unpaired high surrogate (0xD83D) in tool results", { + retry: 3, + timeout: 30000, + }, async () => { + await testUnpairedHighSurrogate(llm); + }); }, ); @@ -702,13 +700,12 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => { await testRealWorldLinkedInData(llm); }); - it( - "should handle unpaired high surrogate (0xD83D) in tool results", - { retry: 3, timeout: 30000 }, - async () => { - await testUnpairedHighSurrogate(llm); - }, - ); + it("should handle unpaired high surrogate (0xD83D) in tool results", { + retry: 3, + timeout: 30000, + }, async () => { + await testUnpairedHighSurrogate(llm); + }); }, ); diff --git a/packages/coding-agent/examples/extensions/minimal-mode.ts b/packages/coding-agent/examples/extensions/minimal-mode.ts index 4928d2788..65fd2515c 100644 --- a/packages/coding-agent/examples/extensions/minimal-mode.ts +++ b/packages/coding-agent/examples/extensions/minimal-mode.ts @@ -102,7 +102,7 @@ export default function (diffex: ExtensionAPI) { // Expanded mode: show full output const textContent = result.content.find((c) => c.type === "text"); - if (!textContent || textContent.type !== "text") { + if (textContent?.type !== "text") { return new Text("", 0, 0); } @@ -143,7 +143,7 @@ export default function (diffex: ExtensionAPI) { // Expanded mode: show full output const textContent = result.content.find((c) => c.type === "text"); - if (!textContent || textContent.type !== "text") { + if (textContent?.type !== "text") { return new Text("", 0, 0); } @@ -233,7 +233,7 @@ export default function (diffex: ExtensionAPI) { // Expanded mode: show diff or error const textContent = result.content.find((c) => c.type === "text"); - if (!textContent || textContent.type !== "text") { + if (textContent?.type !== "text") { return new Text("", 0, 0); } @@ -292,7 +292,7 @@ export default function (diffex: ExtensionAPI) { // Expanded: show full results const textContent = result.content.find((c) => c.type === "text"); - if (!textContent || textContent.type !== "text") { + if (textContent?.type !== "text") { return new Text("", 0, 0); } @@ -354,7 +354,7 @@ export default function (diffex: ExtensionAPI) { // Expanded: show full results const textContent = result.content.find((c) => c.type === "text"); - if (!textContent || textContent.type !== "text") { + if (textContent?.type !== "text") { return new Text("", 0, 0); } @@ -410,7 +410,7 @@ export default function (diffex: ExtensionAPI) { // Expanded: show full listing const textContent = result.content.find((c) => c.type === "text"); - if (!textContent || textContent.type !== "text") { + if (textContent?.type !== "text") { return new Text("", 0, 0); } diff --git a/packages/coding-agent/install-lock/package.json b/packages/coding-agent/install-lock/package.json index 5d4c9828f..af1b10861 100644 --- a/packages/coding-agent/install-lock/package.json +++ b/packages/coding-agent/install-lock/package.json @@ -8,9 +8,9 @@ }, "overrides": { "protobufjs": "7.6.5", - "rimraf": "6.1.2", + "rimraf": "6.1.3", "gaxios": { - "rimraf": "6.1.2" + "rimraf": "6.1.3" } }, "engines": { diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index e4781fb33..d5e74d897 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -76,9 +76,9 @@ }, "overrides": { "protobufjs": "7.6.5", - "rimraf": "6.1.2", + "rimraf": "6.1.3", "gaxios": { - "rimraf": "6.1.2" + "rimraf": "6.1.3" } }, "optionalDependencies": { @@ -86,14 +86,13 @@ }, "devDependencies": { "@types/cross-spawn": "6.0.6", - "@types/diff": "7.0.2", "@types/hosted-git-info": "3.0.5", "@types/ms": "2.1.0", "@types/node": "26.6.5", "@types/proper-lockfile": "4.1.4", "@types/semver": "7.8.0", "shx": "0.4.0", - "typescript": "5.9.3", + "typescript": "7.0.2", "vitest": "4.1.12" }, "keywords": [ diff --git a/packages/coding-agent/src/client/transcript.ts b/packages/coding-agent/src/client/transcript.ts index 999be73ed..61d7689d3 100644 --- a/packages/coding-agent/src/client/transcript.ts +++ b/packages/coding-agent/src/client/transcript.ts @@ -54,7 +54,7 @@ export function applyTranscriptProgress(state: TranscriptState, progress: Transc const item = state.progressItems.get(progress.messageId) ?? state.snapshot.transcript.find(({ id }) => id === progress.messageId); - if (!item || item.role !== "assistant") return state; + if (item?.role !== "assistant") return state; let toolCallBuffers = state.toolCallBuffers; const content = item.content.map((part, index) => { if (index !== progress.contentIndex) return structuredClone(part); diff --git a/packages/coding-agent/src/core/extensions/types.ts b/packages/coding-agent/src/core/extensions/types.ts index bd49f4677..8090c79e2 100644 --- a/packages/coding-agent/src/core/extensions/types.ts +++ b/packages/coding-agent/src/core/extensions/types.ts @@ -86,9 +86,9 @@ import type { } from "../tools/index.ts"; export type { ExecOptions, ExecResult } from "../exec.ts"; +export type { AppKeybinding, KeybindingsManager } from "../keybindings.ts"; export type { BuildSystemPromptOptions } from "../system-prompt.ts"; export type { AgentToolResult, AgentToolUpdateCallback, ToolExecutionMode }; -export type { AppKeybinding, KeybindingsManager } from "../keybindings.ts"; // ============================================================================ // UI Context diff --git a/packages/coding-agent/src/core/keybindings.ts b/packages/coding-agent/src/core/keybindings.ts index d29d9a5cd..166efb454 100644 --- a/packages/coding-agent/src/core/keybindings.ts +++ b/packages/coding-agent/src/core/keybindings.ts @@ -377,4 +377,4 @@ export class KeybindingsManager extends TuiKeybindingsManager { } } -export type { Keybinding, KeyId, KeybindingsConfig }; +export type { Keybinding, KeybindingsConfig, KeyId }; diff --git a/packages/coding-agent/src/core/sdk.ts b/packages/coding-agent/src/core/sdk.ts index b3a35fc55..d0f536e5e 100644 --- a/packages/coding-agent/src/core/sdk.ts +++ b/packages/coding-agent/src/core/sdk.ts @@ -175,19 +175,19 @@ export type { PromptTemplate } from "./prompt-templates.ts"; export type { Skill } from "./skills.ts"; export type { Tool } from "./tools/index.ts"; +// Tool factories (for custom cwd) export { - withFileMutationQueue, - // Tool factories (for custom cwd) - createCodingTools, - createReadOnlyTools, - createReadTool, createBashTool, + createCodingTools, createEditTool, - createWriteTool, - createUpdatePlanTool, - createGrepTool, createFindTool, + createGrepTool, createLsTool, + createReadOnlyTools, + createReadTool, + createUpdatePlanTool, + createWriteTool, + withFileMutationQueue, }; // Helper Functions diff --git a/packages/coding-agent/src/core/subagents/controller.ts b/packages/coding-agent/src/core/subagents/controller.ts index 2264ff6ee..5f8d73cc0 100644 --- a/packages/coding-agent/src/core/subagents/controller.ts +++ b/packages/coding-agent/src/core/subagents/controller.ts @@ -576,7 +576,7 @@ export class SubagentController { const attempts = [...this.#records.values()] .flatMap((record) => { const run = record.coordinatedRun; - if (!run || run.delivery !== "pending") return []; + if (run?.delivery !== "pending") return []; run.delivery = "frozen"; return [{ name: record.name, runId: run.id }]; }) diff --git a/packages/coding-agent/src/modes/interactive/components/config-selector.ts b/packages/coding-agent/src/modes/interactive/components/config-selector.ts index 99b1f6172..90da17724 100644 --- a/packages/coding-agent/src/modes/interactive/components/config-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/config-selector.ts @@ -373,7 +373,7 @@ class ResourceList implements Component, Focusable { private selectFirstItem(): void { const firstItemIndex = this.filteredItems.findIndex((e) => e.type === "item"); - this.selectedIndex = firstItemIndex >= 0 ? firstItemIndex : 0; + this.selectedIndex = Math.max(firstItemIndex, 0); } updateItem(item: ResourceItem, enabled: boolean): void { diff --git a/packages/coding-agent/src/modes/interactive/components/diff.ts b/packages/coding-agent/src/modes/interactive/components/diff.ts index d8858c3fa..5da97326b 100644 --- a/packages/coding-agent/src/modes/interactive/components/diff.ts +++ b/packages/coding-agent/src/modes/interactive/components/diff.ts @@ -119,7 +119,7 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s const removedLines: { lineNum: string; content: string }[] = []; while (i < lines.length) { const p = parseDiffLine(lines[i]); - if (!p || p.prefix !== "-") break; + if (p?.prefix !== "-") break; removedLines.push({ lineNum: p.lineNum, content: p.content }); i++; } @@ -128,7 +128,7 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s const addedLines: { lineNum: string; content: string }[] = []; while (i < lines.length) { const p = parseDiffLine(lines[i]); - if (!p || p.prefix !== "+") break; + if (p?.prefix !== "+") break; addedLines.push({ lineNum: p.lineNum, content: p.content }); i++; } diff --git a/packages/coding-agent/src/modes/interactive/components/model-selector.ts b/packages/coding-agent/src/modes/interactive/components/model-selector.ts index 45b9f831a..1702e2087 100644 --- a/packages/coding-agent/src/modes/interactive/components/model-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/model-selector.ts @@ -232,7 +232,7 @@ export class ModelSelectorComponent extends Container implements Focusable { this.scope = scope; this.activeModels = this.scope === "scoped" ? this.scopedModelItems : this.allModels; const currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model)); - this.selectedIndex = currentIndex >= 0 ? currentIndex : 0; + this.selectedIndex = Math.max(currentIndex, 0); this.filterModels(this.searchInput.getValue()); if (this.scopeText) { this.scopeText.setText(this.getScopeText()); diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index a8c3ac8b5..0e1b7419a 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -5588,7 +5588,7 @@ export class InteractiveMode { if (this.anthropicSubscriptionWarningShown) { return; } - if (!model || model.provider !== "anthropic") { + if (model?.provider !== "anthropic") { return; } diff --git a/packages/coding-agent/src/utils/clipboard-image.ts b/packages/coding-agent/src/utils/clipboard-image.ts index 2895f2af4..2f47d3770 100644 --- a/packages/coding-agent/src/utils/clipboard-image.ts +++ b/packages/coding-agent/src/utils/clipboard-image.ts @@ -238,7 +238,7 @@ function readClipboardImageViaXclip(): ClipboardImage | null { } async function readClipboardImageViaNativeClipboard(): Promise { - if (!clipboard || !clipboard.hasImage()) { + if (!clipboard?.hasImage()) { return null; } diff --git a/packages/coding-agent/src/utils/owned-shell-processes.ts b/packages/coding-agent/src/utils/owned-shell-processes.ts index fadb3af52..b0d89d821 100644 --- a/packages/coding-agent/src/utils/owned-shell-processes.ts +++ b/packages/coding-agent/src/utils/owned-shell-processes.ts @@ -62,7 +62,7 @@ export class OwnedShellProcesses { /** Called synchronously on shell exit, before output draining or another event can cancel it. */ complete(pid: number, successful: boolean): void { const execution = this.#executions.get(pid); - if (!execution || execution.state !== "active") return; + if (execution?.state !== "active") return; // This callback belongs to the ChildProcess just reaped by Node. Capture the // surviving group now; later cleanup must validate these process identities. execution.members = process.platform === "win32" ? [] : readShellProcessGroup(pid); diff --git a/packages/coding-agent/test/streaming-render-debug.ts b/packages/coding-agent/test/streaming-render-debug.ts index bc13fd1ad..7196eafc0 100644 --- a/packages/coding-agent/test/streaming-render-debug.ts +++ b/packages/coding-agent/test/streaming-render-debug.ts @@ -28,7 +28,7 @@ const fixtureMessage: AssistantMessage = JSON.parse( const thinkingContent = fixtureMessage.content.find((c) => c.type === "thinking"); const textContent = fixtureMessage.content.find((c) => c.type === "text"); -if (!thinkingContent || thinkingContent.type !== "thinking") { +if (thinkingContent?.type !== "thinking") { console.error("No thinking content in fixture"); process.exit(1); } diff --git a/packages/evals/package.json b/packages/evals/package.json index 97fa8351b..a709a0f0a 100644 --- a/packages/evals/package.json +++ b/packages/evals/package.json @@ -13,7 +13,7 @@ "@diffexai/diffex": "^0.3.2", "@types/node": "26.6.5", "shx": "0.4.0", - "typescript": "5.9.3", + "typescript": "7.0.2", "vitest-evals": "0.17.0", "vitest": "4.1.12" } diff --git a/packages/evolution/test/skill-curator.test.ts b/packages/evolution/test/skill-curator.test.ts index bc2830cd0..b81c121cf 100644 --- a/packages/evolution/test/skill-curator.test.ts +++ b/packages/evolution/test/skill-curator.test.ts @@ -851,12 +851,19 @@ describe("StructuredSkillCurator", () => { expect(expansionInput).not.toHaveProperty("acceptedOutlines"); expect(expansionInput).not.toHaveProperty("frozenEvolutionMemory"); } - expect((requestInputs.get("alpha")?.[0]?.control as JsonObject).responseCorrection).toBeUndefined(); - expect((requestInputs.get("beta")?.[1]?.control as JsonObject).responseCorrection).toMatchObject({ + const alphaInput = requestInputs.get("alpha")?.[0]; + const betaRetryInput = requestInputs.get("beta")?.[1]; + const gammaInput = requestInputs.get("gamma")?.[0]; + const gammaRetryInput = requestInputs.get("gamma")?.[1]; + if (!alphaInput || !betaRetryInput || !gammaInput || !gammaRetryInput) { + throw new Error("Expected initial and retry expansion inputs"); + } + expect((alphaInput.control as JsonObject).responseCorrection).toBeUndefined(); + expect((betaRetryInput.control as JsonObject).responseCorrection).toMatchObject({ reason: expect.stringContaining("trigger must be an object"), }); - expect((requestInputs.get("gamma")?.[0]?.control as JsonObject).responseCorrection).toBeUndefined(); - expect((requestInputs.get("gamma")?.[1]?.control as JsonObject).responseCorrection).toMatchObject({ + expect((gammaInput.control as JsonObject).responseCorrection).toBeUndefined(); + expect((gammaRetryInput.control as JsonObject).responseCorrection).toMatchObject({ reason: expect.stringContaining("trigger must be an object"), }); const expansionKeys = [...idempotencyKeys.values()].flat(); diff --git a/packages/server/src/transports/websocket/listener.ts b/packages/server/src/transports/websocket/listener.ts index 7439e37f0..3a7faf44c 100644 --- a/packages/server/src/transports/websocket/listener.ts +++ b/packages/server/src/transports/websocket/listener.ts @@ -249,6 +249,6 @@ async function closeWebSocketServer(server: WebSocketServer | undefined): Promis } async function closeHttpServer(server: Server | undefined): Promise { - if (!server || !server.listening) return; + if (!server?.listening) return; await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } diff --git a/packages/session-backends/sqlite-node/src/sqlite/repo.ts b/packages/session-backends/sqlite-node/src/sqlite/repo.ts index 08310b570..443b41c48 100644 --- a/packages/session-backends/sqlite-node/src/sqlite/repo.ts +++ b/packages/session-backends/sqlite-node/src/sqlite/repo.ts @@ -806,7 +806,7 @@ export class SqliteSessionRepository const selectedEntryId = options.entryId ?? main.leaf_id; if (selectedEntryId !== null) { const target = readEntryRow(db, source.id, selectedEntryId); - if (!target || target.type !== "message") { + if (target?.type !== "message") { throw new SessionError( "invalid_fork_target", `Fork target is not a message entry: ${selectedEntryId}`, diff --git a/packages/telemetry/src/index.ts b/packages/telemetry/src/index.ts index 3d3909791..266b99b79 100644 --- a/packages/telemetry/src/index.ts +++ b/packages/telemetry/src/index.ts @@ -170,12 +170,10 @@ export type TelemetrySchemaSpanEndAttributes< ? InferOptionalAttributes : never; -type SchemaSpanEvents< - Schema extends TelemetrySchemaDefinition, - Name extends TelemetrySchemaSpanName, -> = SchemaSpan extends { events: infer Events extends Record } - ? Events - : Record; +type SchemaSpanEvents> = + SchemaSpan extends { events: infer Events extends Record } + ? Events + : Record; export type TelemetrySchemaSpanEventName< Schema extends TelemetrySchemaDefinition, @@ -186,31 +184,34 @@ type SchemaSpanEvent< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, EventName extends TelemetrySchemaSpanEventName, -> = SchemaSpanEvents extends infer Events - ? EventName extends keyof Events - ? Events[EventName] - : never - : never; +> = + SchemaSpanEvents extends infer Events + ? EventName extends keyof Events + ? Events[EventName] + : never + : never; export type TelemetrySchemaSpanEventAttributes< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, EventName extends TelemetrySchemaSpanEventName, -> = SchemaSpanEvent extends { - attributes: infer Definitions extends Record; -} - ? InferEventAttributes - : never; +> = + SchemaSpanEvent extends { + attributes: infer Definitions extends Record; + } + ? InferEventAttributes + : never; type SchemaSpanEventAttributeDefinitions< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, EventName extends TelemetrySchemaSpanEventName, -> = SchemaSpanEvent extends { - attributes: infer Definitions extends Record; -} - ? Definitions - : Record; +> = + SchemaSpanEvent extends { + attributes: infer Definitions extends Record; + } + ? Definitions + : Record; type EventArguments< Definitions extends Record, diff --git a/scripts/check-ts-relative-imports.mjs b/scripts/check-ts-relative-imports.mjs index bf21a5d0a..1120dfd47 100644 --- a/scripts/check-ts-relative-imports.mjs +++ b/scripts/check-ts-relative-imports.mjs @@ -1,6 +1,8 @@ -import { readdirSync, readFileSync } from "node:fs"; -import { join } from "node:path"; -import ts from "typescript"; +import { mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import * as ts from "typescript/unstable/ast"; +import { API } from "typescript/unstable/sync"; const ignoredDirectories = new Set([".git", "coverage", "dist", "node_modules"]); const files = []; @@ -26,7 +28,7 @@ function isRelativeJavaScriptSpecifier(specifier) { function getImportTypeSpecifier(node) { if (!ts.isLiteralTypeNode(node.argument)) return undefined; - if (!ts.isStringLiteralLike(node.argument.literal)) return undefined; + if (!ts.isStringLiteralLikeNode(node.argument.literal)) return undefined; return node.argument.literal; } @@ -34,41 +36,69 @@ const failures = []; collectTypescriptFiles("."); -for (const file of files.sort()) { - const sourceText = readFileSync(file, "utf8"); - const sourceFile = ts.createSourceFile(file, sourceText, ts.ScriptTarget.Latest, true); +if (files.length > 0) { + // TypeScript's native AST API is unstable; the workspace pins its exact version. + // A standalone project checks every collected file, independent of repository configs. + const directory = mkdtempSync(join(tmpdir(), "diffex-ts-relative-imports-")); + let api; + try { + const config = join(directory, "tsconfig.json"); + writeFileSync( + config, + JSON.stringify({ + compilerOptions: { noResolve: true, noLib: true, types: [] }, + files: files.map((file) => resolve(file)), + }), + ); + api = new API(); + const snapshot = api.updateSnapshot({ openProjects: [config] }); + const project = snapshot.getProject(config); + if (!project) throw new Error("Could not open the relative import check project"); - function checkSpecifier(node) { - if (!isRelativeJavaScriptSpecifier(node.text)) return; - const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)); - failures.push(`${file}:${line + 1}:${character + 1}: ${node.text}`); - } + for (const file of files.sort()) { + const sourceFile = project.program.getSourceFile(resolve(file)); + if (!sourceFile) throw new Error(`Could not parse ${file}`); - function visit(node) { - if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) { - checkSpecifier(node.moduleSpecifier); - } else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) { - checkSpecifier(node.moduleSpecifier); - } else if ( - ts.isCallExpression(node) && - node.expression.kind === ts.SyntaxKind.ImportKeyword && - node.arguments[0] && - ts.isStringLiteralLike(node.arguments[0]) - ) { - checkSpecifier(node.arguments[0]); - } else if (ts.isImportTypeNode(node)) { - const specifier = getImportTypeSpecifier(node); - if (specifier) checkSpecifier(specifier); - } + function checkSpecifier(node) { + if (!isRelativeJavaScriptSpecifier(node.text)) return; + const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)); + failures.push(`${file}:${line + 1}:${character + 1}: ${node.text}`); + } - ts.forEachChild(node, visit); - } + function visit(node) { + if (ts.isImportDeclaration(node) && ts.isStringLiteralLikeNode(node.moduleSpecifier)) { + checkSpecifier(node.moduleSpecifier); + } else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteralLikeNode(node.moduleSpecifier)) { + checkSpecifier(node.moduleSpecifier); + } else if ( + ts.isCallExpression(node) && + node.expression.kind === ts.SyntaxKind.ImportKeyword && + node.arguments[0] && + ts.isStringLiteralLikeNode(node.arguments[0]) + ) { + checkSpecifier(node.arguments[0]); + } else if (ts.isImportTypeNode(node)) { + const specifier = getImportTypeSpecifier(node); + if (specifier) checkSpecifier(specifier); + } - visit(sourceFile); + node.forEachChild(visit); + } + + visit(sourceFile); + } + } finally { + try { + // Closing the client also disposes its snapshots and projects. + api?.close(); + } finally { + rmSync(directory, { recursive: true, force: true }); + } + } } if (failures.length > 0) { console.error("Relative .js imports are not allowed in non-declaration .ts files:"); for (const failure of failures) console.error(` ${failure}`); - process.exit(1); + process.exitCode = 1; } diff --git a/scripts/check-ts-relative-imports.test.mjs b/scripts/check-ts-relative-imports.test.mjs new file mode 100644 index 000000000..6f9aa90d1 --- /dev/null +++ b/scripts/check-ts-relative-imports.test.mjs @@ -0,0 +1,145 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const script = fileURLToPath(new URL("./check-ts-relative-imports.mjs", import.meta.url)); + +function runFixture(t, files) { + const root = mkdtempSync(join(tmpdir(), "diffex-relative-imports-test-")); + t.after(() => rmSync(root, { recursive: true, force: true })); + const scratch = join(root, "scratch"); + mkdirSync(scratch); + for (const [file, content] of Object.entries(files)) { + const path = join(root, file); + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, content); + } + const result = spawnSync(process.execPath, [script], { + cwd: root, + env: { ...process.env, TMPDIR: scratch, TMP: scratch, TEMP: scratch }, + encoding: "utf8", + timeout: 30_000, + }); + assert.ifError(result.error); + assert.equal(result.signal, null, result.stderr); + assert.equal(result.stdout, ""); + assert.deepEqual(readdirSync(scratch), [], "native client and temporary project must be cleaned up"); + return result; +} + +test("reports every AST import form with decoded specifiers and exact UTF-16 locations", (t) => { + const result = runFixture(t, { + // Excluded files still need inspection: the check must not use repository tsconfigs. + "tsconfig.json": JSON.stringify({ files: [], exclude: ["**/*"] }), + "a.ts": [ + 'import value from "./default.js";', + 'import "../side-effect.js?raw";', + 'import type { Value } from "./types.js#types";', + 'export { value } from "../named.js?raw#fragment";', + 'export * from "./star.js";', + 'export * as ns from "./namespace.js";', + 'export type { Value } from "./export-type.js";', + 'const promise = import("./dynamic.js");', + 'const template = import(`../template.js#fragment`);', + 'type T = import("./import-type.js").Value;', + 'type Module = typeof import("../typeof.js?raw");', + "const multiline = import(", + " /* before specifier */", + ' "./multiline.js?raw",', + ' { with: { type: "json" } },', + ");", + "import {", + " other,", + "} from /* trivia */", + ' "../multiline-static.js";', + 'type Multiline = import(', + ' /* trivia */ "./multiline-type.js"', + ").Value;", + String.raw`import "\u002e/escaped\x2ejs?raw";`, + String.raw`export * from ".\u002e/escaped-export.js";`, + String.raw`const escaped = import("./dynamic\u002ejs");`, + String.raw`type Escaped = import("./type\x2ejs").Value;`, + 'const emoji = "😀"; import("./unicode.js");', + ].join("\r\n"), + "nested/b.ts": 'export { x } from "./nested.js";\n', + }); + assert.equal(result.status, 1, result.stderr); + assert.equal( + result.stderr, + [ + "Relative .js imports are not allowed in non-declaration .ts files:", + " a.ts:1:19: ./default.js", + " a.ts:2:8: ../side-effect.js?raw", + " a.ts:3:28: ./types.js#types", + " a.ts:4:23: ../named.js?raw#fragment", + " a.ts:5:15: ./star.js", + " a.ts:6:21: ./namespace.js", + " a.ts:7:28: ./export-type.js", + " a.ts:8:24: ./dynamic.js", + " a.ts:9:25: ../template.js#fragment", + " a.ts:10:17: ./import-type.js", + " a.ts:11:29: ../typeof.js?raw", + " a.ts:14:3: ./multiline.js?raw", + " a.ts:20:3: ../multiline-static.js", + " a.ts:22:16: ./multiline-type.js", + " a.ts:24:8: ./escaped.js?raw", + " a.ts:25:15: ../escaped-export.js", + " a.ts:26:24: ./dynamic.js", + " a.ts:27:23: ./type.js", + " a.ts:28:28: ./unicode.js", + " nested/b.ts:1:19: ./nested.js", + "", + ].join("\n"), + ); +}); + +test("ignores comments, string contents, non-relative/non-js specifiers and excluded files", (t) => { + const forbidden = 'import "./ignored.js";\n'; + const result = runFixture(t, { + "src/clean.ts": [ + '// import "./comment.js";', + '/* export * from "../comment.js"; import("./comment.js"); */', + '/** @type {import("./jsdoc.js").Value} */', + String.raw`const text = 'import "./string.js";';`, + 'const template = `export * from "./string.js";`;', + 'const ordinary = "./ordinary.js";', + 'import "package.js";', + 'import "/absolute.js";', + 'import "./source.ts";', + 'import "../extensionless";', + 'import "./other.mjs";', + 'import "./other.jsx";', + 'import "./other.js.map";', + 'import "./other.json?raw";', + 'export * from "package.js#fragment";', + 'import("https://example.com/remote.js");', + 'import(`./${name}.js`);', + 'import("./" + name + ".js");', + 'require("./require.js");', + 'object.import("./method.js");', + 'type T = import("./types.ts").Value;', + 'import type { Ignored } from "./ignored.d.ts";', + ].join("\n"), + "src/ignored.d.ts": forbidden, + "node_modules/pkg/index.ts": forbidden, + "src/node_modules/pkg/index.ts": forbidden, + ".git/ignored.ts": forbidden, + "coverage/ignored.ts": forbidden, + "dist/ignored.ts": forbidden, + "src/dist/ignored.ts": forbidden, + "ignored.js": forbidden, + "ignored.tsx": forbidden, + }); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stderr, ""); +}); + +test("an empty source tree passes without creating a native project", (t) => { + const result = runFixture(t, { "only.d.ts": 'export * from "./ignored.js";' }); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stderr, ""); +}); diff --git a/tsconfig.base.json b/tsconfig.base.json index 57e97d6e3..7a8e0600b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "ES2022", + "target": "ES2024", "module": "Node16", - "lib": ["ES2022"], + "lib": ["ES2024"], "strict": true, "erasableSyntaxOnly": true, "esModuleInterop": true, From 644893afcfd58dc52e0c83b3270ab274b4351cc2 Mon Sep 17 00:00:00 2001 From: Jay Yu Date: Sun, 25 Oct 2026 11:59:58 -0400 Subject: [PATCH 2/3] chore(docs): document stable tooling and compiler API migration --- docs/dependency-maintenance.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/dependency-maintenance.md b/docs/dependency-maintenance.md index e460837d4..a4bb058ac 100644 --- a/docs/dependency-maintenance.md +++ b/docs/dependency-maintenance.md @@ -15,7 +15,7 @@ Keep each dependency's pins synchronized across consuming workspaces and example - Vitest and `@vitest/coverage-v8` use **4.1.12**, the patched 4.x release. The latest `vitest-evals` (**0.17.0**) requires `vitest >=4 <5`, so Vitest 5 is not compatible with the evaluation workspace. - `hosted-git-info` remains **9.0.3**. Version 10.1.1 requires Node `^22.22.2 || ^24.15.0 || >=26.0.0`, above the project's Node 22.19.0 minimum. -- `@typescript/native-preview` has no stable release under that package name; do not replace its existing pin with another prerelease as part of a stable-only update. +- `@typescript/native-preview` has no stable release under that package name; do not replace its existing pin with another prerelease as part of a stable-only update. Its existing pin remains available to workspace build scripts, while the root check uses stable TypeScript 7's `tsc`. ## Install tooling @@ -29,6 +29,12 @@ If changed dependencies remain duplicated under workspaces, run `npm dedupe --pa The repository normally enforces a two-day minimum release age. Do not disable it without explicit approval. This update batch received approval to use `npm_config_min_release_age=0` on install commands only; `.npmrc` remains unchanged. +## Development tooling + +Development tooling uses Biome **2.6.3**, TypeScript **7.0.2**, esbuild **0.28.2**, and tsx **4.23.15**. Biome's schema and recommended preset configuration are migrated together with newly enforced diagnostics. The TypeScript target is **ES2024**, matching Unicode-set regular expressions already used on supported Node runtimes. The relative-import policy checker uses TypeScript 7's exported native AST API (`typescript/unstable/ast` and `typescript/unstable/sync`); these API paths are unstable, so the exact compiler pin and syntax/diagnostic regression tests must move together. Temporary parser projects and native clients are cleaned up after every check. + +`diff` supplies its own declarations, so the obsolete `@types/diff` stub is no longer needed. The rimraf override is synchronized at **6.1.3** in the root and published CLI manifests/locks. Nanoid and protobufjs overrides remain in their consumers' compatible major lines rather than forcing unsupported transitive majors. + ## Runtime libraries Runtime pins use keyring **2.1.2**, Chalk **6.0.1**, diff **9.0.0**, highlight.js **11.12.0**, and HTTP/HTTPS proxy agents **9.1.0**. Terminal tests use xterm/headless **6.0.0**. Node declarations are synchronized at **26.6.5**; the supported runtime minimum remains Node **22.19.0**, and no new runtime APIs are required. From b5838fba30f1b8537b200405b942d0a2573dae7e Mon Sep 17 00:00:00 2001 From: Jay Yu Date: Sun, 25 Oct 2026 15:02:51 -0400 Subject: [PATCH 3/3] chore(ci): assert rendering fixture validity without exit guard --- packages/coding-agent/test/streaming-render-debug.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/test/streaming-render-debug.ts b/packages/coding-agent/test/streaming-render-debug.ts index 7196eafc0..de72b1415 100644 --- a/packages/coding-agent/test/streaming-render-debug.ts +++ b/packages/coding-agent/test/streaming-render-debug.ts @@ -4,6 +4,7 @@ * Run with: npx tsx test/streaming-render-debug.ts */ +import assert from "node:assert/strict"; import type { AssistantMessage } from "@diffexai/diffex-ai"; import { ProcessTerminal, type TUI, TuiMainScreen } from "@diffexai/diffex-tui"; import { readFileSync } from "fs"; @@ -28,10 +29,7 @@ const fixtureMessage: AssistantMessage = JSON.parse( const thinkingContent = fixtureMessage.content.find((c) => c.type === "thinking"); const textContent = fixtureMessage.content.find((c) => c.type === "text"); -if (thinkingContent?.type !== "thinking") { - console.error("No thinking content in fixture"); - process.exit(1); -} +assert.ok(thinkingContent, "No thinking content in fixture"); const fullThinkingText = thinkingContent.thinking; const fullTextContent = textContent && textContent.type === "text" ? textContent.text : "";