diff --git a/package-lock.json b/package-lock.json index 8d62a9c75..10f4b3772 100644 --- a/package-lock.json +++ b/package-lock.json @@ -794,6 +794,10 @@ "resolved": "packages/coding-agent", "link": true }, + "node_modules/@earendil-works/pi-evals": { + "resolved": "packages/evals", + "link": true + }, "node_modules/@earendil-works/pi-server": { "resolved": "packages/server", "link": true @@ -1479,6 +1483,52 @@ "node": ">= 10" } }, + "node_modules/@mariozechner/pi-agent-core": { + "name": "@earendil-works/pi-agent-core", + "version": "0.81.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.81.1.tgz", + "integrity": "sha512-yqbh68CyhqxMov/jUogFJfMqlu2Gd37GAki+tr59YCmAPHfomiCA5ESzusXtpGzABeiZFC/OrRdQ4GwCCOMIHA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@earendil-works/pi-ai": "^0.81.1", + "ignore": "7.0.5", + "typebox": "1.1.38", + "yaml": "2.9.0" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@mariozechner/pi-ai": { + "name": "@earendil-works/pi-ai", + "version": "0.81.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.81.1.tgz", + "integrity": "sha512-hzHE7Z8l5mgJk+ke67Lge0rwS2+wbKJrFKl9o5M1R1rh33+cCT7D1AHz1OAtX5wFs90E1/BTGhyJRTUHaMxGvQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@anthropic-ai/sdk": "0.91.1", + "@aws-sdk/client-bedrock-runtime": "3.1048.0", + "@google/genai": "1.52.0", + "@mistralai/mistralai": "2.2.6", + "@opentelemetry/api": "1.9.0", + "@smithy/node-http-handler": "4.7.3", + "http-proxy-agent": "7.0.2", + "https-proxy-agent": "7.0.6", + "openai": "6.26.0", + "partial-json": "0.1.7", + "typebox": "1.1.38" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/@mistralai/mistralai": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz", @@ -2288,6 +2338,151 @@ "win32" ] }, + "node_modules/@vitest-evals/core": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@vitest-evals/core/-/core-0.15.0.tgz", + "integrity": "sha512-kqyg3ocVtZxr3rP9oh01oAycRgXNRasvTgLZ8MbFGBJ4BRd4tk7Bgww253HY7fw7ZFUp4UPYNmDp5AcHajm++w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "zod": ">=3 <5" + } + }, + "node_modules/@vitest-evals/harness-pi-ai": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@vitest-evals/harness-pi-ai/-/harness-pi-ai-0.15.0.tgz", + "integrity": "sha512-iigLAFAZdJ4wefndL2pkX69lJ682QisN8dNhXx/rsJQGTsjXA0dQekw5uww8CCwOIJVagyII8y9XOmdJutz73Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "@mariozechner/pi-agent-core": ">=0.67 <1", + "@mariozechner/pi-ai": ">=0.67 <1", + "vitest-evals": "*" + } + }, + "node_modules/@vitest-evals/report-ui": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@vitest-evals/report-ui/-/report-ui-0.15.0.tgz", + "integrity": "sha512-icrutiRhZ2T1MrHo2RW09hcEwfi/tV/FgsGAE76Wd0C02tZyCleQRjt11RWhBJiYDPpsjjjTHGCHIO6tmtX5zQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@vitest-evals/core": "0.15.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.9", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@xterm/headless": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.5.0.tgz", @@ -2480,6 +2675,16 @@ "node": ">=20" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -2643,6 +2848,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", @@ -4586,6 +4798,13 @@ "dev": true, "license": "MIT" }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4678,6 +4897,16 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -4726,6 +4955,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4922,6 +5161,137 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vitest": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest-evals": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/vitest-evals/-/vitest-evals-0.15.0.tgz", + "integrity": "sha512-bOYALQdLXKYFRCV8aQ+yLeH7XkmYsTxUWQxLHzJdIEcIBx2v/kPgG3Zg6m231jMCphuoM6xH4YQB3+0pDP7LLw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@vitest-evals/core": "0.15.0", + "@vitest-evals/report-ui": "0.15.0" + }, + "bin": { + "vitest-evals": "bin/vitest-evals.js" + }, + "peerDependencies": { + "ai": ">=4 <7", + "tinyrainbow": ">=2 <4", + "vitest": ">=4 <5", + "zod": ">=3 <5" + }, + "peerDependenciesMeta": { + "ai": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -5101,92 +5471,6 @@ } } }, - "packages/agent/node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/agent/node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/agent/node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.9", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/agent/node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/agent/node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/agent/node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.9", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "packages/agent/node_modules/ast-v8-to-istanbul": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.4.tgz", @@ -5199,23 +5483,6 @@ "js-tokens": "^10.0.0" } }, - "packages/agent/node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "packages/agent/node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, "packages/agent/node_modules/magicast": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", @@ -5228,46 +5495,6 @@ "source-map-js": "^1.2.1" } }, - "packages/agent/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "packages/agent/node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", - "dev": true, - "license": "MIT" - }, - "packages/agent/node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "packages/agent/node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "packages/agent/node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -5275,123 +5502,6 @@ "dev": true, "license": "MIT" }, - "packages/agent/node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/coverage-istanbul": { - "optional": true - }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, - "packages/agent/node_modules/vitest/node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "4.1.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, "packages/ai": { "name": "@earendil-works/pi-ai", "version": "0.82.0", @@ -5431,176 +5541,6 @@ "undici-types": "~7.16.0" } }, - "packages/ai/node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/ai/node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "4.1.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "packages/ai/node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/ai/node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.9", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/ai/node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/ai/node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/ai/node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.9", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/ai/node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "packages/ai/node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, - "packages/ai/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "packages/ai/node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", - "dev": true, - "license": "MIT" - }, - "packages/ai/node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "packages/ai/node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "packages/ai/node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -5608,96 +5548,6 @@ "dev": true, "license": "MIT" }, - "packages/ai/node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/coverage-istanbul": { - "optional": true - }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, "packages/coding-agent": { "name": "@earendil-works/pi-coding-agent", "version": "0.82.0", @@ -5819,149 +5669,6 @@ "undici-types": "~7.16.0" } }, - "packages/coding-agent/node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/coding-agent/node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/coding-agent/node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.9", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/coding-agent/node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/coding-agent/node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/coding-agent/node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.9", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/coding-agent/node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "packages/coding-agent/node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, - "packages/coding-agent/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "packages/coding-agent/node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", - "dev": true, - "license": "MIT" - }, - "packages/coding-agent/node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "packages/coding-agent/node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "packages/coding-agent/node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -5969,123 +5676,83 @@ "dev": true, "license": "MIT" }, - "packages/coding-agent/node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/coverage-istanbul": { - "optional": true - }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } + "packages/evals": { + "name": "@earendil-works/pi-evals", + "version": "0.82.0", + "devDependencies": { + "@earendil-works/pi-coding-agent": "^0.82.0", + "@mariozechner/pi-agent-core": "npm:@earendil-works/pi-agent-core@0.82.0", + "@mariozechner/pi-ai": "npm:@earendil-works/pi-ai@0.82.0", + "@types/node": "24.12.4", + "@vitest-evals/harness-pi-ai": "0.15.0", + "shx": "0.4.0", + "typescript": "5.9.3", + "vitest": "4.1.9", + "vitest-evals": "0.15.0" } }, - "packages/coding-agent/node_modules/vitest/node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "packages/evals/node_modules/@mariozechner/pi-agent-core": { + "name": "@earendil-works/pi-agent-core", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.82.0.tgz", + "integrity": "sha512-bnS9DpOKK5T/F/gQkaOnYdMsuuciWiScfAHHWC+k5OQ0HxjSqMFQvp8keurULLoT4+v8NHv4V14pNvd4hsfC0Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "@earendil-works/pi-ai": "^0.82.0", + "diff": "8.0.4", + "ignore": "7.0.5", + "typebox": "1.1.38", + "yaml": "2.9.0" }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } + "engines": { + "node": ">=22.19.0" } }, + "packages/evals/node_modules/@mariozechner/pi-ai": { + "name": "@earendil-works/pi-ai", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.82.0.tgz", + "integrity": "sha512-8MvW9+zno13sXDuT2kFMnWeTNUufUhPeZDRVO+igGoBRCDWgn7Xh2FkRQI1mRuet6QhF4ENQuLYdIAOyG6BhNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "0.91.1", + "@aws-sdk/client-bedrock-runtime": "3.1048.0", + "@google/genai": "1.52.0", + "@mistralai/mistralai": "2.2.6", + "@opentelemetry/api": "1.9.0", + "@smithy/node-http-handler": "4.7.3", + "http-proxy-agent": "7.0.2", + "https-proxy-agent": "7.0.6", + "openai": "6.26.0", + "partial-json": "0.1.7", + "typebox": "1.1.38" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "packages/evals/node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "packages/evals/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, "packages/server": { "name": "@earendil-works/pi-server", "version": "0.82.0", diff --git a/package.json b/package.json index ae0bb74fa..74979fc70 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,12 @@ "check:model-data": "npm --prefix packages/ai run check:model-data", "generate:model-catalog": "npm --prefix packages/ai run generate-model-catalog", "diff:model-catalog": "node scripts/diff-model-catalog.mjs", + "eval": "npm run eval --workspace=@earendil-works/pi-evals --", "check:model-catalog": "node scripts/publish-model-catalog.mjs --input .artifacts/model-catalog --dry-run", "profile:tui": "node scripts/profile-coding-agent-node.mjs --mode tui", "profile:rpc": "node scripts/profile-coding-agent-node.mjs --mode rpc", - "test": "npm run test --workspaces --if-present", + "test": "npm run test:scripts && npm run test --workspaces --if-present", + "test:scripts": "node --test scripts/*.test.mjs", "version:patch": "npm version patch -ws --no-git-tag-version && node scripts/sync-versions.js && npm install --package-lock-only --ignore-scripts", "version:minor": "npm version minor -ws --no-git-tag-version && node scripts/sync-versions.js && npm install --package-lock-only --ignore-scripts", "version:major": "npm version major -ws --no-git-tag-version && node scripts/sync-versions.js && npm install --package-lock-only --ignore-scripts", diff --git a/packages/coding-agent/vitest.config.ts b/packages/coding-agent/vitest.config.ts index 2c6b8cf33..dbd8f34ac 100644 --- a/packages/coding-agent/vitest.config.ts +++ b/packages/coding-agent/vitest.config.ts @@ -1,38 +1,28 @@ -import { fileURLToPath } from "node:url"; -import { defineConfig } from "vitest/config"; +import { defineConfig, mergeConfig } from "vitest/config"; +import baseConfig, { workspaceSourcePaths } from "../../vitest.base.ts"; -const aiSrcIndex = fileURLToPath(new URL("../ai/src/index.ts", import.meta.url)); -const aiSrcCompat = fileURLToPath(new URL("../ai/src/compat.ts", import.meta.url)); -const aiSrcOAuth = fileURLToPath(new URL("../ai/src/oauth.ts", import.meta.url)); -const aiSrcProviders = fileURLToPath(new URL("../ai/src/providers", import.meta.url)); -const agentSrcIndex = fileURLToPath(new URL("../agent/src/index.ts", import.meta.url)); -const tuiSrcIndex = fileURLToPath(new URL("../tui/src/index.ts", import.meta.url)); - -export default defineConfig({ - test: { - globals: true, - environment: "node", - testTimeout: 30000, - reporters: process.env.GITHUB_ACTIONS ? ["dot", "github-actions"] : ["dot"], - silent: "passed-only", - server: { - deps: { - external: [/@silvia-odwyer\/photon-node/], +export default mergeConfig( + baseConfig, + defineConfig({ + test: { + globals: true, + environment: "node", + testTimeout: 30000, + reporters: process.env.GITHUB_ACTIONS ? ["dot", "github-actions"] : ["dot"], + silent: "passed-only", + server: { + deps: { + external: [/@silvia-odwyer\/photon-node/], + }, }, }, - }, - resolve: { - alias: [ - { find: /^@earendil-works\/pi-ai$/, replacement: aiSrcIndex }, - { find: /^@earendil-works\/pi-ai\/compat$/, replacement: aiSrcCompat }, - { find: /^@earendil-works\/pi-ai\/oauth$/, replacement: aiSrcOAuth }, - { find: /^@earendil-works\/pi-ai\/providers\/(.+)$/, replacement: `${aiSrcProviders}/$1.ts` }, - { find: /^@earendil-works\/pi-agent-core$/, replacement: agentSrcIndex }, - { find: /^@earendil-works\/pi-tui$/, replacement: tuiSrcIndex }, - { find: /^@mariozechner\/pi-ai$/, replacement: aiSrcIndex }, - { find: /^@mariozechner\/pi-ai\/oauth$/, replacement: aiSrcOAuth }, - { find: /^@mariozechner\/pi-agent-core$/, replacement: agentSrcIndex }, - { find: /^@mariozechner\/pi-tui$/, replacement: tuiSrcIndex }, - ], - }, -}); + resolve: { + alias: [ + { find: /^@mariozechner\/pi-ai$/, replacement: workspaceSourcePaths.aiIndex }, + { find: /^@mariozechner\/pi-ai\/oauth$/, replacement: workspaceSourcePaths.aiOAuth }, + { find: /^@mariozechner\/pi-agent-core$/, replacement: workspaceSourcePaths.agentIndex }, + { find: /^@mariozechner\/pi-tui$/, replacement: workspaceSourcePaths.tuiIndex }, + ], + }, + }), +); diff --git a/packages/evals/README.md b/packages/evals/README.md new file mode 100644 index 000000000..2e938cb0a --- /dev/null +++ b/packages/evals/README.md @@ -0,0 +1,34 @@ +# Pi evals + +Behavioral evaluations for Pi using `vitest-evals` and its Pi harness. + +## Running + +From the repository root, run with an explicit provider and model: + +```bash +npm run eval -- --provider openai-codex --model gpt-5.4 +``` + +When invoked from a Pi Bash tool, the current session supplies `PI_PROVIDER` and `PI_MODEL`, so this is sufficient: + +```bash +npm run eval +``` + +The runner requires both values and never falls back to another model. Additional arguments are forwarded to Vitest, for example: + +```bash +npm run eval -- -t "capital of France" +``` + +Authentication is resolved by Pi's normal `ModelRuntime`. Subscription-backed providers such as `openai-codex` use credentials from the user's Pi configuration. API-backed providers use their standard environment variables, such as `OPENAI_API_KEY` for `openai`. + +## Pi harness compatibility + +`@vitest-evals/harness-pi-ai` 0.15.0 still declares and imports the former +`@mariozechner/pi-ai` and `@mariozechner/pi-agent-core` package names. The eval +package provides aliases at the current Pi version and routes the legacy AI +import through `src/pi-ai-compat.ts`. Keep those alias versions synchronized +with the Pi workspace version until the upstream harness supports the current +`@earendil-works` packages. diff --git a/packages/evals/package.json b/packages/evals/package.json new file mode 100644 index 000000000..ab7773ffb --- /dev/null +++ b/packages/evals/package.json @@ -0,0 +1,21 @@ +{ + "name": "@earendil-works/pi-evals", + "version": "0.82.0", + "private": true, + "type": "module", + "scripts": { + "clean": "shx rm -rf .eval", + "eval": "node scripts/run-evals.mjs" + }, + "devDependencies": { + "@earendil-works/pi-coding-agent": "^0.82.0", + "@mariozechner/pi-agent-core": "npm:@earendil-works/pi-agent-core@0.82.0", + "@mariozechner/pi-ai": "npm:@earendil-works/pi-ai@0.82.0", + "@types/node": "24.12.4", + "@vitest-evals/harness-pi-ai": "0.15.0", + "shx": "0.4.0", + "typescript": "5.9.3", + "vitest-evals": "0.15.0", + "vitest": "4.1.9" + } +} diff --git a/packages/evals/scripts/run-evals.mjs b/packages/evals/scripts/run-evals.mjs new file mode 100644 index 000000000..84799e09d --- /dev/null +++ b/packages/evals/scripts/run-evals.mjs @@ -0,0 +1,78 @@ +import { spawnSync } from "node:child_process"; +import { createRequire } from "node:module"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const args = process.argv.slice(2); +let provider; +let model; +let hasCliModelSelection = false; +const vitestArgs = []; + +for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; + if (arg === "--provider" || arg === "--model") { + const value = args[index + 1]; + if (!value || value.startsWith("-")) { + console.error(`Missing value for ${arg}`); + process.exit(1); + } + if (arg === "--provider") provider = value; + else model = value; + hasCliModelSelection = true; + index += 1; + continue; + } + if (arg.startsWith("--provider=")) { + provider = arg.slice("--provider=".length); + hasCliModelSelection = true; + continue; + } + if (arg.startsWith("--model=")) { + model = arg.slice("--model=".length); + hasCliModelSelection = true; + continue; + } + vitestArgs.push(arg); +} + +if (hasCliModelSelection && (!provider || !model)) { + console.error("CLI model selection requires both --provider and --model."); + process.exit(1); +} + +provider ??= process.env.PI_PROVIDER; +model ??= process.env.PI_MODEL; + +if (!provider || !model) { + console.error( + "No eval model selected. Pass --provider and --model, or set PI_PROVIDER and PI_MODEL.", + ); + process.exit(1); +} + +const require = createRequire(import.meta.url); +const vitestPackagePath = require.resolve("vitest/package.json"); +const vitestCliPath = resolve(dirname(vitestPackagePath), "vitest.mjs"); + +console.error(`[eval] provider=${provider} model=${model}`); +const result = spawnSync( + process.execPath, + [vitestCliPath, "run", "--config", "vitest.config.ts", ...vitestArgs], + { + cwd: packageRoot, + stdio: "inherit", + env: { + ...process.env, + PI_PROVIDER: provider, + PI_MODEL: model, + }, + }, +); + +if (result.error) { + throw result.error; +} + +process.exit(result.status ?? 1); diff --git a/packages/evals/src/general-knowledge.eval.ts b/packages/evals/src/general-knowledge.eval.ts new file mode 100644 index 000000000..7354bb4ec --- /dev/null +++ b/packages/evals/src/general-knowledge.eval.ts @@ -0,0 +1,15 @@ +import { expect } from "vitest"; +import { describeEval } from "vitest-evals"; +import { piCodingAgentHarness } from "./pi-harness.ts"; + +describeEval("general knowledge", { harness: piCodingAgentHarness }, (it) => { + it("knows the capital of France", async ({ run }) => { + const result = await run("What's the capital of France? Respond with only the city name."); + + expect(result.output.trim()).toBe("Paris"); + expect(result.errors).toEqual([]); + expect(result.usage.provider).toBe(process.env.PI_PROVIDER); + expect(result.usage.model).toBe(process.env.PI_MODEL); + expect(result.usage.totalTokens).toBeGreaterThan(0); + }); +}); diff --git a/packages/evals/src/pi-ai-compat.ts b/packages/evals/src/pi-ai-compat.ts new file mode 100644 index 000000000..7e8f7ed31 --- /dev/null +++ b/packages/evals/src/pi-ai-compat.ts @@ -0,0 +1,5 @@ +// @vitest-evals/harness-pi-ai 0.15.0 imports completeSimple from the legacy +// @mariozechner/pi-ai entry point. Remove this shim when the harness supports +// the current @earendil-works/pi-ai API. +export * from "@earendil-works/pi-ai"; +export { completeSimple } from "@earendil-works/pi-ai/compat"; diff --git a/packages/evals/src/pi-harness.ts b/packages/evals/src/pi-harness.ts new file mode 100644 index 000000000..0585a13ea --- /dev/null +++ b/packages/evals/src/pi-harness.ts @@ -0,0 +1,182 @@ +import { mkdir, mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { + createAgentSession, + DefaultResourceLoader, + ModelRuntime, + SessionManager, + SettingsManager, +} from "@earendil-works/pi-coding-agent"; +import { type PiAiCreateAgentArgs, type PiAiRuntime, type PiAiToolset, piAiHarness } from "@vitest-evals/harness-pi-ai"; + +type EvalSession = Awaited>["session"]; + +interface EvalRunResult { + output: string; + metrics: { + provider: string; + model: string; + inputTokens: number; + outputTokens: number; + totalTokens: number; + }; +} + +async function cleanupEvalResources(session: EvalSession | undefined, removeRoot: () => Promise): Promise { + const failures: unknown[] = []; + if (session) { + try { + session.dispose(); + } catch (error) { + failures.push(error); + } + } + try { + await removeRoot(); + } catch (error) { + failures.push(error); + } + if (failures.length === 1) { + throw failures[0]; + } + if (failures.length > 1) { + throw new AggregateError(failures, "Pi eval session disposal and temporary directory cleanup failed."); + } +} + +function getRequiredModelSelection(): { provider: string; model: string } { + const provider = process.env.PI_PROVIDER?.trim(); + const model = process.env.PI_MODEL?.trim(); + if (!provider || !model) { + throw new Error("PI_PROVIDER and PI_MODEL must both be set for eval runs."); + } + return { provider, model }; +} + +async function createPiEvalAgent({ context }: PiAiCreateAgentArgs) { + const signal = context.signal; + signal?.throwIfAborted(); + const selection = getRequiredModelSelection(); + const modelRuntime = await ModelRuntime.create(); + signal?.throwIfAborted(); + const model = modelRuntime.getModel(selection.provider, selection.model); + if (!model) { + throw new Error(`Eval model not found: ${selection.provider}/${selection.model}`); + } + const auth = await modelRuntime.checkAuth(selection.provider); + signal?.throwIfAborted(); + if (!auth) { + throw new Error(`No authentication configured for eval model ${selection.provider}/${selection.model}.`); + } + + const root = await mkdtemp(join(tmpdir(), "pi-eval-")); + const cwd = join(root, "workspace"); + const agentDir = join(root, "agent"); + const cleanup = () => rm(root, { recursive: true, force: true }); + let session: EvalSession | undefined; + try { + signal?.throwIfAborted(); + await Promise.all([mkdir(cwd), mkdir(agentDir)]); + signal?.throwIfAborted(); + + const settingsManager = SettingsManager.inMemory(); + const resourceLoader = new DefaultResourceLoader({ + cwd, + agentDir, + settingsManager, + noExtensions: true, + noSkills: true, + noPromptTemplates: true, + noThemes: true, + noContextFiles: true, + }); + await resourceLoader.reload(); + signal?.throwIfAborted(); + + session = ( + await createAgentSession({ + cwd, + agentDir, + model, + modelRuntime, + thinkingLevel: "off", + noTools: "all", + resourceLoader, + settingsManager, + sessionManager: SessionManager.inMemory(cwd), + }) + ).session; + signal?.throwIfAborted(); + } catch (error) { + try { + await cleanupEvalResources(session, cleanup); + } catch (cleanupError) { + throw new AggregateError([error, cleanupError], "Pi eval setup failed and cleanup also failed."); + } + throw error; + } + if (!session) { + throw new Error("Pi eval session setup completed without a session."); + } + + return { + agent: session.agent, + async run(input: string, runtime: PiAiRuntime, string>) { + const abort = () => void session.abort(); + runtime.signal?.addEventListener("abort", abort, { once: true }); + let result: EvalRunResult; + try { + runtime.signal?.throwIfAborted(); + await session.prompt(input); + const assistant = session.messages + .slice() + .reverse() + .find((message) => message.role === "assistant"); + if (!assistant) { + throw new Error("Pi eval did not produce an assistant message."); + } + if (assistant.stopReason !== "stop") { + throw new Error(assistant.errorMessage ?? `Pi eval stopped unexpectedly: ${assistant.stopReason}`); + } + + const output = session.getLastAssistantText(); + if (!output) { + throw new Error("Pi eval produced an empty assistant response."); + } + + const stats = session.getSessionStats(); + runtime.events.assistant(output, { + provider: model.provider, + model: model.id, + }); + result = { + output, + metrics: { + provider: model.provider, + model: model.id, + inputTokens: stats.tokens.input, + outputTokens: stats.tokens.output, + totalTokens: stats.tokens.total, + }, + }; + } catch (error) { + runtime.signal?.removeEventListener("abort", abort); + try { + await cleanupEvalResources(session, cleanup); + } catch (cleanupError) { + throw new AggregateError([error, cleanupError], "Pi eval run failed and cleanup also failed."); + } + throw error; + } + runtime.signal?.removeEventListener("abort", abort); + await cleanupEvalResources(session, cleanup); + return result; + }, + }; +} + +export const piCodingAgentHarness = piAiHarness({ + name: "pi-coding-agent", + agent: createPiEvalAgent, +}); diff --git a/packages/evals/tsconfig.json b/packages/evals/tsconfig.json new file mode 100644 index 000000000..54e7cdf3f --- /dev/null +++ b/packages/evals/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": ["node", "vitest"] + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/evals/vitest.config.ts b/packages/evals/vitest.config.ts new file mode 100644 index 000000000..6e7c6aa1d --- /dev/null +++ b/packages/evals/vitest.config.ts @@ -0,0 +1,31 @@ +import { fileURLToPath } from "node:url"; +import { defineConfig, mergeConfig } from "vitest/config"; +import baseConfig, { workspaceSourcePaths } from "../../vitest.base.ts"; + +const piAiCompatIndex = fileURLToPath(new URL("./src/pi-ai-compat.ts", import.meta.url)); + +export default mergeConfig( + baseConfig, + defineConfig({ + test: { + environment: "node", + fileParallelism: false, + include: ["src/**/*.eval.ts"], + testTimeout: 120000, + hookTimeout: 30000, + reporters: ["vitest-evals/reporter"], + server: { + deps: { + inline: [/@vitest-evals\/harness-pi-ai/], + }, + }, + }, + resolve: { + alias: [ + { find: /^@earendil-works\/pi-coding-agent$/, replacement: workspaceSourcePaths.codingAgentIndex }, + { find: /^@mariozechner\/pi-ai$/, replacement: piAiCompatIndex }, + { find: /^@mariozechner\/pi-agent-core$/, replacement: workspaceSourcePaths.agentIndex }, + ], + }, + }), +); diff --git a/scripts/sync-versions.js b/scripts/sync-versions.js index 88ebdf876..01291d810 100644 --- a/scripts/sync-versions.js +++ b/scripts/sync-versions.js @@ -1,29 +1,47 @@ #!/usr/bin/env node /** - * Syncs all non-private workspace package dependency versions to match their current versions. - * This ensures release packages, including unpublished packages, use lockstep versioning. + * Validates lockstep versions for published packages, then synchronizes + * internal dependency versions in all workspace packages, including private ones. */ import { readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { findPackageDirectories } from "./package-workspaces.mjs"; -const packages = findPackageDirectories() +const GENERATED_PACKAGE_SUFFIXES = [join("coding-agent", "install-lock")]; + +function synchronizedDependencyVersion(dependencyName, currentSpecifier, versionMap) { + const directVersion = versionMap.get(dependencyName); + if (directVersion) { + return `^${directVersion}`; + } + if (typeof currentSpecifier !== "string" || !currentSpecifier.startsWith("npm:")) { + return null; + } + + const separator = currentSpecifier.lastIndexOf("@"); + const aliasPackage = currentSpecifier.slice("npm:".length, separator); + const aliasVersion = versionMap.get(aliasPackage); + return aliasVersion ? `npm:${aliasPackage}@${aliasVersion}` : null; +} + +const packageRoot = process.argv[2] ?? "packages"; +const workspacePackages = findPackageDirectories(packageRoot) + .filter((directory) => !GENERATED_PACKAGE_SUFFIXES.some((suffix) => directory.endsWith(suffix))) .map((directory) => { const path = join(directory, "package.json"); return { data: JSON.parse(readFileSync(path, "utf8")), path }; - }) - .filter((pkg) => pkg.data.private !== true); - -const versionMap = new Map(packages.map((pkg) => [pkg.data.name, pkg.data.version])); + }); +const publishedPackages = workspacePackages.filter((pkg) => pkg.data.private !== true); +const versionMap = new Map(workspacePackages.map((pkg) => [pkg.data.name, pkg.data.version])); console.log("Current versions:"); -for (const [name, version] of [...versionMap].sort(([a], [b]) => a.localeCompare(b))) { - console.log(` ${name}: ${version}`); +for (const pkg of [...publishedPackages].sort((a, b) => a.data.name.localeCompare(b.data.name))) { + console.log(` ${pkg.data.name}: ${pkg.data.version}`); } -const versions = new Set(versionMap.values()); +const versions = new Set(publishedPackages.map((pkg) => pkg.data.version)); if (versions.size > 1) { console.error("\nERROR: Not all non-private packages have the same version."); console.error("Expected lockstep versioning. Run one of:"); @@ -36,37 +54,33 @@ if (versions.size > 1) { console.log("\nAll non-private packages are at the same version (lockstep)."); let totalUpdates = 0; -for (const pkg of packages) { - let updated = false; - +const updatedPackages = new Set(); +for (const pkg of workspacePackages) { for (const dependencyType of ["dependencies", "devDependencies"]) { const dependencies = pkg.data[dependencyType]; if (!dependencies) { continue; } - for (const [dependencyName, currentVersion] of Object.entries(dependencies)) { - const dependencyVersion = versionMap.get(dependencyName); - if (!dependencyVersion) { - continue; - } - - const newVersion = `^${dependencyVersion}`; - if (currentVersion === newVersion) { + for (const [dependencyName, currentSpecifier] of Object.entries(dependencies)) { + const newSpecifier = synchronizedDependencyVersion(dependencyName, currentSpecifier, versionMap); + if (!newSpecifier || currentSpecifier === newSpecifier) { continue; } console.log(`\n${pkg.data.name}:`); - console.log(` ${dependencyName}: ${currentVersion} → ${newVersion}${dependencyType === "devDependencies" ? " (devDependencies)" : ""}`); - dependencies[dependencyName] = newVersion; - updated = true; + console.log( + ` ${dependencyName}: ${currentSpecifier} → ${newSpecifier}${dependencyType === "devDependencies" ? " (devDependencies)" : ""}`, + ); + dependencies[dependencyName] = newSpecifier; + updatedPackages.add(pkg); totalUpdates++; } } +} - if (updated) { - writeFileSync(pkg.path, `${JSON.stringify(pkg.data, null, "\t")}\n`); - } +for (const pkg of updatedPackages) { + writeFileSync(pkg.path, `${JSON.stringify(pkg.data, null, "\t")}\n`); } if (totalUpdates === 0) { diff --git a/scripts/sync-versions.test.mjs b/scripts/sync-versions.test.mjs new file mode 100644 index 000000000..433ccb51c --- /dev/null +++ b/scripts/sync-versions.test.mjs @@ -0,0 +1,75 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const syncVersionsScript = fileURLToPath(new URL("./sync-versions.js", import.meta.url)); + +async function writeManifest(root, relativeDirectory, manifest) { + const directory = join(root, relativeDirectory); + await mkdir(directory, { recursive: true }); + await writeFile(join(directory, "package.json"), `${JSON.stringify(manifest, null, "\t")}\n`); +} + +async function readManifest(root, relativeDirectory) { + return JSON.parse(await readFile(join(root, relativeDirectory, "package.json"), "utf8")); +} + +function runSyncVersions(root) { + return spawnSync(process.execPath, [syncVersionsScript, join(root, "packages")], { + cwd: root, + encoding: "utf8", + }); +} + +test("synchronizes private dependencies without touching generated manifests or relaxing published lockstep", async () => { + const root = await mkdtemp(join(tmpdir(), "pi-sync-versions-")); + try { + await writeManifest(root, "packages/ai", { + name: "@earendil-works/pi-ai", + version: "2.0.0", + }); + await writeManifest(root, "packages/coding-agent", { + name: "@earendil-works/pi-coding-agent", + version: "2.0.0", + }); + await writeManifest(root, "packages/evals", { + name: "@earendil-works/pi-evals", + version: "9.9.9", + private: true, + dependencies: { + "@earendil-works/pi-coding-agent": "^1.0.0", + "@mariozechner/pi-ai": "npm:@earendil-works/pi-ai@1.0.0", + }, + }); + await writeManifest(root, "packages/coding-agent/install-lock", { + name: "generated-install-lock", + version: "0.0.0", + private: true, + dependencies: { + "@earendil-works/pi-coding-agent": "^1.0.0", + }, + }); + + const result = runSyncVersions(root); + assert.equal(result.status, 0, result.stderr); + + const evalsManifest = await readManifest(root, "packages/evals"); + assert.equal(evalsManifest.dependencies["@earendil-works/pi-coding-agent"], "^2.0.0"); + assert.equal(evalsManifest.dependencies["@mariozechner/pi-ai"], "npm:@earendil-works/pi-ai@2.0.0"); + const generatedManifest = await readManifest(root, "packages/coding-agent/install-lock"); + assert.equal(generatedManifest.dependencies["@earendil-works/pi-coding-agent"], "^1.0.0"); + + await writeManifest(root, "packages/ai", { + name: "@earendil-works/pi-ai", + version: "3.0.0", + }); + const lockstepFailure = runSyncVersions(root); + assert.equal(lockstepFailure.status, 1, lockstepFailure.stderr); + } finally { + await rm(root, { recursive: true, force: true }); + } +}); diff --git a/vitest.base.ts b/vitest.base.ts new file mode 100644 index 000000000..1ede2a2f3 --- /dev/null +++ b/vitest.base.ts @@ -0,0 +1,28 @@ +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vitest/config"; + +export const workspaceSourcePaths = { + aiIndex: fileURLToPath(new URL("./packages/ai/src/index.ts", import.meta.url)), + aiCompat: fileURLToPath(new URL("./packages/ai/src/compat.ts", import.meta.url)), + aiOAuth: fileURLToPath(new URL("./packages/ai/src/oauth.ts", import.meta.url)), + aiProviders: fileURLToPath(new URL("./packages/ai/src/providers", import.meta.url)), + agentIndex: fileURLToPath(new URL("./packages/agent/src/index.ts", import.meta.url)), + codingAgentIndex: fileURLToPath(new URL("./packages/coding-agent/src/index.ts", import.meta.url)), + tuiIndex: fileURLToPath(new URL("./packages/tui/src/index.ts", import.meta.url)), +} as const; + +export default defineConfig({ + resolve: { + alias: [ + { find: /^@earendil-works\/pi-ai$/, replacement: workspaceSourcePaths.aiIndex }, + { find: /^@earendil-works\/pi-ai\/compat$/, replacement: workspaceSourcePaths.aiCompat }, + { find: /^@earendil-works\/pi-ai\/oauth$/, replacement: workspaceSourcePaths.aiOAuth }, + { + find: /^@earendil-works\/pi-ai\/providers\/(.+)$/, + replacement: `${workspaceSourcePaths.aiProviders}/$1.ts`, + }, + { find: /^@earendil-works\/pi-agent-core$/, replacement: workspaceSourcePaths.agentIndex }, + { find: /^@earendil-works\/pi-tui$/, replacement: workspaceSourcePaths.tuiIndex }, + ], + }, +});