From bbc1c158187759e6734804aaf7667b23c010df4c Mon Sep 17 00:00:00 2001 From: Chongsun Yu Date: Sat, 1 Aug 2026 19:42:45 +0200 Subject: [PATCH] fix(release): stage exact bundled dependencies --- package-lock.json | 8 +- packages/coding-agent/changes.md | 22 + .../install-lock/package-lock.json | 14 +- packages/coding-agent/publish-deps.lock.json | 638 +++++++++++------- packages/protocol/package.json | 2 +- scripts/generate-coding-agent-shrinkwrap.mjs | 40 +- scripts/install-lock-utils.mjs | 5 +- ...are-senpi-bundled-workspaces-copy.test.mjs | 74 +- ...are-senpi-bundled-workspaces-pack.test.mjs | 84 +-- scripts/prepare-senpi-bundled-workspaces.mjs | 43 +- ...-senpi-bundled-workspaces.prepare.test.mjs | 19 +- .../prepare-senpi-bundled-workspaces.test.mjs | 56 ++ scripts/prepare-senpi-publish-manifest.mjs | 36 +- .../prepare-senpi-publish-optionals.test.mjs | 4 + .../publish-registry-dependencies.test.mjs | 11 +- 15 files changed, 693 insertions(+), 363 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c9763ebc..bbb0981e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9569,7 +9569,7 @@ "version": "2026.8.1", "license": "MIT", "dependencies": { - "typebox": "1.3.7" + "typebox": "1.3.8" }, "devDependencies": { "shx": "0.4.0", @@ -9705,6 +9705,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "packages/protocol/node_modules/typebox": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.8.tgz", + "integrity": "sha512-xYaJgF0KMvBViKWRaKTAtfR6sDt/yH6xAjGAHXYJxKxUF4pVyPXZZhIlwOg6cDIE81N7L0pyIHs136RHBm6rLQ==", + "license": "MIT" + }, "packages/protocol/node_modules/vitest": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", diff --git a/packages/coding-agent/changes.md b/packages/coding-agent/changes.md index dd748ce0e..c91cb9382 100644 --- a/packages/coding-agent/changes.md +++ b/packages/coding-agent/changes.md @@ -1,5 +1,27 @@ # Local fork changes +## 2026-08-01 — Repair published dependency staging integrity + +### What changed + +- Rebased publish-lock dependency paths from the coding-agent workspace into the standalone package layout. +- Aligned the protocol workspace on `typebox@1.3.8` and regenerated the publish/install dependency artifacts. +- Added the client and protocol workspaces to the bundled package set and materialized lock-pinned dependencies beneath their owning workspaces. +- Added a version-gated publish-stage normalization for `@anthropic-ai/claude-agent-sdk@0.3.220` that removes only its incompatible `@anthropic-ai/sdk >=0.93.0` peer while preserving the browser-safe Senpi SDK pin. +- Added regressions for staged TypeBox integrity and the narrowed Agent SDK peer waiver. + +### Why + +The published package could bundle a stale TypeBox copy and expose an unsatisfiable Anthropic peer graph even though the source manifest declared the intended versions. Keeping source and output lock paths distinct preserves the browser build while making a default npm consumer install deterministic. + +### Extension Boundary + +No extension API or runtime hook contract changed. The compatibility fixture updates live in standalone extension repositories; Senpi changes are confined to publish staging, generated locks, and bundled workspace metadata. + +### Merge Conflict Signals + +When merging upstream packaging changes, preserve the source/output lock-path rebasing, exact-version source selection, nested workspace dependency staging, client/protocol bundle entries, and the `claude-agent-sdk@0.3.220`-only peer normalization. Re-run the full script suite and isolated packed install after resolving conflicts in these areas. + ## 2026-08-01 — Reconcile fork runtime contracts after the upstream merge ### What changed diff --git a/packages/coding-agent/install-lock/package-lock.json b/packages/coding-agent/install-lock/package-lock.json index 12c56667a..3c71cf85d 100644 --- a/packages/coding-agent/install-lock/package-lock.json +++ b/packages/coding-agent/install-lock/package-lock.json @@ -1266,12 +1266,18 @@ "resolved": "https://registry.npmjs.org/@earendil-works/pi-protocol/-/pi-protocol-2026.8.1.tgz", "license": "MIT", "dependencies": { - "typebox": "1.3.7" + "typebox": "1.3.8" }, "engines": { "node": ">=22.19.0" } }, + "node_modules/@earendil-works/pi-protocol/node_modules/typebox": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.8.tgz", + "integrity": "sha512-xYaJgF0KMvBViKWRaKTAtfR6sDt/yH6xAjGAHXYJxKxUF4pVyPXZZhIlwOg6cDIE81N7L0pyIHs136RHBm6rLQ==", + "license": "MIT" + }, "node_modules/@earendil-works/pi-pty": { "version": "2026.8.1", "resolved": "https://registry.npmjs.org/@earendil-works/pi-pty/-/pi-pty-2026.8.1.tgz", @@ -3595,12 +3601,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/typebox": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz", - "integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==", - "license": "MIT" - }, "node_modules/undici": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/undici/-/undici-8.9.0.tgz", diff --git a/packages/coding-agent/publish-deps.lock.json b/packages/coding-agent/publish-deps.lock.json index 65cd08ad7..42c5b0818 100644 --- a/packages/coding-agent/publish-deps.lock.json +++ b/packages/coding-agent/publish-deps.lock.json @@ -63,6 +63,30 @@ "node": ">=24.0.0" } }, + "node_modules/@anthropic-ai/claude-agent-sdk": { + "version": "0.3.220", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.220.tgz", + "integrity": "sha512-glc7SdwPkOkLw8oxwLo9PKTdLJGqW/PIR4urWXFoRtX9YllwozsEVc5Tc1+EvLSkfrsxPJqQWqOgpjUOQXf1oA==", + "license": "SEE LICENSE IN README.md", + "optionalDependencies": { + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.220", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.220", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.220", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.220", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.220", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.220", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.220", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.220" + }, + "peerDependencies": { + "@anthropic-ai/sdk": ">=0.93.0", + "@modelcontextprotocol/sdk": "^1.29.0", + "zod": "^4.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { "version": "0.3.220", "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.220.tgz", @@ -246,74 +270,23 @@ "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "license": "MIT" }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, "node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1048.0.tgz", - "integrity": "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==", + "version": "3.1095.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1095.0.tgz", + "integrity": "sha512-DWcwoQdQPrQJxnG3hz1sG88EjfzGv3SReRy4mtAO+pZXtLX+trriTa20o+qcTmVzwqS43JXtX26ythOzErev9A==", "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/credential-provider-node": "^3.972.42", - "@aws-sdk/eventstream-handler-node": "^3.972.16", - "@aws-sdk/middleware-eventstream": "^3.972.12", - "@aws-sdk/middleware-websocket": "^3.972.19", - "@aws-sdk/token-providers": "3.1048.0", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", + "@aws-sdk/core": "^3.977.0", + "@aws-sdk/credential-provider-node": "^3.972.72", + "@aws-sdk/eventstream-handler-node": "^3.972.30", + "@aws-sdk/middleware-eventstream": "^3.972.25", + "@aws-sdk/middleware-websocket": "^3.972.43", + "@aws-sdk/token-providers": "3.1095.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.8", + "@smithy/fetch-http-handler": "^5.6.10", + "@smithy/node-http-handler": "^4.9.10", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { @@ -615,16 +588,16 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1048.0.tgz", - "integrity": "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==", + "version": "3.1095.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1095.0.tgz", + "integrity": "sha512-65SudS6y4nzaYHybtqcpm3sHe5jLhdMn68HRKS1nUx690BtQeaAQOoujQ+dpOjBATIVGVgKKjEP8tR+U06QJQA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", + "@aws-sdk/core": "^3.977.0", + "@aws-sdk/nested-clients": "^3.997.35", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.8", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { @@ -644,18 +617,6 @@ "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/@aws-sdk/xml-builder": { "version": "3.972.37", "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.37.tgz", @@ -678,39 +639,6 @@ "node": ">=18.0.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.7" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/runtime": { "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", @@ -720,19 +648,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@bramus/specificity": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", @@ -761,6 +676,58 @@ "node": ">=24.0.0" } }, + "node_modules/@code-yeongyu/senpi-codemode/node_modules/@babel/helper-string-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", + "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@code-yeongyu/senpi-codemode/node_modules/@babel/helper-validator-identifier": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.4.tgz", + "integrity": "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@code-yeongyu/senpi-codemode/node_modules/@babel/parser": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.4.tgz", + "integrity": "sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@code-yeongyu/senpi-codemode/node_modules/@babel/types": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.4.tgz", + "integrity": "sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.0", + "@babel/helper-validator-identifier": "^8.0.4" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@code-yeongyu/senpi-codemode/node_modules/typebox": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.8.tgz", + "integrity": "sha512-xYaJgF0KMvBViKWRaKTAtfR6sDt/yH6xAjGAHXYJxKxUF4pVyPXZZhIlwOg6cDIE81N7L0pyIHs136RHBm6rLQ==", + "license": "MIT" + }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", @@ -910,6 +877,30 @@ "node": ">=24.0.0" } }, + "node_modules/@earendil-works/pi-agent-core/node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@earendil-works/pi-agent-core/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@earendil-works/pi-agent-core/node_modules/typebox": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.8.tgz", + "integrity": "sha512-xYaJgF0KMvBViKWRaKTAtfR6sDt/yH6xAjGAHXYJxKxUF4pVyPXZZhIlwOg6cDIE81N7L0pyIHs136RHBm6rLQ==", + "license": "MIT" + }, "node_modules/@earendil-works/pi-ai": { "version": "2026.8.1", "inBundle": true, @@ -938,6 +929,192 @@ "node": ">=24.0.0" } }, + "node_modules/@earendil-works/pi-ai/node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.1095.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1095.0.tgz", + "integrity": "sha512-DWcwoQdQPrQJxnG3hz1sG88EjfzGv3SReRy4mtAO+pZXtLX+trriTa20o+qcTmVzwqS43JXtX26ythOzErev9A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.0", + "@aws-sdk/credential-provider-node": "^3.972.72", + "@aws-sdk/eventstream-handler-node": "^3.972.30", + "@aws-sdk/middleware-eventstream": "^3.972.25", + "@aws-sdk/middleware-websocket": "^3.972.43", + "@aws-sdk/token-providers": "3.1095.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.8", + "@smithy/fetch-http-handler": "^5.6.10", + "@smithy/node-http-handler": "^4.9.10", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/types": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz", + "integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@aws-sdk/token-providers": { + "version": "3.1095.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1095.0.tgz", + "integrity": "sha512-65SudS6y4nzaYHybtqcpm3sHe5jLhdMn68HRKS1nUx690BtQeaAQOoujQ+dpOjBATIVGVgKKjEP8tR+U06QJQA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.0", + "@aws-sdk/nested-clients": "^3.997.35", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.8", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@aws-sdk/token-providers/node_modules/@smithy/types": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz", + "integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@google/genai": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.13.0.tgz", + "integrity": "sha512-GM7C8Kaomvjz05x5JEO6+l3d/pciL9LxAG9dUjJLD7nTPZ9X0Cfsf2Z7eET6UjgWyUmxXCHtYnQoQ77F9+ZIOQ==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + }, + "engines": { + "node": ">=20.0.0" + }, + "hasInstallScript": true + }, + "node_modules/@earendil-works/pi-ai/node_modules/@mistralai/mistralai": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.5.0.tgz", + "integrity": "sha512-S/r6tkiUHblaDJGsb84WS0ePTF2YHVta2EoTi1NJqHNt5mfRRS2uE4v7hCYV134+an+fk6WgG3+aFfinJbQBjQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.40.0", + "ws": "^8.18.0", + "zod": "^3.25.0 || ^4.0.0", + "zod-to-json-schema": "^3.25.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@smithy/node-http-handler": { + "version": "4.9.11", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.11.tgz", + "integrity": "sha512-slbzbz8taEOzoXv/9y34YNBoE+ZHmddLykCgjDAjvMAsu2nM5s2Gzwa5OGF721tD8s+CKeFUBds5lSj9lcbuDg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.30.0", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@smithy/node-http-handler/node_modules/@smithy/types": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz", + "integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/http-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz", + "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==", + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/https-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/typebox": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.8.tgz", + "integrity": "sha512-xYaJgF0KMvBViKWRaKTAtfR6sDt/yH6xAjGAHXYJxKxUF4pVyPXZZhIlwOg6cDIE81N7L0pyIHs136RHBm6rLQ==", + "license": "MIT" + }, "node_modules/@earendil-works/pi-client": { "version": "2026.8.1", "inBundle": true, @@ -954,12 +1131,18 @@ "inBundle": true, "license": "MIT", "dependencies": { - "typebox": "1.3.7" + "typebox": "1.3.8" }, "engines": { "node": ">=22.19.0" } }, + "node_modules/@earendil-works/pi-protocol/node_modules/typebox": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.8.tgz", + "integrity": "sha512-xYaJgF0KMvBViKWRaKTAtfR6sDt/yH6xAjGAHXYJxKxUF4pVyPXZZhIlwOg6cDIE81N7L0pyIHs136RHBm6rLQ==", + "license": "MIT" + }, "node_modules/@earendil-works/pi-pty": { "version": "2026.8.1", "inBundle": true, @@ -1000,30 +1183,6 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/@google/genai": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", - "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", - "license": "Apache-2.0", - "dependencies": { - "google-auth-library": "^10.3.0", - "p-retry": "^4.6.2", - "protobufjs": "^7.5.4", - "ws": "^8.18.0" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.25.2" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/sdk": { - "optional": true - } - }, - "engines": { - "node": ">=20.0.0" - }, - "hasInstallScript": true - }, "node_modules/@hono/node-server": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.10.tgz", @@ -1216,9 +1375,9 @@ "optional": true }, "node_modules/@mistralai/mistralai": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz", - "integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.5.0.tgz", + "integrity": "sha512-S/r6tkiUHblaDJGsb84WS0ePTF2YHVta2EoTi1NJqHNt5mfRRS2uE4v7hCYV134+an+fk6WgG3+aFfinJbQBjQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", @@ -1291,9 +1450,9 @@ } }, "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", "license": "Apache-2.0", "engines": { "node": ">=8.0.0" @@ -1412,26 +1571,14 @@ "node": ">=18.0.0" } }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@smithy/node-http-handler": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", + "version": "4.9.11", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.11.tgz", + "integrity": "sha512-slbzbz8taEOzoXv/9y34YNBoE+ZHmddLykCgjDAjvMAsu2nM5s2Gzwa5OGF721tD8s+CKeFUBds5lSj9lcbuDg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", + "@smithy/core": "^3.30.0", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { @@ -1464,32 +1611,6 @@ "node": ">=18.0.0" } }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@types/node": { "version": "26.1.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", @@ -1528,12 +1649,12 @@ } }, "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", "license": "MIT", "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/ajv": { @@ -1866,9 +1987,9 @@ } }, "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -2387,29 +2508,31 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz", + "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/iconv-lite": { @@ -2429,9 +2552,9 @@ } }, "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "license": "MIT", "engines": { "node": ">= 4" @@ -2911,12 +3034,12 @@ } }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -2987,6 +3110,23 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent-negotiate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz", + "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==", + "license": "MIT", + "peerDependencies": { + "kerberos": "^2.0.0" + }, + "peerDependenciesMeta": { + "kerberos": { + "optional": true + } + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", @@ -3415,9 +3555,9 @@ } }, "node_modules/typebox": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz", - "integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.8.tgz", + "integrity": "sha512-xYaJgF0KMvBViKWRaKTAtfR6sDt/yH6xAjGAHXYJxKxUF4pVyPXZZhIlwOg6cDIE81N7L0pyIHs136RHBm6rLQ==", "license": "MIT" }, "node_modules/undici": { @@ -3573,9 +3713,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -3589,30 +3729,6 @@ "peerDependencies": { "zod": "^3.25.28 || ^4" } - }, - "packages/coding-agent/node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.3.220", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.220.tgz", - "integrity": "sha512-glc7SdwPkOkLw8oxwLo9PKTdLJGqW/PIR4urWXFoRtX9YllwozsEVc5Tc1+EvLSkfrsxPJqQWqOgpjUOQXf1oA==", - "license": "SEE LICENSE IN README.md", - "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.220", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.220", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.220", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.220", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.220", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.220", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.220", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.220" - }, - "peerDependencies": { - "@anthropic-ai/sdk": ">=0.93.0", - "@modelcontextprotocol/sdk": "^1.29.0", - "zod": "^4.0.0" - }, - "engines": { - "node": ">=18.0.0" - } } } } diff --git a/packages/protocol/package.json b/packages/protocol/package.json index 5da717f07..889a5b2de 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -27,6 +27,6 @@ "directory": "packages/protocol" }, "engines": { "node": ">=22.19.0" }, - "dependencies": { "typebox": "1.3.7" }, + "dependencies": { "typebox": "1.3.8" }, "devDependencies": { "shx": "0.4.0", "vitest": "4.1.9" } } diff --git a/scripts/generate-coding-agent-shrinkwrap.mjs b/scripts/generate-coding-agent-shrinkwrap.mjs index f408cd356..ef7404d87 100644 --- a/scripts/generate-coding-agent-shrinkwrap.mjs +++ b/scripts/generate-coding-agent-shrinkwrap.mjs @@ -3,6 +3,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join, posix, resolve } from "node:path"; import { fileURLToPath } from "node:url"; +import { rebaseResolvedLockPath } from "./install-lock-utils.mjs"; const scriptDir = dirname(fileURLToPath(import.meta.url)); const repoRoot = resolve(scriptDir, ".."); @@ -18,7 +19,7 @@ const manifestRelPath = "packages/coding-agent/publish-deps.lock.json"; const shrinkwrapPath = join(repoRoot, manifestRelPath); const internalPackagePrefixes = ["@earendil-works/pi-", "@code-yeongyu/senpi-codemode"]; const allowedInstallScriptPackages = new Map([ - ["@google/genai@1.52.0", "preinstall is a no-op in the published package"], + ["@google/genai@2.13.0", "preinstall is a no-op in the published package"], ["protobufjs@7.6.5", "postinstall only warns about protobufjs version scheme mismatches"], ]); @@ -210,22 +211,35 @@ function addInternalWorkspace(shrinkwrapPackages, addedPaths, queue, name, works addedPaths.add(outputPath); for (const dependencyName of Object.keys(packageDependencies(packageJson))) { - queue.push({ name: dependencyName, from: outputPath }); + queue.push({ + name: dependencyName, + from: outputPath, + resolveFrom: workspace.lockPath, + sourceBase: workspace.lockPath, + outputBase: outputPath, + }); } } -function addExternalPackage(lockPackages, shrinkwrapPackages, addedPaths, queue, name, from) { - const lockPath = resolveExternalDependency(lockPackages, name, from); - if (addedPaths.has(lockPath)) { +function addExternalPackage(lockPackages, shrinkwrapPackages, addedPaths, queue, item) { + const lockPath = resolveExternalDependency(lockPackages, item.name, item.resolveFrom); + const outputPath = rebaseResolvedLockPath(lockPath, item.sourceBase, item.outputBase); + if (addedPaths.has(outputPath)) { return; } const entry = lockPackages[lockPath]; - shrinkwrapPackages[lockPath] = copyLockEntry(entry); - addedPaths.add(lockPath); + shrinkwrapPackages[outputPath] = copyLockEntry(entry); + addedPaths.add(outputPath); for (const dependencyName of Object.keys(packageDependencies(entry))) { - queue.push({ name: dependencyName, from: lockPath }); + queue.push({ + name: dependencyName, + from: outputPath, + resolveFrom: lockPath, + sourceBase: item.sourceBase, + outputBase: item.outputBase, + }); } } @@ -309,7 +323,13 @@ function generateShrinkwrap() { }; const addedPaths = new Set([""]); const internalNames = new Set(); - const queue = Object.keys(packageDependencies(codingAgentPackage)).map((name) => ({ name, from: "" })); + const queue = Object.keys(packageDependencies(codingAgentPackage)).map((name) => ({ + name, + from: "", + resolveFrom: "packages/coding-agent", + sourceBase: "packages/coding-agent", + outputBase: "", + })); while (queue.length > 0) { const item = queue.shift(); @@ -327,7 +347,7 @@ function generateShrinkwrap() { continue; } - addExternalPackage(lockPackages, shrinkwrapPackages, addedPaths, queue, item.name, item.from); + addExternalPackage(lockPackages, shrinkwrapPackages, addedPaths, queue, item); } const shrinkwrap = { diff --git a/scripts/install-lock-utils.mjs b/scripts/install-lock-utils.mjs index bb603bea2..76be4da7c 100644 --- a/scripts/install-lock-utils.mjs +++ b/scripts/install-lock-utils.mjs @@ -157,12 +157,13 @@ export function resolveExternalDependency(lockPackages, packageName, fromLockPat } export function rebaseResolvedLockPath(lockPath, sourceBase, outputBase) { - if (!sourceBase || !outputBase) { + if (!sourceBase) { return lockPath; } const nestedPrefix = `${sourceBase}/node_modules/`; if (!lockPath.startsWith(nestedPrefix)) { return lockPath; } - return `${outputBase}/node_modules/${lockPath.slice(nestedPrefix.length)}`; + const outputPrefix = outputBase ? `${outputBase}/node_modules/` : "node_modules/"; + return `${outputPrefix}${lockPath.slice(nestedPrefix.length)}`; } diff --git a/scripts/prepare-senpi-bundled-workspaces-copy.test.mjs b/scripts/prepare-senpi-bundled-workspaces-copy.test.mjs index 692e413d0..82be202cd 100644 --- a/scripts/prepare-senpi-bundled-workspaces-copy.test.mjs +++ b/scripts/prepare-senpi-bundled-workspaces-copy.test.mjs @@ -19,10 +19,10 @@ function writeJson(path, value) { writeFileSync(path, `${JSON.stringify(value, undefined, "\t")}\n`); } -function writePackage(root, name) { +function writePackage(root, name, version = "1.0.0") { const packageDir = join(root, "node_modules", name); mkdirSync(packageDir, { recursive: true }); - writeJson(join(packageDir, "package.json"), { name, version: "1.0.0" }); + writeJson(join(packageDir, "package.json"), { name, version }); } function writeShrinkwrap(root, packages) { @@ -38,18 +38,76 @@ function writeShrinkwrap(root, packages) { } describe("copyPublishDependencies", () => { + it("preserves an exact dependency already installed in the coding-agent workspace", () => { + // Given: npm nested the exact coding-agent dependency because the root has + // a different transitive version. + tempDir = mkdtempSync(join(tmpdir(), "senpi-bundle-workspace-dep-")); + writePackage(tempDir, "typebox", "1.3.7"); + writePackage(join(tempDir, "packages", "coding-agent"), "typebox", "1.3.8"); + writeShrinkwrap(tempDir, { + "": { dependencies: { typebox: "1.3.8" } }, + "node_modules/typebox": { version: "1.3.8" }, + }); + + // When + copyPublishDependencies(tempDir); + + // Then + assert.equal( + JSON.parse( + readFileSync(join(tempDir, "packages", "coding-agent", "node_modules", "typebox", "package.json"), "utf8"), + ).version, + "1.3.8", + ); + }); + + it("copies dependencies nested beneath an internal workspace", () => { + // Given + tempDir = mkdtempSync(join(tmpdir(), "senpi-bundle-internal-dep-")); + writePackage(join(tempDir, "packages", "coding-agent"), "@earendil-works/pi-ai", "2026.8.1"); + writePackage(join(tempDir, "packages", "ai"), "@google/genai", "2.13.0"); + writeShrinkwrap(tempDir, { + "": { dependencies: { "@earendil-works/pi-ai": "^2026.8.1" } }, + "node_modules/@earendil-works/pi-ai": { version: "2026.8.1" }, + "node_modules/@earendil-works/pi-ai/node_modules/@google/genai": { version: "2.13.0" }, + }); + + // When + copyPublishDependencies(tempDir); + + // Then + assert.equal( + JSON.parse( + readFileSync( + join( + tempDir, + "packages", + "coding-agent", + "node_modules", + "@earendil-works", + "pi-ai", + "node_modules", + "@google", + "genai", + "package.json", + ), + "utf8", + ), + ).version, + "2.13.0", + ); + }); + it("copies direct publish dependencies and skips internal workspaces and missing optional packages", () => { tempDir = mkdtempSync(join(tmpdir(), "senpi-bundle-deps-")); writePackage(tempDir, "typebox"); writePackage(tempDir, "@scope/pkg"); - writePackage(tempDir, "nested-only"); writeShrinkwrap(tempDir, { "": { dependencies: { typebox: "1.0.0" } }, "node_modules/typebox": { version: "1.0.0" }, "node_modules/@scope/pkg": { version: "1.0.0" }, "node_modules/@earendil-works/pi-ai": { version: "1.0.0" }, "node_modules/missing-optional": { version: "1.0.0", optional: true }, - "node_modules/typebox/node_modules/nested-only": { version: "1.0.0" }, }); copyPublishDependencies(tempDir); @@ -82,14 +140,6 @@ describe("copyPublishDependencies", () => { ), /ENOENT/, ); - assert.throws( - () => - readFileSync( - join(tempDir, "packages", "coding-agent", "node_modules", "typebox", "node_modules", "nested-only"), - "utf8", - ), - /ENOENT/, - ); }); it("copies transitive dependencies nested inside a staged package directory", () => { diff --git a/scripts/prepare-senpi-bundled-workspaces-pack.test.mjs b/scripts/prepare-senpi-bundled-workspaces-pack.test.mjs index a814495bb..28653f734 100644 --- a/scripts/prepare-senpi-bundled-workspaces-pack.test.mjs +++ b/scripts/prepare-senpi-bundled-workspaces-pack.test.mjs @@ -8,12 +8,19 @@ import { nativePrebuildTarget, } from "./prepare-senpi-bundled-workspaces.mjs"; +function clientProtocolFiles(prefix = "package/") { + return [ + "node_modules/@earendil-works/pi-client/package.json", + "node_modules/@earendil-works/pi-client/dist/index.js", + "node_modules/@earendil-works/pi-protocol/package.json", + "node_modules/@earendil-works/pi-protocol/dist/index.js", + ].map((path) => ({ path: `${prefix}${path}` })); +} + describe("assertSenpiPackedWorkspaceFiles", () => { it("rejects senpi package metadata that omits bundled workspace files", () => { // Given - const packed = { - files: [{ path: "package/dist/cli.js" }, { path: "package/CHANGELOG.md" }], - }; + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/CHANGELOG.md" }] }; // When / Then assert.throws( @@ -25,9 +32,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { it("rejects a packed tarball that omits a declared runtime dependency", () => { // Given: workspace bundles are present, but the cross-spawn registry dep is not vendored. const hostPrebuild = nativePrebuildFile(nativePrebuildTarget()); - const packed = { - files: [ - { path: "package/dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-ai/package.json" }, @@ -41,9 +46,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "package/node_modules/@code-yeongyu/senpi-codemode/package.json" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" }, - { path: "package/node_modules/which/package.json" }, - ], - }; + { path: "package/node_modules/which/package.json" },] }; // When / Then assert.throws( @@ -55,9 +58,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { it("accepts a packed tarball whose declared runtime dependencies are all vendored", () => { // Given const hostPrebuild = nativePrebuildFile(nativePrebuildTarget()); - const packed = { - files: [ - { path: "package/dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-ai/package.json" }, @@ -72,9 +73,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" }, { path: "package/node_modules/cross-spawn/package.json" }, - { path: "package/node_modules/@modelcontextprotocol/sdk/package.json" }, - ], - }; + { path: "package/node_modules/@modelcontextprotocol/sdk/package.json" },] }; // When / Then assert.doesNotThrow(() => @@ -87,9 +86,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { // locked tree and never installs the non-bundled direct deps (cross-spawn, the // MCP sdk, ...), so the installed CLI dies with ERR_MODULE_NOT_FOUND. const hostPrebuild = nativePrebuildFile(nativePrebuildTarget()); - const packed = { - files: [ - { path: "package/dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/npm-shrinkwrap.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, @@ -103,9 +100,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "package/node_modules/@earendil-works/pi-tui/dist/index.js" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/package.json" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts" }, - { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" }, - ], - }; + { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" },] }; // When / Then assert.throws( @@ -117,9 +112,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { it("accepts senpi package metadata that includes bundled workspace entrypoints", () => { // Given const hostPrebuild = nativePrebuildFile(nativePrebuildTarget()); - const packed = { - files: [ - { path: "package/dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-ai/package.json" }, @@ -132,9 +125,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "package/node_modules/@earendil-works/pi-tui/dist/index.js" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/package.json" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts" }, - { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" }, - ], - }; + { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" },] }; // When / Then assert.doesNotThrow(() => assertSenpiPackedWorkspaceFiles(packed)); @@ -143,9 +134,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { it("accepts npm dry-run package metadata with unprefixed paths", () => { // Given const hostPrebuild = nativePrebuildFile(nativePrebuildTarget()); - const packed = { - files: [ - { path: "dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "dist/cli.js" }, { path: "node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "node_modules/@earendil-works/pi-ai/package.json" }, @@ -158,9 +147,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "node_modules/@earendil-works/pi-tui/dist/index.js" }, { path: "node_modules/@code-yeongyu/senpi-codemode/package.json" }, { path: "node_modules/@code-yeongyu/senpi-codemode/src/index.ts" }, - { path: "node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" }, - ], - }; + { path: "node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" },] }; // When / Then assert.doesNotThrow(() => assertSenpiPackedWorkspaceFiles(packed)); @@ -168,9 +155,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { it("rejects senpi package metadata that omits the bundled pty native loader", () => { // Given - const packed = { - files: [ - { path: "package/dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-ai/package.json" }, @@ -178,9 +163,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "package/node_modules/@earendil-works/pi-pty/package.json" }, { path: "package/node_modules/@earendil-works/pi-pty/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-tui/package.json" }, - { path: "package/node_modules/@earendil-works/pi-tui/dist/index.js" }, - ], - }; + { path: "package/node_modules/@earendil-works/pi-tui/dist/index.js" },] }; // When / Then assert.throws( @@ -191,9 +174,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { it("accepts senpi package metadata that omits the host pty prebuild (pipe fallback)", () => { // Given: all loader files present, but no host native prebuild. - const packed = { - files: [ - { path: "package/dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-ai/package.json" }, @@ -205,9 +186,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "package/node_modules/@earendil-works/pi-tui/dist/index.js" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/package.json" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts" }, - { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" }, - ], - }; + { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" },] }; // When / Then: the native prebuild is optional (pipe fallback), so this must not throw. const originalWarn = console.warn; @@ -222,9 +201,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { it("accepts an all-OS check when a target's prebuild is absent (pipe fallback)", () => { // Given: the darwin-arm64 prebuild is present but linux-x64 is not. const missingTarget = "linux-x64"; - const packed = { - files: [ - { path: "package/dist/cli.js" }, + const packed = { files: [...clientProtocolFiles(), { path: "package/dist/cli.js" }, { path: "package/node_modules/@earendil-works/pi-agent-core/package.json" }, { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-ai/package.json" }, @@ -237,9 +214,7 @@ describe("assertSenpiPackedWorkspaceFiles", () => { { path: "package/node_modules/@earendil-works/pi-tui/dist/index.js" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/package.json" }, { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts" }, - { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" }, - ], - }; + { path: "package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py" },] }; // When / Then: a missing per-target prebuild is optional, so the check must not throw. assert.ok(SUPPORTED_NATIVE_PREBUILD_TARGETS.includes(missingTarget)); @@ -254,6 +229,13 @@ describe("assertSenpiPackedWorkspaceFiles", () => { } }); + it("includes client and protocol workspaces required by the coding agent", () => { + const packageNames = new Set(bundledWorkspacePackageChecks().map((check) => check.packageName)); + + assert.ok(packageNames.has("@earendil-works/pi-client")); + assert.ok(packageNames.has("@earendil-works/pi-protocol")); + }); + it("publishes the supported native target list through package checks", () => { // When const checks = bundledWorkspacePackageChecks(SUPPORTED_NATIVE_PREBUILD_TARGETS); diff --git a/scripts/prepare-senpi-bundled-workspaces.mjs b/scripts/prepare-senpi-bundled-workspaces.mjs index 0e1439736..112cc31bb 100644 --- a/scripts/prepare-senpi-bundled-workspaces.mjs +++ b/scripts/prepare-senpi-bundled-workspaces.mjs @@ -38,6 +38,8 @@ export function nativePrebuildFile(target) { const bundledWorkspaces = [ { source: "packages/agent", packageName: "@earendil-works/pi-agent-core", targetParts: ["@earendil-works", "pi-agent-core"], sourceOnly: false }, { source: "packages/ai", packageName: "@earendil-works/pi-ai", targetParts: ["@earendil-works", "pi-ai"], sourceOnly: false }, + { source: "packages/protocol", packageName: "@earendil-works/pi-protocol", targetParts: ["@earendil-works", "pi-protocol"], sourceOnly: false }, + { source: "packages/client", packageName: "@earendil-works/pi-client", targetParts: ["@earendil-works", "pi-client"], sourceOnly: false }, { source: "packages/pty", packageName: "@earendil-works/pi-pty", @@ -99,30 +101,53 @@ export function directNodeModulesPackageName(lockPath) { return parts.length === 1 ? parts[0] : undefined; } +function installedPackageVersion(packagePath) { + const manifestPath = join(packagePath, "package.json"); + if (!existsSync(manifestPath)) { + return undefined; + } + return JSON.parse(readFileSync(manifestPath, "utf8")).version; +} + export function copyPublishDependencies(repoRoot) { // Staging manifest for the bundled publish tree. NOT npm-shrinkwrap.json: shipping a // file named npm-shrinkwrap.json breaks bundleDependencies installs (see the guard in // assertSenpiPackedWorkspaceFiles). Generated by generate-coding-agent-shrinkwrap.mjs. const manifestPath = join(repoRoot, "packages/coding-agent/publish-deps.lock.json"); const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); - const rootNodeModules = join(repoRoot, "node_modules"); const codingAgentNodeModules = join(repoRoot, "packages/coding-agent/node_modules"); for (const [lockPath, entry] of Object.entries(manifest.packages ?? {}).sort(([a], [b]) => a.localeCompare(b))) { - const packageName = directNodeModulesPackageName(lockPath); - if (!packageName || internalPackageNames.has(packageName)) { + if (!lockPath.startsWith("node_modules/")) { + continue; + } + const directPackageName = directNodeModulesPackageName(lockPath); + if (directPackageName && internalPackageNames.has(directPackageName)) { continue; } - const sourcePath = join(rootNodeModules, packageName); - if (!existsSync(sourcePath)) { + const targetPath = join(repoRoot, "packages/coding-agent", lockPath); + const sourceCandidates = [targetPath]; + for (const workspace of bundledWorkspaces) { + const workspacePrefix = `node_modules/${workspace.packageName}`; + if (lockPath.startsWith(`${workspacePrefix}/node_modules/`)) { + sourceCandidates.push(join(repoRoot, workspace.source, lockPath.slice(workspacePrefix.length + 1))); + } + } + sourceCandidates.push(join(repoRoot, lockPath)); + const sourcePath = sourceCandidates.find((candidate) => installedPackageVersion(candidate) === entry.version); + if (!sourcePath) { if (entry && typeof entry === "object" && entry.optional === true) { continue; } - throw new Error(`Missing ${sourcePath}. Run npm install before publishing.`); + throw new Error( + `Missing ${lockPath}@${entry.version} from ${sourceCandidates.join(", ")}. Run npm install before publishing.`, + ); } - const targetPath = join(codingAgentNodeModules, packageName); + if (sourcePath === targetPath) { + continue; + } rmSync(targetPath, { recursive: true, force: true }); mkdirSync(dirname(targetPath), { recursive: true }); cpSync(sourcePath, targetPath, { recursive: true }); @@ -186,7 +211,6 @@ export function assertSenpiPackedWorkspaceFiles(packed, options = {}) { } export function prepareSenpiBundledWorkspaces(repoRoot = root) { - copyPublishDependencies(repoRoot); const codingAgentNodeModules = join(repoRoot, "packages/coding-agent/node_modules"); for (const workspace of bundledWorkspaces) { @@ -226,9 +250,10 @@ export function prepareSenpiBundledWorkspaces(repoRoot = root) { filter: (sourcePath) => shouldCopyWorkspaceFile(sourceRoot, sourcePath, workspace.sourceOnly), }); } + copyPublishDependencies(repoRoot); // Rewrite the publish manifest LAST: bundleDependencies must mirror the staged - // node_modules exactly (all registry runtime deps + the 5 workspace packages), so + // node_modules exactly (all registry runtime deps + the bundled workspaces), so // npm pack vendors the complete runtime closure into the tarball. This dirties // packages/coding-agent/package.json in the working tree; restore it with // `git checkout -- packages/coding-agent/package.json` after packing/publishing. diff --git a/scripts/prepare-senpi-bundled-workspaces.prepare.test.mjs b/scripts/prepare-senpi-bundled-workspaces.prepare.test.mjs index 65a71e796..6aa78dad6 100644 --- a/scripts/prepare-senpi-bundled-workspaces.prepare.test.mjs +++ b/scripts/prepare-senpi-bundled-workspaces.prepare.test.mjs @@ -36,6 +36,8 @@ function writeShrinkwrap(root, packages) { const BUNDLED_WORKSPACE_NAMES = [ "@earendil-works/pi-agent-core", "@earendil-works/pi-ai", + "@earendil-works/pi-protocol", + "@earendil-works/pi-client", "@earendil-works/pi-pty", "@earendil-works/pi-tui", "@code-yeongyu/senpi-codemode", @@ -82,7 +84,7 @@ describe("prepareSenpiBundledWorkspaces", () => { tempDir = mkdtempSync(join(tmpdir(), "senpi-bundle-workspaces-")); writeShrinkwrap(tempDir, { "": { dependencies: {} } }); writeCodingAgentManifest(tempDir); - for (const workspace of ["agent", "ai", "pty", "tui", "senpi-codemode"]) { + for (const workspace of ["agent", "ai", "protocol", "client", "pty", "tui", "senpi-codemode"]) { writeBundledWorkspace(tempDir, workspace); } @@ -112,7 +114,7 @@ describe("prepareSenpiBundledWorkspaces", () => { tempDir = mkdtempSync(join(tmpdir(), "senpi-bundle-missing-pty-prebuild-")); writeShrinkwrap(tempDir, { "": { dependencies: {} } }); writeCodingAgentManifest(tempDir); - for (const workspace of ["agent", "ai", "tui", "senpi-codemode"]) { + for (const workspace of ["agent", "ai", "protocol", "client", "tui", "senpi-codemode"]) { writeBundledWorkspace(tempDir, workspace); } writeBundledWorkspace(tempDir, "pty"); @@ -154,11 +156,14 @@ describe("prepareSenpiBundledWorkspaces", () => { "node_modules/which": { version: "2.0.2" }, }); writeCodingAgentManifest(tempDir); - for (const workspace of ["agent", "ai", "pty", "tui", "senpi-codemode"]) { + for (const workspace of ["agent", "ai", "protocol", "client", "pty", "tui", "senpi-codemode"]) { writeBundledWorkspace(tempDir, workspace); } - for (const name of ["cross-spawn", "which"]) { - writeJson(join(tempDir, "node_modules", name, "package.json"), { name, version: "1.0.0" }); + for (const [name, version] of [ + ["cross-spawn", "7.0.6"], + ["which", "2.0.2"], + ]) { + writeJson(join(tempDir, "node_modules", name, "package.json"), { name, version }); } // When @@ -184,6 +189,8 @@ describe("prepareSenpiBundledWorkspaces", () => { "@code-yeongyu/senpi-codemode": "^2026.7.22", "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@^2026.7.22", "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@^2026.7.22", + "@earendil-works/pi-client": "^2026.7.22", + "@earendil-works/pi-protocol": "^2026.7.22", "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@^2026.7.22", "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@^2026.7.22", }); @@ -194,7 +201,7 @@ describe("prepareSenpiBundledWorkspaces", () => { tempDir = mkdtempSync(join(tmpdir(), "senpi-bundle-missing-pty-loader-")); writeShrinkwrap(tempDir, { "": { dependencies: {} } }); writeCodingAgentManifest(tempDir); - for (const workspace of ["agent", "ai", "tui"]) { + for (const workspace of ["agent", "ai", "protocol", "client", "tui"]) { writeBundledWorkspace(tempDir, workspace); } writeBundledWorkspace(tempDir, "pty"); diff --git a/scripts/prepare-senpi-bundled-workspaces.test.mjs b/scripts/prepare-senpi-bundled-workspaces.test.mjs index 29176e8bd..59e0f4c05 100644 --- a/scripts/prepare-senpi-bundled-workspaces.test.mjs +++ b/scripts/prepare-senpi-bundled-workspaces.test.mjs @@ -158,6 +158,62 @@ describe("stagePublishManifest", () => { return JSON.parse(readFileSync(join(root, "packages", "coding-agent", "package.json"), "utf8")); } + it("removes only the incompatible Anthropic SDK peer from Agent SDK 0.3.220", () => { + // Given + tempDir = mkdtempSync(join(tmpdir(), "senpi-stage-agent-sdk-peer-")); + writeCodingAgentManifest(tempDir, { + dependencies: { + "@anthropic-ai/claude-agent-sdk": "0.3.220", + "@anthropic-ai/sdk": "0.91.1", + "@earendil-works/pi-ai": "^2026.7.22", + "@modelcontextprotocol/sdk": "1.30.0", + "cross-spawn": "7.0.6", + zod: "4.4.3", + }, + }); + for (const packageName of [ + "@anthropic-ai/sdk", + "@earendil-works/pi-ai", + "@modelcontextprotocol/sdk", + "@mariozechner/clipboard", + "cross-spawn", + "zod", + ]) { + stagePackage(tempDir, packageName); + } + stagePackage(tempDir, "@anthropic-ai/claude-agent-sdk", { + version: "0.3.220", + peerDependencies: { + "@anthropic-ai/sdk": ">=0.93.0", + "@modelcontextprotocol/sdk": "^1.29.0", + zod: "^4.0.0", + }, + }); + + // When + stagePublishManifest(tempDir); + + // Then + const stagedAgentSdk = JSON.parse( + readFileSync( + join( + tempDir, + "packages", + "coding-agent", + "node_modules", + "@anthropic-ai", + "claude-agent-sdk", + "package.json", + ), + "utf8", + ), + ); + assert.deepEqual(stagedAgentSdk.peerDependencies, { + "@modelcontextprotocol/sdk": "^1.29.0", + zod: "^4.0.0", + }); + }); + it("lists every staged runtime dependency and transitive in bundleDependencies", () => { // Given: cross-spawn's transitive dep `which` is staged but only reachable via an edge. tempDir = mkdtempSync(join(tmpdir(), "senpi-stage-manifest-")); diff --git a/scripts/prepare-senpi-publish-manifest.mjs b/scripts/prepare-senpi-publish-manifest.mjs index 9e0e6456d..42a4eb9d0 100644 --- a/scripts/prepare-senpi-publish-manifest.mjs +++ b/scripts/prepare-senpi-publish-manifest.mjs @@ -24,6 +24,37 @@ export function rewriteOwnedRegistryAliases(manifest) { return manifest; } +function normalizeStagedClaudeAgentSdk(codingAgentNodeModules) { + const manifestPath = join( + codingAgentNodeModules, + "@anthropic-ai", + "claude-agent-sdk", + "package.json", + ); + if (!existsSync(manifestPath)) { + return; + } + + const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); + if (manifest.version !== "0.3.220") { + return; + } + + // Senpi must retain sdk@0.91.1 for its browser bundle. Agent SDK 0.3.220 + // runs against that API but advertises >=0.93.0, so waive only this reviewed + // staged peer; later Agent SDK releases must pass packaging without the waiver. + if (manifest.peerDependencies?.["@anthropic-ai/sdk"] !== undefined) { + delete manifest.peerDependencies["@anthropic-ai/sdk"]; + } + if (manifest.peerDependenciesMeta?.["@anthropic-ai/sdk"] !== undefined) { + delete manifest.peerDependenciesMeta["@anthropic-ai/sdk"]; + if (Object.keys(manifest.peerDependenciesMeta).length === 0) { + delete manifest.peerDependenciesMeta; + } + } + writeFileSync(manifestPath, `${JSON.stringify(manifest, null, "\t")}\n`); +} + export function listStagedPublishPackageNames(codingAgentNodeModules) { const packageNames = []; for (const entry of readdirSync(codingAgentNodeModules, { withFileTypes: true })) { @@ -106,12 +137,12 @@ function promotePlatformOptionalDependencyFamilies(codingAgentNodeModules, packa } // The publish tarball must be fully self-contained: every runtime dependency edge in -// the coding-agent manifest (registry deps AND the 5 vendored workspace packages) is +// the coding-agent manifest (registry deps and vendored workspace packages) is // staged into packages/coding-agent/node_modules, and bundleDependencies lists every // portable staged package (platform-constrained natives are excluded — see // isPlatformConstrainedPackage). npm then needs no registry fetch for anything that // installs identically on every platform at install time. The historical -// partial bundle (only the 5 workspace packages + their closure) forced npm to fetch +// partial bundle (only the original workspace subset + its closure) forced npm to fetch // the other runtime deps from the registry, where arborist nondeterministically tried // to resolve the registry-absent `^2026.x` workspace specs (ETARGET) and aborted reify // mid-flight, leaving arbitrary deps (cross-spawn, which, @modelcontextprotocol/sdk) @@ -144,6 +175,7 @@ export function stagePublishManifest(repoRoot) { ); } + normalizeStagedClaudeAgentSdk(codingAgentNodeModules); const bundlablePackageNames = bundlablePublishPackageNames(codingAgentNodeModules, stagedPackageNames); manifest.optionalDependencies = { ...promotePlatformOptionalDependencyFamilies(codingAgentNodeModules, stagedPackageNames), diff --git a/scripts/prepare-senpi-publish-optionals.test.mjs b/scripts/prepare-senpi-publish-optionals.test.mjs index 4aa58cc75..3f1c076ec 100644 --- a/scripts/prepare-senpi-publish-optionals.test.mjs +++ b/scripts/prepare-senpi-publish-optionals.test.mjs @@ -17,6 +17,10 @@ it("accepts consumer-resolved platform optionals outside the packed bundle", () { path: "package/node_modules/@earendil-works/pi-agent-core/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-ai/package.json" }, { path: "package/node_modules/@earendil-works/pi-ai/dist/index.js" }, + { path: "package/node_modules/@earendil-works/pi-protocol/package.json" }, + { path: "package/node_modules/@earendil-works/pi-protocol/dist/index.js" }, + { path: "package/node_modules/@earendil-works/pi-client/package.json" }, + { path: "package/node_modules/@earendil-works/pi-client/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-pty/package.json" }, { path: "package/node_modules/@earendil-works/pi-pty/dist/index.js" }, { path: "package/node_modules/@earendil-works/pi-pty/native/index.js" }, diff --git a/scripts/publish-registry-dependencies.test.mjs b/scripts/publish-registry-dependencies.test.mjs index 4ae47eb0c..34156fa72 100644 --- a/scripts/publish-registry-dependencies.test.mjs +++ b/scripts/publish-registry-dependencies.test.mjs @@ -20,7 +20,6 @@ const OWNED_REGISTRY_ALIASES = [ "@code-yeongyu/senpi-codemode", "@code-yeongyu/senpi", ]; - function readJson(path) { return JSON.parse(readFileSync(path, "utf8")); } @@ -39,4 +38,14 @@ describe("npm publish dependency graph", () => { assert.match(publishScript, new RegExp(`name: "${packageName}"`)); } }); + + it("stages the coding-agent TypeBox version in the publish lock", () => { + const manifest = readJson(join(repoRoot, "packages", "coding-agent", "package.json")); + const publishLock = readJson(join(repoRoot, "packages", "coding-agent", "publish-deps.lock.json")); + assert.equal( + publishLock.packages["node_modules/typebox"]?.version, + manifest.dependencies.typebox, + "the staged TypeBox package must match the coding-agent manifest", + ); + }); });