diff --git a/.changeset/global-provider-policy.md b/.changeset/global-provider-policy.md
index 17c6325..404e8d0 100644
--- a/.changeset/global-provider-policy.md
+++ b/.changeset/global-provider-policy.md
@@ -2,6 +2,6 @@
"@jmfederico/pi-web": patch
---
-Require Pi Coding Agent `>=0.81.1 <0.82` and build an immutable provider baseline at session-daemon startup. Globally installed Pi extensions can register both config-form and native providers during startup bootstrap; every later Pi extension registration or unregistration—including global replay, project same-ID replacement, lifecycle callbacks, and `/reload`—is ignored. PI WEB browser plugins are a separate browser-only system and are unaffected. Non-provider Pi extension features still work, and ignored calls are de-duplicated in session-daemon logs by operation/provider ID without logging provider configuration or credentials or creating session warnings/notifications.
+Require Pi Coding Agent `>=0.82.1 <0.83` and build an immutable provider baseline at session-daemon startup. Globally installed Pi extensions can register both config-form and native providers during startup bootstrap; every later Pi extension registration or unregistration—including global replay, project same-ID replacement, lifecycle callbacks, and `/reload`—is ignored. PI WEB browser plugins are a separate browser-only system and are unaffected. Non-provider Pi extension features still work, and ignored calls are de-duplicated in session-daemon logs by operation/provider ID without logging provider configuration or credentials or creating session warnings/notifications.
After updating PI WEB, or after installing, removing, or updating a globally installed Pi extension that registers providers, manually restart `pi-web-sessiond.service` (`systemctl --user restart pi-web-sessiond`). Restarting only the web/API service and running `/reload` do not rebuild the provider baseline.
diff --git a/.changeset/require-pi-0-82.md b/.changeset/require-pi-0-82.md
new file mode 100644
index 0000000..ddbca8b
--- /dev/null
+++ b/.changeset/require-pi-0-82.md
@@ -0,0 +1,5 @@
+---
+"@jmfederico/pi-web": patch
+---
+
+Require Pi Coding Agent `>=0.82.1 <0.83`. PI WEB no longer supports Pi 0.81 and earlier, so update Pi before updating PI WEB. On Pi 0.82 provider model catalogs revalidate with the server instead of downloading in full when nothing changed, and newly published catalog updates are no longer suppressed for a while after a fresh install.
diff --git a/README.md b/README.md
index 0127950..314c248 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ Requirements:
- Node.js 22.19.0 or newer
- npm
-- Pi Coding Agent `>=0.81.1 <0.82`, configured for your user
+- Pi Coding Agent `>=0.82.1 <0.83`, configured for your user
- git and the development tools your agents need
Install and start PI WEB as per-user services:
diff --git a/docs/config.html b/docs/config.html
index 2c327da..b068eab 100644
--- a/docs/config.html
+++ b/docs/config.html
@@ -675,10 +675,11 @@
- Each run is bounded: it is aborted after 60 seconds, and a run that times out or fails
- earns one retry after five minutes. Failures never clear the stored catalogs — the last
- successfully fetched models stay in use and the daemon log records what failed. A refresh in flight is
- also aborted when the daemon shuts down.
+ Each run is bounded: it is aborted after 60 seconds, and a run that times out or cannot
+ reach a provider earns one retry after five minutes; a provider that answers with an
+ error status is retried on the next scheduled refresh instead. Failures never clear the stored catalogs
+ — the last successfully fetched models stay in use and the daemon log records what failed. A refresh in
+ flight is also aborted when the daemon shuts down.
Models fetched by a background refresh appear the next time a client asks for the model list, so a model
diff --git a/docs/config.md b/docs/config.md
index efc68d1..7535243 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -228,7 +228,7 @@ The session daemon runs the refresh:
- **15 seconds after the daemon starts**, then **hourly**. Pi treats stored catalogs as fresh for four hours, so most hourly ticks make no network request at all; the shorter tick only makes sure a due refresh is not delayed to the next tick.
- **Immediately after a provider login or logout**, bypassing that freshness window, because the cached catalog is known to be wrong.
-Each run is bounded: it is aborted after **60 seconds**, and a run that times out or fails earns **one retry after five minutes**. Failures never clear the stored catalogs — the last successfully fetched models stay in use and the daemon log records what failed. A refresh in flight is also aborted when the daemon shuts down.
+Each run is bounded: it is aborted after **60 seconds**, and a run that times out or cannot reach a provider earns **one retry after five minutes**; a provider that answers with an error status is retried on the next scheduled refresh instead. Failures never clear the stored catalogs — the last successfully fetched models stay in use and the daemon log records what failed. A refresh in flight is also aborted when the daemon shuts down.
Models fetched by a background refresh appear the next time a client asks for the model list, so a model selector left open across a refresh may need to be reopened.
diff --git a/docs/index.html b/docs/index.html
index 5debb5f..bd5b502 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -38,7 +38,7 @@
"downloadUrl": "https://www.npmjs.com/package/@jmfederico/pi-web",
"codeRepository": "https://github.com/jmfederico/pi-web",
"description": "PI WEB is a web UI for Pi Coding Agent that keeps persistent agent sessions running in real workspaces on your machine or server.",
- "softwareRequirements": "Node.js 22.19.0 or newer and Pi Coding Agent >=0.81.1 <0.82",
+ "softwareRequirements": "Node.js 22.19.0 or newer and Pi Coding Agent >=0.82.1 <0.83",
"license": "https://github.com/jmfederico/pi-web/blob/main/LICENSE"
}
diff --git a/docs/install.html b/docs/install.html
index 9373721..cc6ce3e 100644
--- a/docs/install.html
+++ b/docs/install.html
@@ -107,7 +107,7 @@
Requirements
- Node.js 22.19.0 or newer and npm.
- - Pi Coding Agent
>=0.81.1 <0.82 installed/configured so the pi command works for your user.
+ - Pi Coding Agent
>=0.82.1 <0.83 installed/configured so the pi command works for your user.
- A shell login environment that exposes Node, npm, Pi, git, and any tools your agents need.
- For the automatic installer: a supported per-user service manager.
diff --git a/package-lock.json b/package-lock.json
index c8b6487..1d19fc9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,9 +42,9 @@
},
"devDependencies": {
"@changesets/cli": "^2.31.0",
- "@earendil-works/pi-agent-core": "^0.81.1",
- "@earendil-works/pi-ai": "^0.81.1",
- "@earendil-works/pi-coding-agent": "^0.81.1",
+ "@earendil-works/pi-agent-core": "^0.82.1",
+ "@earendil-works/pi-ai": "^0.82.1",
+ "@earendil-works/pi-coding-agent": "^0.82.1",
"@eslint/js": "^10.0.1",
"@types/node": "^24.13.3",
"@types/ws": "^8.18.1",
@@ -61,9 +61,9 @@
"node": ">=22.19.0"
},
"peerDependencies": {
- "@earendil-works/pi-agent-core": ">=0.81.1 <0.82",
- "@earendil-works/pi-ai": ">=0.81.1 <0.82",
- "@earendil-works/pi-coding-agent": ">=0.81.1 <0.82"
+ "@earendil-works/pi-agent-core": ">=0.82.1 <0.83",
+ "@earendil-works/pi-ai": ">=0.82.1 <0.83",
+ "@earendil-works/pi-coding-agent": ">=0.82.1 <0.83"
}
},
"node_modules/@anthropic-ai/sdk": {
@@ -167,17 +167,17 @@
}
},
"node_modules/@aws-sdk/core": {
- "version": "3.976.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.976.0.tgz",
- "integrity": "sha512-0cjRaEdlVoOrsNb9pP5q1Syyc8pXw5xSj2Np2ryReRTr9FppIIRVSdZK4lbnfmc2Hvgux/xBOUU6baB7z8//uA==",
+ "version": "3.977.1",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.1.tgz",
+ "integrity": "sha512-KVtQRtc00ES/y+Sc3vYXeP6pCIcNlBJCZOwvqSy8ZpVGmbM5+IG+AfhuTKQ2oXmIVqZJewaGMMpzPkywC6xg0w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.974.2",
- "@aws-sdk/xml-builder": "^3.972.36",
+ "@aws-sdk/xml-builder": "^3.972.37",
"@aws/lambda-invoke-store": "^0.3.0",
- "@smithy/core": "^3.29.4",
- "@smithy/signature-v4": "^5.6.5",
+ "@smithy/core": "^3.29.8",
+ "@smithy/signature-v4": "^5.6.9",
"@smithy/types": "^4.16.1",
"bowser": "^2.11.0",
"tslib": "^2.6.2"
@@ -187,15 +187,15 @@
}
},
"node_modules/@aws-sdk/credential-provider-env": {
- "version": "3.972.60",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.60.tgz",
- "integrity": "sha512-BAkxdoe7tpDDqCghGpuOeHQRbm/2znVvOQm0AvpQbA2tbfMN46doN4zx65fv85ImP3KADwc2zQPmbrlI9MPfMg==",
+ "version": "3.972.61",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.61.tgz",
+ "integrity": "sha512-qihs2ekMb89Nxd2JenCgVFhjbkb3EIo7HEBCBzyZACKVJdrLUZBLOmAE3xr0Sayml8n/jZSzwO/IufIiIzO7PQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
+ "@aws-sdk/core": "^3.977.0",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -204,17 +204,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-http": {
- "version": "3.972.62",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.62.tgz",
- "integrity": "sha512-g/0fGqKTb9xpKdd9AtpmV5Eo3DFKbnkpA2+w0peISSlu7NfAoWOuYBFxsu+yWBtxU89ka55ezoZBCbFaS8pjYQ==",
+ "version": "3.972.63",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.63.tgz",
+ "integrity": "sha512-yfozsS8wkWZEi/n6IsrodcFKBWZ0iNAezhJbTReMNc0z1Px17qdeAeuL1/wziCAmCZyXiW7QzP75ggJkBQv8jQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
+ "@aws-sdk/core": "^3.977.0",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
- "@smithy/fetch-http-handler": "^5.6.6",
- "@smithy/node-http-handler": "^4.9.6",
+ "@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"
},
@@ -223,13 +223,13 @@
}
},
"node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": {
- "version": "4.9.9",
- "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.9.tgz",
- "integrity": "sha512-xVBZ3hptB99iNO9XyWqEhC7KD9bP9UPXhuy3h5Y2ItCfBv160D9IIC/Fmmp3EbnWwit4C+KVqlSE+E29Nk/pPg==",
+ "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==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.29.7",
+ "@smithy/core": "^3.30.0",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -238,23 +238,23 @@
}
},
"node_modules/@aws-sdk/credential-provider-ini": {
- "version": "3.973.5",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.5.tgz",
- "integrity": "sha512-ylubazcRfq2TVus/qXucSXeC42Qdjp5HQxTu68K/BsdMiZlcSLD1zkpoCgApXZX1Y6YJhtGGs7ZHhO/GuIgBlw==",
+ "version": "3.973.6",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.6.tgz",
+ "integrity": "sha512-jGLTW1bj148GL/6/IMlfY2fMYS9FtHOG+NahkFD4y0qkzYudNUahelxryY68/HGMslYuHClk1XaS/3b3eJzEkg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
- "@aws-sdk/credential-provider-env": "^3.972.60",
- "@aws-sdk/credential-provider-http": "^3.972.62",
- "@aws-sdk/credential-provider-login": "^3.972.67",
- "@aws-sdk/credential-provider-process": "^3.972.60",
- "@aws-sdk/credential-provider-sso": "^3.973.4",
- "@aws-sdk/credential-provider-web-identity": "^3.972.66",
- "@aws-sdk/nested-clients": "^3.997.34",
+ "@aws-sdk/core": "^3.977.0",
+ "@aws-sdk/credential-provider-env": "^3.972.61",
+ "@aws-sdk/credential-provider-http": "^3.972.63",
+ "@aws-sdk/credential-provider-login": "^3.972.68",
+ "@aws-sdk/credential-provider-process": "^3.972.61",
+ "@aws-sdk/credential-provider-sso": "^3.973.5",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.67",
+ "@aws-sdk/nested-clients": "^3.997.35",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
- "@smithy/credential-provider-imds": "^4.4.9",
+ "@smithy/core": "^3.29.8",
+ "@smithy/credential-provider-imds": "^4.4.13",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -263,16 +263,16 @@
}
},
"node_modules/@aws-sdk/credential-provider-login": {
- "version": "3.972.67",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.67.tgz",
- "integrity": "sha512-CCygIKJ9YbI3n84OClSaSppkgKKHVj2TGT33c6FRORZrYNZQ1POmD+ip0FLYokiJAK7sSdc3YVkOsBm90oxWMQ==",
+ "version": "3.972.68",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.68.tgz",
+ "integrity": "sha512-w6tNci6g7RqFpLhj1f5xseBvaNojb4Pkgp5Jp5apl9hrJtaf2AA+rX9+qlhlWUK6kcyAFYPA7emO+55zj+S98Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
- "@aws-sdk/nested-clients": "^3.997.34",
+ "@aws-sdk/core": "^3.977.0",
+ "@aws-sdk/nested-clients": "^3.997.35",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -281,21 +281,21 @@
}
},
"node_modules/@aws-sdk/credential-provider-node": {
- "version": "3.972.71",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.71.tgz",
- "integrity": "sha512-HIg7Q2osBzajQwL+1Vkyh2E7Gim3eTNb9RHIsOxDGjW0eZg4oEKtRs5sioCnc73ilhaOm4gX2lHVF8J7+nt2rg==",
+ "version": "3.972.72",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.72.tgz",
+ "integrity": "sha512-blQ7F5QGzylnzeh5549zQLoCAiMHkXFLjFovEMaVy4b2X8JhUu+u9NXro1hyK95YHdVFNmBHKs2hIHtZchxKlQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/credential-provider-env": "^3.972.60",
- "@aws-sdk/credential-provider-http": "^3.972.62",
- "@aws-sdk/credential-provider-ini": "^3.973.5",
- "@aws-sdk/credential-provider-process": "^3.972.60",
- "@aws-sdk/credential-provider-sso": "^3.973.4",
- "@aws-sdk/credential-provider-web-identity": "^3.972.66",
+ "@aws-sdk/credential-provider-env": "^3.972.61",
+ "@aws-sdk/credential-provider-http": "^3.972.63",
+ "@aws-sdk/credential-provider-ini": "^3.973.6",
+ "@aws-sdk/credential-provider-process": "^3.972.61",
+ "@aws-sdk/credential-provider-sso": "^3.973.5",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.67",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
- "@smithy/credential-provider-imds": "^4.4.9",
+ "@smithy/core": "^3.29.8",
+ "@smithy/credential-provider-imds": "^4.4.13",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -304,15 +304,15 @@
}
},
"node_modules/@aws-sdk/credential-provider-process": {
- "version": "3.972.60",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.60.tgz",
- "integrity": "sha512-YIo3f99hM43QdYG8hDzwGemnR/pU95b0kramqSJUTleCqaB7+HwKf7YZFHqvOgTqZTPx/mRmNIqoDRr3U0Z3Tw==",
+ "version": "3.972.61",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.61.tgz",
+ "integrity": "sha512-xzRuj+fUVO4nkafKQJVKAF97kGpeQbfjuwmRrtGZNf42/1dkmcz6o7dswBy7alY0htQn5sCL1GWQYEykviWZkA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
+ "@aws-sdk/core": "^3.977.0",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -321,17 +321,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-sso": {
- "version": "3.973.4",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.4.tgz",
- "integrity": "sha512-BPdmL8sSBOCv4ngZ+3LHxyc3CNqDCEK37CHioCk7zGrTMY5sUtkH8q+o6qA80nn6w3/fyBPGNE7OIRlmoOxRQA==",
+ "version": "3.973.5",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.5.tgz",
+ "integrity": "sha512-fZRjjWhLFelsDoOYjqShQTrIGYC3Pf9Mx9Czf+1ikfQDgktxjze33dVo1q1/ZQ+T0qbtejVoHNHrfD5aJVpv/w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
- "@aws-sdk/nested-clients": "^3.997.34",
- "@aws-sdk/token-providers": "3.1092.0",
+ "@aws-sdk/core": "^3.977.0",
+ "@aws-sdk/nested-clients": "^3.997.35",
+ "@aws-sdk/token-providers": "3.1095.0",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -340,16 +340,16 @@
}
},
"node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": {
- "version": "3.1092.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1092.0.tgz",
- "integrity": "sha512-hBYUAr6iBLNFcsiWTgtBb0stdSw39VOUq4Sp4A5caCNf66BAZplWN4FleKrVpJx5li2YgdnK2DqoFSMWC642FQ==",
+ "version": "3.1095.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1095.0.tgz",
+ "integrity": "sha512-65SudS6y4nzaYHybtqcpm3sHe5jLhdMn68HRKS1nUx690BtQeaAQOoujQ+dpOjBATIVGVgKKjEP8tR+U06QJQA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
- "@aws-sdk/nested-clients": "^3.997.34",
+ "@aws-sdk/core": "^3.977.0",
+ "@aws-sdk/nested-clients": "^3.997.35",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -358,16 +358,16 @@
}
},
"node_modules/@aws-sdk/credential-provider-web-identity": {
- "version": "3.972.66",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.66.tgz",
- "integrity": "sha512-kSAziJboOmZmsR9/MTbiNjowl2BPes1bQuJpne4qAZ62ubi8fjfr/aupJSQje6udBoYxXTQbsL0e0kby2la3ng==",
+ "version": "3.972.67",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.67.tgz",
+ "integrity": "sha512-FTNZ05gkPBA6CKbU3N4zPgybV+stdazwMOya75CmGdcJL7p8Fw/BdHP8WVxJd0mvzyPK2cg/C3gli58Ir4HgCw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
- "@aws-sdk/nested-clients": "^3.997.34",
+ "@aws-sdk/core": "^3.977.0",
+ "@aws-sdk/nested-clients": "^3.997.35",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -376,14 +376,14 @@
}
},
"node_modules/@aws-sdk/eventstream-handler-node": {
- "version": "3.972.29",
- "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.29.tgz",
- "integrity": "sha512-t3tKQRTVXsI2QNPE3CaNjHl0wRO9Xi3acZkAyti2RQsiFmZ9Gi0kArX2ighlRJ1BtDVuul413gThAgzyTfgmWA==",
+ "version": "3.972.30",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.30.tgz",
+ "integrity": "sha512-hJboPgIpq5+ADc++/B9TBqn65CXV21cZLGB8V5RBQbxkZ/rQ6qMfcxTnW/SvQlasX4jhaSG8B1wsVjhQyDrsnQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -392,14 +392,14 @@
}
},
"node_modules/@aws-sdk/middleware-eventstream": {
- "version": "3.972.24",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.24.tgz",
- "integrity": "sha512-oykin4mDWxNOuYQ7SF1cHzgYeuFEkF4cdRwgvjFFbIklkx09qIFBiOgsORafG9sXZFO3TayMmQuAQYgADXhI8w==",
+ "version": "3.972.25",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.25.tgz",
+ "integrity": "sha512-9SFbPzJDHHR5k6Q6KvXVas/veUm/TzNcNTFM2UhdXHZHpyIvI2lS+s4cxljw1BihGpVhsAkQDo/2nW7dHxpf4Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
+ "@smithy/core": "^3.29.8",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -408,17 +408,17 @@
}
},
"node_modules/@aws-sdk/middleware-websocket": {
- "version": "3.972.42",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.42.tgz",
- "integrity": "sha512-dw+GP8DC7QC2C8tUoK7DI8BnrNAjz8tb+uBHSrD2qJvxkCf58kTtFr98pljSrk+umU4n4HDW4eU2k7C2dWMzsg==",
+ "version": "3.972.43",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.43.tgz",
+ "integrity": "sha512-n29++15Vma64Kd0enp9Bo8a6LTm8TvUoMbJEwqXtIksv0oEs+SUCRMm3gozDfPD1Ly0k/sSBughxarlLlRF6Xw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
+ "@aws-sdk/core": "^3.977.0",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
- "@smithy/fetch-http-handler": "^5.6.6",
- "@smithy/signature-v4": "^5.6.5",
+ "@smithy/core": "^3.29.8",
+ "@smithy/fetch-http-handler": "^5.6.10",
+ "@smithy/signature-v4": "^5.6.9",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -427,18 +427,18 @@
}
},
"node_modules/@aws-sdk/nested-clients": {
- "version": "3.997.34",
- "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.34.tgz",
- "integrity": "sha512-Y9REVrSwmLM+Qy6sZJ7ofMC2S3Hr3tPP/4CzL5U1olPP7OGoF+6+Px0E49cVQBtSxJtyeLJMf0UaBErfeSahAA==",
+ "version": "3.997.35",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.35.tgz",
+ "integrity": "sha512-2MJfseVG/aXvIyOIBlYA/Oaf6qFDdsu4D8RKsEUdOQpVuLaor0BdxIBBtJLBNQQEe6Ku3YMvLljwb1MwVUpzRw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.976.0",
- "@aws-sdk/signature-v4-multi-region": "^3.996.41",
+ "@aws-sdk/core": "^3.977.0",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.42",
"@aws-sdk/types": "^3.974.2",
- "@smithy/core": "^3.29.4",
- "@smithy/fetch-http-handler": "^5.6.6",
- "@smithy/node-http-handler": "^4.9.6",
+ "@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"
},
@@ -447,13 +447,13 @@
}
},
"node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler": {
- "version": "4.9.9",
- "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.9.tgz",
- "integrity": "sha512-xVBZ3hptB99iNO9XyWqEhC7KD9bP9UPXhuy3h5Y2ItCfBv160D9IIC/Fmmp3EbnWwit4C+KVqlSE+E29Nk/pPg==",
+ "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==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.29.7",
+ "@smithy/core": "^3.30.0",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -462,14 +462,14 @@
}
},
"node_modules/@aws-sdk/signature-v4-multi-region": {
- "version": "3.996.41",
- "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.41.tgz",
- "integrity": "sha512-QMUytg+FQMGouc8gHS00KoYih3+N6cqmVI/pQGOIo7Nr7OpQaiXjSYOuL+vsPZ1tymY4LAQ8MYcHJmws5LRxng==",
+ "version": "3.996.42",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.42.tgz",
+ "integrity": "sha512-DBV4naZP6HYBlAvPpoQzOP12Wvfou/5rN8yJPXjBTBylU5qwCbh/tXr2MddHoIjgoRkEl/eS+IljiUqvmwey1Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.974.2",
- "@smithy/signature-v4": "^5.6.5",
+ "@smithy/signature-v4": "^5.6.9",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -523,9 +523,9 @@
}
},
"node_modules/@aws-sdk/xml-builder": {
- "version": "3.972.36",
- "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.36.tgz",
- "integrity": "sha512-RdGmS1GLrtaTOLE1ElSluMldNrpk9Emq6uYs8SS8iHlu5xTAmM9rRkM91o48+rIRryBtyO9t+uLYCoMG6jVMVA==",
+ "version": "3.972.37",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.37.tgz",
+ "integrity": "sha512-zKq4HQum8JwDyEuyfuI4bbiAcU0KxP6qy+9PR/IsR92IyE/DaBAikzAS50tjxip4bqIIANpCcG+Yyj6CVhXupg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -984,13 +984,14 @@
}
},
"node_modules/@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==",
+ "version": "0.82.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.82.1.tgz",
+ "integrity": "sha512-Z3kloziJIE2dmrisRckZX8zDca/gIv9/YdFAzeoqpHiLV2wsni6bL4hInNSjVKLbqT+4kqLIkph2JQLKvSepjg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@earendil-works/pi-ai": "^0.81.1",
+ "@earendil-works/pi-ai": "^0.82.1",
+ "diff": "8.0.4",
"ignore": "7.0.5",
"typebox": "1.1.38",
"yaml": "2.9.0"
@@ -999,6 +1000,16 @@
"node": ">=22.19.0"
}
},
+ "node_modules/@earendil-works/pi-agent-core/node_modules/diff": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz",
+ "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/@earendil-works/pi-agent-core/node_modules/typebox": {
"version": "1.1.38",
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz",
@@ -1007,9 +1018,9 @@
"license": "MIT"
},
"node_modules/@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==",
+ "version": "0.82.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.82.1.tgz",
+ "integrity": "sha512-3WFYRhEp3lQB3444EhPMBcM7zSaEUE3eJgHOR7s4081NLqbw/FsWilIKWXSua0Gv3sRr7m9xMidR3pPDE7jI/A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1040,16 +1051,16 @@
"license": "MIT"
},
"node_modules/@earendil-works/pi-coding-agent": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.81.1.tgz",
- "integrity": "sha512-r6ovAsZOgAqbC/aU6s+/dPnv/sGZBuWyZNvi3pXjpbuX5wvp3XvGkQI7/VLvX2o9XpmpFaPUxKNym1WfkN/P8A==",
+ "version": "0.82.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.82.1.tgz",
+ "integrity": "sha512-zbkAhoIuDPMF3pKuja0ajZabrMWU29FUMV9A/XMXT/XC1yXs5xt6t6t13GogQFsDrDqbFP4DkZQO1w8rWRAzYA==",
"dev": true,
"hasShrinkwrap": true,
"license": "MIT",
"dependencies": {
- "@earendil-works/pi-agent-core": "^0.81.1",
- "@earendil-works/pi-ai": "^0.81.1",
- "@earendil-works/pi-tui": "^0.81.1",
+ "@earendil-works/pi-agent-core": "^0.82.1",
+ "@earendil-works/pi-ai": "^0.82.1",
+ "@earendil-works/pi-tui": "^0.82.1",
"@silvia-odwyer/photon-node": "0.3.4",
"chalk": "5.6.2",
"cross-spawn": "7.0.6",
@@ -1539,12 +1550,13 @@
}
},
"node_modules/@earendil-works/pi-coding-agent/node_modules/@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",
+ "version": "0.82.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.82.1.tgz",
"dev": true,
"license": "MIT",
"dependencies": {
- "@earendil-works/pi-ai": "^0.81.1",
+ "@earendil-works/pi-ai": "^0.82.1",
+ "diff": "8.0.4",
"ignore": "7.0.5",
"typebox": "1.1.38",
"yaml": "2.9.0"
@@ -1554,8 +1566,8 @@
}
},
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.81.1.tgz",
+ "version": "0.82.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.82.1.tgz",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1579,8 +1591,8 @@
}
},
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.81.1.tgz",
+ "version": "0.82.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.82.1.tgz",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2749,9 +2761,9 @@
}
},
"node_modules/@earendil-works/pi-coding-agent/node_modules/protobufjs": {
- "version": "7.6.4",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz",
- "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==",
+ "version": "7.6.5",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz",
+ "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==",
"dev": true,
"hasInstallScript": true,
"license": "BSD-3-Clause",
@@ -5330,9 +5342,9 @@
"license": "MIT"
},
"node_modules/@smithy/core": {
- "version": "3.29.7",
- "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.7.tgz",
- "integrity": "sha512-BiEE2bnnGoPKdlGe3L+gOYORDHFGPuYVRLP7iUow/Sflm0B4hC4XY3FC1MRuc7ltzpW2xNnXopKi34TTkULlKQ==",
+ "version": "3.30.0",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.30.0.tgz",
+ "integrity": "sha512-dl2yRglDxfzH9uJ4fSo4zTaAHa0zH7+V7BZMRWy8hEYIKT1BiqMUK/CN6T3ADQ3kbA5N1tmUulroJ2UtONS7Kw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -5344,13 +5356,13 @@
}
},
"node_modules/@smithy/credential-provider-imds": {
- "version": "4.4.12",
- "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.12.tgz",
- "integrity": "sha512-ZZPDbl/aRp77aycuoMlo3BTayT4CE2a3uoqETYZU5ySnVbhpl5IJiY7dCZedn+ZusyDLqVv44IvKBiXd2/nK0Q==",
+ "version": "4.4.14",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.14.tgz",
+ "integrity": "sha512-QgbuahIb2qxQeZQvNK0sw3aF3JH5zwH8j2lLp5DUasVXexGGMWULAR+7z0omPXFolCP/m5wN9M5lm9EGdSviTQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.29.7",
+ "@smithy/core": "^3.30.0",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -5359,13 +5371,13 @@
}
},
"node_modules/@smithy/fetch-http-handler": {
- "version": "5.6.9",
- "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.9.tgz",
- "integrity": "sha512-EJktha5m5MXCwzdXrlWyqb9UCNHNFKlg+PmTpRsdX3dncJPTiqYleM9OKj2mLgdVJHR01d2tU4alG+z2NdH5rQ==",
+ "version": "5.6.11",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.11.tgz",
+ "integrity": "sha512-o0Zkj1nKqJAoq+a+BrkhU39tRftMNjLwpc/z06Frfl43wpbHrJMaSAVZE4vTqlxtVkNaGaT0bIDxOp7tkFTuQQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.29.7",
+ "@smithy/core": "^3.30.0",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -5402,13 +5414,13 @@
}
},
"node_modules/@smithy/signature-v4": {
- "version": "5.6.8",
- "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.8.tgz",
- "integrity": "sha512-iGBm6hIwD2MGvVRSgrjVWa4FXtXDq3akxu0DCpnkmBo0xtEHZ/siMRt7ycfZAefYr2UdywUgmGtoRLaq5u56pg==",
+ "version": "5.6.10",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.10.tgz",
+ "integrity": "sha512-EXhWePm3SXJAX38npIy4TXL2Aex/OVgCClTjelN2QHw/U+8CQUH8C7AaxsVzQcYieYPseywn48s89DmvuGjiAg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.29.7",
+ "@smithy/core": "^3.30.0",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
@@ -7214,9 +7226,9 @@
}
},
"node_modules/gaxios": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.2.0.tgz",
- "integrity": "sha512-CUVb4wcYe+771XevyH6HtGmXFAGGKkIC3kswAP8Z1JCe0j80JMaTPZH930DWFrvo0atjh18Arc0pEyUCWa5bfg==",
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.0.tgz",
+ "integrity": "sha512-RB5vLV+vvQeoFPCX4QMK6/hjVkbIamPp1QSUD0CiZcnj12qbpiL+pLbYtgD+oZkWl0tl9z+o2Utp+MpM3QRhBA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -7331,9 +7343,9 @@
}
},
"node_modules/google-auth-library": {
- "version": "10.9.0",
- "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.0.tgz",
- "integrity": "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg==",
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.1.tgz",
+ "integrity": "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
diff --git a/package.json b/package.json
index b4ac1aa..06fbfd2 100644
--- a/package.json
+++ b/package.json
@@ -83,9 +83,9 @@
},
"devDependencies": {
"@changesets/cli": "^2.31.0",
- "@earendil-works/pi-agent-core": "^0.81.1",
- "@earendil-works/pi-ai": "^0.81.1",
- "@earendil-works/pi-coding-agent": "^0.81.1",
+ "@earendil-works/pi-agent-core": "^0.82.1",
+ "@earendil-works/pi-ai": "^0.82.1",
+ "@earendil-works/pi-coding-agent": "^0.82.1",
"@eslint/js": "^10.0.1",
"@types/node": "^24.13.3",
"@types/ws": "^8.18.1",
@@ -114,9 +114,9 @@
"homepage": "https://pi-web.dev/",
"packageManager": "npm@11.11.0",
"peerDependencies": {
- "@earendil-works/pi-agent-core": ">=0.81.1 <0.82",
- "@earendil-works/pi-ai": ">=0.81.1 <0.82",
- "@earendil-works/pi-coding-agent": ">=0.81.1 <0.82"
+ "@earendil-works/pi-agent-core": ">=0.82.1 <0.83",
+ "@earendil-works/pi-ai": ">=0.82.1 <0.83",
+ "@earendil-works/pi-coding-agent": ">=0.82.1 <0.83"
},
"keywords": [
"pi-package",
diff --git a/src/server/sessions/authService.test.ts b/src/server/sessions/authService.test.ts
index 2cb00ce..3dd3f44 100644
--- a/src/server/sessions/authService.test.ts
+++ b/src/server/sessions/authService.test.ts
@@ -11,7 +11,7 @@ import { OAuthLoginFlowService } from "./oauthLoginFlowService.js";
const tempDirs: string[] = [];
beforeEach(() => {
- // Pi 0.81 uses PI_OFFLINE for refreshes after runtime creation. Auth tests
+ // Pi 0.82 uses PI_OFFLINE for refreshes after runtime creation. Auth tests
// exercise local credential behavior and must never fetch provider catalogs.
vi.stubEnv("PI_OFFLINE", "1");
});
@@ -24,14 +24,14 @@ afterEach(async () => {
describe("AuthService", () => {
it("saves API keys and emits a global auth change after the runtime refreshes", async () => {
const { auth, runtime, credentials, changes } = await createAuthService();
- const reloadConfig = vi.spyOn(runtime, "reloadConfig").mockResolvedValue(undefined);
+ // Pi 0.82 merged reloadConfig() into refresh(), so the provider lookup and
+ // the post-login refresh both land on refresh(): one call each.
const refresh = vi.spyOn(runtime, "refresh");
await expect(auth.saveApiKey("anthropic", "sk-test")).resolves.toEqual({ accepted: true });
await expect(credentials.read("anthropic")).resolves.toEqual({ type: "api_key", key: "sk-test" });
- expect(reloadConfig).toHaveBeenCalledOnce();
- expect(refresh).toHaveBeenCalledOnce();
+ expect(refresh).toHaveBeenCalledTimes(2);
expect(changes).toEqual([{}]);
auth.dispose();
});
@@ -382,10 +382,16 @@ describe("AuthService", () => {
expires: Date.now() + 60_000,
};
vi.spyOn(provider.auth.oauth, "login").mockResolvedValue(credential);
- vi.spyOn(runtime, "reloadConfig").mockResolvedValue(undefined);
const refreshStarted = deferred();
const finishRefresh = deferred();
+ // Pi 0.82 merged reloadConfig() into refresh(), so the provider lookup now
+ // shares this seam with the post-login refresh. Only the post-login call
+ // (the second) is held open; deferring the lookup would stall the flow
+ // before it starts.
+ let refreshCalls = 0;
const refresh = vi.spyOn(runtime, "refresh").mockImplementation(async () => {
+ refreshCalls += 1;
+ if (refreshCalls < 2) return { aborted: false, errors: new Map() };
refreshStarted.resolve(undefined);
await finishRefresh.promise;
return { aborted: false, errors: new Map() };
@@ -405,7 +411,7 @@ describe("AuthService", () => {
expect(auth.oauthFlow(state.flowId)).not.toHaveProperty("error");
await expect(credentials.read(provider.id)).resolves.toEqual(credential);
expect(changes).toEqual([{}]);
- expect(refresh).toHaveBeenCalledOnce();
+ expect(refresh).toHaveBeenCalledTimes(2);
auth.dispose();
});
@@ -472,8 +478,10 @@ describe("AuthService", () => {
describe("createModelRuntimeForAgentDir", () => {
it("keeps runtime-owned refreshes local so request paths cannot stall", async () => {
- // reloadConfig() is the request-path call site that regressed: it refreshes
- // with allowNetwork = the construction-time network flag and no abort signal.
+ // Runtime-owned mutations refresh with allowNetwork = the construction-time
+ // network flag and no abort signal; that is what regressed. Pi 0.82 removed
+ // reloadConfig(), so removeRuntimeApiKey() is the surviving public seam that
+ // still forwards that flag explicitly and can be observed on refresh().
// The ambient PI_OFFLINE=1 stub has to go, or the runtime would be offline
// whether or not the helper forces it and this would assert nothing.
vi.stubEnv("PI_OFFLINE", undefined);
@@ -481,7 +489,7 @@ describe("createModelRuntimeForAgentDir", () => {
const runtime = await createModelRuntimeForAgentDir(agentDir);
const refresh = vi.spyOn(runtime, "refresh");
- await runtime.reloadConfig();
+ await runtime.removeRuntimeApiKey("anthropic");
expect(refresh).toHaveBeenCalledWith({ allowNetwork: false });
});
@@ -502,7 +510,7 @@ describe("createModelRuntimeForAgentDir", () => {
const runtime = await createModelRuntimeForAgentDir(agentDir);
const refresh = vi.spyOn(runtime, "refresh");
- await runtime.reloadConfig();
+ await runtime.removeRuntimeApiKey("anthropic");
expect(refresh).toHaveBeenCalledWith({ allowNetwork: false });
expect(process.env["PI_OFFLINE"]).toBeUndefined();
@@ -523,7 +531,7 @@ describe("createModelRuntimeForAgentDir", () => {
expect(process.env["PI_OFFLINE"]).toBeUndefined();
for (const runtime of runtimes) {
const refresh = vi.spyOn(runtime, "refresh");
- await runtime.reloadConfig();
+ await runtime.removeRuntimeApiKey("anthropic");
expect(refresh).toHaveBeenCalledWith({ allowNetwork: false });
}
} finally {
diff --git a/src/server/sessions/authService.ts b/src/server/sessions/authService.ts
index 81e726d..b313e28 100644
--- a/src/server/sessions/authService.ts
+++ b/src/server/sessions/authService.ts
@@ -42,12 +42,12 @@ let offlineRuntimeCreations: Promise = Promise.resolve();
/**
* Create the shared model runtime with runtime-owned network refreshes disabled.
*
- * Upstream `ModelRuntime.reloadConfig()`, `login()`, and `logout()` always refresh
- * with `allowNetwork: modelNetworkEnabled` and accept no abort signal. With the
- * default (`PI_OFFLINE` unset) a single stalled provider-catalog fetch can block
- * those call paths for minutes — and, because pi-web shares one runtime and pi
- * coalesces per-provider refreshes, session creation joins the same stalled
- * fetch. Forcing `PI_OFFLINE` during construction makes every runtime-driven
+ * Upstream `ModelRuntime.refresh()`, `login()`, and `logout()` always refresh
+ * with `allowNetwork: modelNetworkEnabled` by default, and `login()`/`logout()`
+ * accept no abort signal. With the default (`PI_OFFLINE` unset) a single stalled
+ * provider-catalog fetch can block those call paths for minutes — and, because
+ * pi-web shares one runtime and pi coalesces per-provider refreshes, session
+ * creation joins the same stalled fetch. Forcing `PI_OFFLINE` during construction makes every runtime-driven
* refresh local-only; pi-web performs its own bounded catalog refreshes in the
* background instead (see modelCatalogRefresher.ts).
*
@@ -122,7 +122,7 @@ export class AuthService {
}
async authProviders(mode: "login" | "logout", authType?: AuthType): Promise {
- await this.runtime.reloadConfig();
+ await this.runtime.refresh();
const providers = mode === "logout" ? await getLogoutProviderOptions(this.runtime) : getLoginProviderOptions(this.runtime, authType);
return { providers };
}
@@ -208,7 +208,7 @@ export class AuthService {
}
private async requireApiKeyLoginProvider(providerId: string) {
- await this.runtime.reloadConfig();
+ await this.runtime.refresh();
const provider = getLoginProviderOptions(this.runtime, "api_key").find((option) => option.id === providerId);
if (provider !== undefined) return provider;
@@ -220,7 +220,7 @@ export class AuthService {
}
private async requireOAuthLoginProvider(providerId: string) {
- await this.runtime.reloadConfig();
+ await this.runtime.refresh();
const provider = getLoginProviderOptions(this.runtime, "oauth").find((option) => option.id === providerId);
if (provider === undefined) throw new Error(`OAuth provider not found: ${providerId}`);
return provider;
diff --git a/src/server/sessions/globalProviderPolicy.test.ts b/src/server/sessions/globalProviderPolicy.test.ts
index 0545f9b..69aae37 100644
--- a/src/server/sessions/globalProviderPolicy.test.ts
+++ b/src/server/sessions/globalProviderPolicy.test.ts
@@ -157,6 +157,60 @@ describe("bootstrapAndFreezeGlobalExtensionProviders", () => {
expect(JSON.stringify(ignoredMutations)).not.toContain("secret");
});
+ it("keeps the frozen baseline intact across runtime refreshes that rebuild every provider", async () => {
+ const agentDir = await agentDirWithExtension(`
+ export default function (pi) {
+ pi.registerProvider("global-config", {
+ name: "Global Config",
+ baseUrl: "https://global.example.com",
+ apiKey: "$GLOBAL_PROVIDER_KEY",
+ api: "openai-completions",
+ models: [{
+ id: "global-model",
+ name: "Global Model",
+ reasoning: false,
+ input: ["text"],
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
+ contextWindow: 8192,
+ maxTokens: 1024
+ }]
+ });
+ }
+ `);
+ const runtime = await createTestModelRuntime();
+ // Native and config registrations use separate runtime storage, so the
+ // baseline must cover both before it is frozen.
+ runtime.registerNativeProvider(nativeProvider("global-native", "Global Native"));
+ const { logger } = capturingLogger();
+
+ await bootstrapAndFreezeGlobalExtensionProviders(runtime, agentDir, logger);
+
+ const baselineRegisteredIds = [...runtime.getRegisteredProviderIds()].sort();
+ const baselineProviderIds = runtime.getProviders().map((provider) => provider.id).sort();
+ const baselineConfig = runtime.getRegisteredProviderConfig("global-config");
+ const baselineNative = runtime.getRegisteredNativeProvider("global-native");
+ expect(baselineRegisteredIds).toEqual(["global-config", "global-native"]);
+ expect(baselineProviderIds).toEqual(expect.arrayContaining(["global-config", "global-native", TEST_MODEL_PROVIDER]));
+
+ // Pi 0.82 rebuilds every provider inside refresh(), and PI WEB's background
+ // refresher calls it hourly, so ignored mutations must stay ignored across it.
+ registerProjectConfigProvider(runtime);
+ runtime.registerNativeProvider(nativeProvider("project-native"));
+ runtime.unregisterProvider("global-config");
+ runtime.unregisterProvider("global-native");
+ await runtime.refresh();
+ await runtime.refresh();
+
+ expect([...runtime.getRegisteredProviderIds()].sort()).toEqual(baselineRegisteredIds);
+ expect(runtime.getProviders().map((provider) => provider.id).sort()).toEqual(baselineProviderIds);
+ expect(runtime.getRegisteredProviderConfig("global-config")).toBe(baselineConfig);
+ expect(runtime.getRegisteredNativeProvider("global-native")).toBe(baselineNative);
+ expect(runtime.getRegisteredProviderConfig("project-config")).toBeUndefined();
+ expect(runtime.getRegisteredNativeProvider("project-native")).toBeUndefined();
+ expect(runtime.getModel("global-config", "global-model")).toBeDefined();
+ expect(runtime.getModel(TEST_MODEL_PROVIDER, TEST_MODEL_ID)).toBeDefined();
+ });
+
it("keeps ignored mutations as no-ops when structured logging fails", async () => {
const agentDir = await tempDir("pi-web-global-provider-unit-");
const runtime = await createTestModelRuntime();
diff --git a/src/server/sessions/piSessionService.promptQueue.test.ts b/src/server/sessions/piSessionService.promptQueue.test.ts
index 781acce..9aaa39e 100644
--- a/src/server/sessions/piSessionService.promptQueue.test.ts
+++ b/src/server/sessions/piSessionService.promptQueue.test.ts
@@ -11,7 +11,7 @@ import { CapturingSessionEventHub, createTestModelRuntime, fakeRuntime, runtimeC
const TEST_AGENT_DIR = "/tmp/pi-web-test-agent";
beforeEach(() => {
- // Pi 0.81 uses PI_OFFLINE for refreshes after runtime creation. These tests
+ // Pi 0.82 uses PI_OFFLINE for refreshes after runtime creation. These tests
// exercise local model/auth behavior and must never fetch provider catalogs.
vi.stubEnv("PI_OFFLINE", "1");
});
diff --git a/src/server/sessions/piSessionService.ts b/src/server/sessions/piSessionService.ts
index 720d949..a86dc8a 100644
--- a/src/server/sessions/piSessionService.ts
+++ b/src/server/sessions/piSessionService.ts
@@ -1400,7 +1400,7 @@ export class PiSessionService implements SessionRouteService {
async availableModels(ref: PiSessionLookup): Promise {
const session = await this.getOrOpen(ref);
- await session.modelRuntime.reloadConfig();
+ await session.modelRuntime.refresh();
const models = session.scopedModels.length > 0
? session.scopedModels.map((scoped) => scoped.model)
: session.modelRuntime.getAvailableSnapshot();
@@ -1411,7 +1411,7 @@ export class PiSessionService implements SessionRouteService {
await this.assertWritable(ref);
const session = await this.getOrOpen(ref);
this.assertTreeNavigationInactive(session, "change models");
- await session.modelRuntime.reloadConfig();
+ await session.modelRuntime.refresh();
this.assertTreeNavigationInactive(session, "change models");
const candidates = session.scopedModels.length > 0
? session.scopedModels.map((scoped) => scoped.model)