Archived
feat(sessions): allow global-extension providers, require Pi 0.81
Relaxes the provider policy from 'global config only' to 'global sources': providers registered by agent-dir (global) extensions are learned once at daemon startup and allowed on the shared runtime; project-extension registrations are still rejected with a session warning. Global extensions load identically for every session, so their providers are daemon-consistent and cannot leak project state (#76). - Shim now allows allowlisted ids through and also covers Pi 0.81's native provider path (registerNativeProvider), closing a bypass. - Startup learning step loads only global extensions against a scratch cwd and diffs the runtime's registered provider ids. - Bumps @earendil-works/* dev/peer ranges to >=0.81.1 <0.82; adapts to the Agent.streamFn -> streamFunction rename. - Docs, changeset, unit and acceptance tests updated (global-extension allow path, late re-registration a la pi-tensorx, native provider rule).
This commit is contained in:
@@ -2,4 +2,4 @@
|
|||||||
"@jmfederico/pi-web": patch
|
"@jmfederico/pi-web": patch
|
||||||
---
|
---
|
||||||
|
|
||||||
Support only globally configured providers (Pi built-ins, environment credentials, and the agent directory's `models.json`). Provider registrations from Pi extensions (`pi.registerProvider`) are now ignored and reported with a session warning instead of leaking into every concurrent session; all other extension features keep working. Configure such providers globally in the agent directory's `models.json` to use them. Session daemon code changed: after updating, restart `pi-web-sessiond.service` manually (`systemctl --user restart pi-web-sessiond`).
|
Support providers from global sources only: Pi built-ins, environment credentials, the agent directory's `models.json`, and providers registered by globally installed (agent-dir) extensions. Provider registrations from project extensions (`pi.registerProvider` in a workspace's extensions) are ignored and reported with a session warning instead of leaking into every concurrent session; all other extension features keep working. To use such a provider, configure it globally in `models.json` or install the extension globally. Requires Pi 0.81 or newer. Session daemon code changed: after updating, restart `pi-web-sessiond.service` manually (`systemctl --user restart pi-web-sessiond`).
|
||||||
|
|||||||
+6
-5
@@ -163,11 +163,12 @@
|
|||||||
PI WEB browser plugins. A routine session daemon restart is not required.
|
PI WEB browser plugins. A routine session daemon restart is not required.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
One exception applies to Pi package extensions: PI WEB supports only globally configured providers
|
One exception applies to Pi package extensions: PI WEB providers come from global sources only
|
||||||
(Pi built-ins, environment credentials, and the agent directory's <code>models.json</code>). If an
|
(Pi built-ins, environment credentials, the agent directory's <code>models.json</code>, and providers
|
||||||
extension calls <code>pi.registerProvider</code>, PI WEB ignores the registration and warns in the
|
registered by globally installed, agent-dir extensions). If a project extension calls
|
||||||
session; everything else the extension registers keeps working. Configure such providers globally in
|
<code>pi.registerProvider</code>, PI WEB ignores the registration and warns in the session; everything
|
||||||
the agent directory's <code>models.json</code> instead.
|
else the extension registers keeps working. Move such a provider to a global source: declare it in
|
||||||
|
the agent directory's <code>models.json</code>, or install the extension globally.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -27,9 +27,9 @@ Use **Settings → PI WEB plugins** to enable or disable discovered PI WEB brows
|
|||||||
|
|
||||||
## Extension provider registrations
|
## Extension provider registrations
|
||||||
|
|
||||||
PI WEB only supports globally configured providers: Pi built-ins, environment credentials, and providers declared in the agent directory's `models.json` (the directory selected by `agent.dir`; see [Configuration](https://pi-web.dev/config)). All sessions share one daemon-wide provider set, so extensions cannot add their own: if a Pi extension calls `pi.registerProvider(...)`, PI WEB ignores the registration and shows a warning in the session naming the provider. The extension itself still loads and everything else it registers keeps working; only the ignored provider's models never appear, so an extension that requires its own provider may load but remain unusable.
|
PI WEB providers come from global sources only: Pi built-ins, environment credentials, providers declared in the agent directory's `models.json` (the directory selected by `agent.dir`; see [Configuration](https://pi-web.dev/config)), and providers registered by globally installed (agent-dir) extensions. Global extensions load identically for every session, so their providers are safe on the shared daemon-wide runtime; project extensions differ per workspace and cannot add providers. If a project extension calls `pi.registerProvider(...)`, PI WEB ignores the registration and shows a warning in the session naming the provider. The extension itself still loads and everything else it registers keeps working; only the ignored provider's models never appear, so a project extension that requires its own provider may load but remain unusable.
|
||||||
|
|
||||||
To use such a provider, configure it globally in the agent directory's `models.json` instead. Project-level `models.json` files do not add providers to PI WEB sessions.
|
To use a project extension's provider, move it to a global source: declare it in the agent directory's `models.json`, or install the extension globally in the agent directory. Project-level `models.json` files do not add providers to PI WEB sessions. This policy guards against accidental cross-workspace leakage; it is not a security boundary, since extensions run as trusted code inside the daemon.
|
||||||
|
|
||||||
## Trust model
|
## Trust model
|
||||||
|
|
||||||
|
|||||||
Generated
+116
-131
@@ -42,9 +42,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@changesets/cli": "^2.31.0",
|
"@changesets/cli": "^2.31.0",
|
||||||
"@earendil-works/pi-agent-core": "^0.80.8",
|
"@earendil-works/pi-agent-core": "^0.81.1",
|
||||||
"@earendil-works/pi-ai": "^0.80.8",
|
"@earendil-works/pi-ai": "^0.81.1",
|
||||||
"@earendil-works/pi-coding-agent": "^0.80.8",
|
"@earendil-works/pi-coding-agent": "^0.81.1",
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@types/node": "^24.13.3",
|
"@types/node": "^24.13.3",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
@@ -61,9 +61,9 @@
|
|||||||
"node": ">=22.19.0"
|
"node": ">=22.19.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@earendil-works/pi-agent-core": ">=0.80.8 <0.81",
|
"@earendil-works/pi-agent-core": ">=0.81.1 <0.82",
|
||||||
"@earendil-works/pi-ai": ">=0.80.8 <0.81",
|
"@earendil-works/pi-ai": ">=0.81.1 <0.82",
|
||||||
"@earendil-works/pi-coding-agent": ">=0.80.8 <0.81"
|
"@earendil-works/pi-coding-agent": ">=0.81.1 <0.82"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@anthropic-ai/sdk": {
|
"node_modules/@anthropic-ai/sdk": {
|
||||||
@@ -167,9 +167,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/core": {
|
"node_modules/@aws-sdk/core": {
|
||||||
"version": "3.975.3",
|
"version": "3.976.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.975.3.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.976.0.tgz",
|
||||||
"integrity": "sha512-7ur3kCKuvPLqlsZ2XlvnNBVQ7KkpSu6Y6dOTwSPHLrFpTEfZM8isLBJc4cgv96WB7GifeVM436mpycwxBd2vEA==",
|
"integrity": "sha512-0cjRaEdlVoOrsNb9pP5q1Syyc8pXw5xSj2Np2ryReRTr9FppIIRVSdZK4lbnfmc2Hvgux/xBOUU6baB7z8//uA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -187,13 +187,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-env": {
|
"node_modules/@aws-sdk/credential-provider-env": {
|
||||||
"version": "3.972.59",
|
"version": "3.972.60",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.59.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.60.tgz",
|
||||||
"integrity": "sha512-Ny5e4Mfh3QPmiAc0AiUe+cbTXDlxkU3Rc+EpWOfyWeWEy6yp7Fa1KmfNeCc+1a8by9zQ9gtohmiQUkMPScF3ng==",
|
"integrity": "sha512-BAkxdoe7tpDDqCghGpuOeHQRbm/2znVvOQm0AvpQbA2tbfMN46doN4zx65fv85ImP3KADwc2zQPmbrlI9MPfMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
@@ -204,13 +204,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-http": {
|
"node_modules/@aws-sdk/credential-provider-http": {
|
||||||
"version": "3.972.61",
|
"version": "3.972.62",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.61.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.62.tgz",
|
||||||
"integrity": "sha512-8jAjgStl5Ytq4+HF3X/9f+EmRinaRbGRRtQGktlPfBRVx73H+R1y48vIeXerQtYGFaUqkEp3fT6jP854rVO2yQ==",
|
"integrity": "sha512-g/0fGqKTb9xpKdd9AtpmV5Eo3DFKbnkpA2+w0peISSlu7NfAoWOuYBFxsu+yWBtxU89ka55ezoZBCbFaS8pjYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/fetch-http-handler": "^5.6.6",
|
"@smithy/fetch-http-handler": "^5.6.6",
|
||||||
@@ -223,13 +223,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": {
|
"node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": {
|
||||||
"version": "4.9.7",
|
"version": "4.9.9",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.7.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.9.tgz",
|
||||||
"integrity": "sha512-wCU8HCLjAtAVqxxe0j2xff9LcEPw3yjBbg5IdQDIYFnxnPxbxcSLc7rgex7kqm9L/WYOnJEgaWQlfDkZleozMA==",
|
"integrity": "sha512-xVBZ3hptB99iNO9XyWqEhC7KD9bP9UPXhuy3h5Y2ItCfBv160D9IIC/Fmmp3EbnWwit4C+KVqlSE+E29Nk/pPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^3.29.5",
|
"@smithy/core": "^3.29.7",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
@@ -238,20 +238,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-ini": {
|
"node_modules/@aws-sdk/credential-provider-ini": {
|
||||||
"version": "3.973.4",
|
"version": "3.973.5",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.4.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.5.tgz",
|
||||||
"integrity": "sha512-e6ZvVsj90aRALf1kHP+J4iqC1496ZpVgqI/+u0LJ5HL7q7ATauGy4gdDvRCP13L1pN/fMiZLah162PGIYkbUVQ==",
|
"integrity": "sha512-ylubazcRfq2TVus/qXucSXeC42Qdjp5HQxTu68K/BsdMiZlcSLD1zkpoCgApXZX1Y6YJhtGGs7ZHhO/GuIgBlw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/credential-provider-env": "^3.972.59",
|
"@aws-sdk/credential-provider-env": "^3.972.60",
|
||||||
"@aws-sdk/credential-provider-http": "^3.972.61",
|
"@aws-sdk/credential-provider-http": "^3.972.62",
|
||||||
"@aws-sdk/credential-provider-login": "^3.972.66",
|
"@aws-sdk/credential-provider-login": "^3.972.67",
|
||||||
"@aws-sdk/credential-provider-process": "^3.972.59",
|
"@aws-sdk/credential-provider-process": "^3.972.60",
|
||||||
"@aws-sdk/credential-provider-sso": "^3.973.3",
|
"@aws-sdk/credential-provider-sso": "^3.973.4",
|
||||||
"@aws-sdk/credential-provider-web-identity": "^3.972.65",
|
"@aws-sdk/credential-provider-web-identity": "^3.972.66",
|
||||||
"@aws-sdk/nested-clients": "^3.997.33",
|
"@aws-sdk/nested-clients": "^3.997.34",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/credential-provider-imds": "^4.4.9",
|
"@smithy/credential-provider-imds": "^4.4.9",
|
||||||
@@ -263,14 +263,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-login": {
|
"node_modules/@aws-sdk/credential-provider-login": {
|
||||||
"version": "3.972.66",
|
"version": "3.972.67",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.66.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.67.tgz",
|
||||||
"integrity": "sha512-g2fsqm87r/nKthLZ0VkkDBElkGg0PvSa8d97HQ6EilMbJTZ6hxa8FxkSZyJfgPfFdZn0TTmkOffQmTSUcAHIng==",
|
"integrity": "sha512-CCygIKJ9YbI3n84OClSaSppkgKKHVj2TGT33c6FRORZrYNZQ1POmD+ip0FLYokiJAK7sSdc3YVkOsBm90oxWMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/nested-clients": "^3.997.33",
|
"@aws-sdk/nested-clients": "^3.997.34",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
@@ -281,18 +281,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-node": {
|
"node_modules/@aws-sdk/credential-provider-node": {
|
||||||
"version": "3.972.70",
|
"version": "3.972.71",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.70.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.71.tgz",
|
||||||
"integrity": "sha512-3xzvkGdykBunxqh8WudmUpSyLWvIhfI6aBQo1b5rb3mDO5mNLadK+0hiI0qBQBMVynJbfLO+Ajy9dztMwy9O8w==",
|
"integrity": "sha512-HIg7Q2osBzajQwL+1Vkyh2E7Gim3eTNb9RHIsOxDGjW0eZg4oEKtRs5sioCnc73ilhaOm4gX2lHVF8J7+nt2rg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/credential-provider-env": "^3.972.59",
|
"@aws-sdk/credential-provider-env": "^3.972.60",
|
||||||
"@aws-sdk/credential-provider-http": "^3.972.61",
|
"@aws-sdk/credential-provider-http": "^3.972.62",
|
||||||
"@aws-sdk/credential-provider-ini": "^3.973.4",
|
"@aws-sdk/credential-provider-ini": "^3.973.5",
|
||||||
"@aws-sdk/credential-provider-process": "^3.972.59",
|
"@aws-sdk/credential-provider-process": "^3.972.60",
|
||||||
"@aws-sdk/credential-provider-sso": "^3.973.3",
|
"@aws-sdk/credential-provider-sso": "^3.973.4",
|
||||||
"@aws-sdk/credential-provider-web-identity": "^3.972.65",
|
"@aws-sdk/credential-provider-web-identity": "^3.972.66",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/credential-provider-imds": "^4.4.9",
|
"@smithy/credential-provider-imds": "^4.4.9",
|
||||||
@@ -304,13 +304,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-process": {
|
"node_modules/@aws-sdk/credential-provider-process": {
|
||||||
"version": "3.972.59",
|
"version": "3.972.60",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.59.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.60.tgz",
|
||||||
"integrity": "sha512-DlZF2/MhLlatDdlrIy3CUCpfdbLrKx+3SMjVo+WyHnPpwzkc/M3vwAHw4OVJf7DMvO+4vfRqSCMc/E9I1auN0g==",
|
"integrity": "sha512-YIo3f99hM43QdYG8hDzwGemnR/pU95b0kramqSJUTleCqaB7+HwKf7YZFHqvOgTqZTPx/mRmNIqoDRr3U0Z3Tw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
@@ -321,15 +321,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-sso": {
|
"node_modules/@aws-sdk/credential-provider-sso": {
|
||||||
"version": "3.973.3",
|
"version": "3.973.4",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.3.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.4.tgz",
|
||||||
"integrity": "sha512-hmdDHoy2G5Es2e8IgelNMYUuSQI6uCIAKZMJ2u2PdKDhxvbk1uWD/g4+R7R5c/tJfKEB1+KjjWiaoCr/S+ZTiQ==",
|
"integrity": "sha512-BPdmL8sSBOCv4ngZ+3LHxyc3CNqDCEK37CHioCk7zGrTMY5sUtkH8q+o6qA80nn6w3/fyBPGNE7OIRlmoOxRQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/nested-clients": "^3.997.33",
|
"@aws-sdk/nested-clients": "^3.997.34",
|
||||||
"@aws-sdk/token-providers": "3.1088.0",
|
"@aws-sdk/token-providers": "3.1092.0",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
@@ -340,14 +340,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": {
|
"node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": {
|
||||||
"version": "3.1088.0",
|
"version": "3.1092.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1088.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1092.0.tgz",
|
||||||
"integrity": "sha512-4ObatWt2qpJg5FBk4LOOKrTQYzaqeewAtdO3r9ZO8lH9YqLtpTzLyIdy0mJ+nVdfYOnqISkKNfmzP22bNDhwyw==",
|
"integrity": "sha512-hBYUAr6iBLNFcsiWTgtBb0stdSw39VOUq4Sp4A5caCNf66BAZplWN4FleKrVpJx5li2YgdnK2DqoFSMWC642FQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/nested-clients": "^3.997.33",
|
"@aws-sdk/nested-clients": "^3.997.34",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
@@ -358,14 +358,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-web-identity": {
|
"node_modules/@aws-sdk/credential-provider-web-identity": {
|
||||||
"version": "3.972.65",
|
"version": "3.972.66",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.65.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.66.tgz",
|
||||||
"integrity": "sha512-gHQb/Kt0chjk/JQDa/GJDqmAvEuVn8n7z10wK2h0LFM9TUDRkohgOO4aEF+s2sBLM0br7Cl5W6P7phgjrrJvLQ==",
|
"integrity": "sha512-kSAziJboOmZmsR9/MTbiNjowl2BPes1bQuJpne4qAZ62ubi8fjfr/aupJSQje6udBoYxXTQbsL0e0kby2la3ng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/nested-clients": "^3.997.33",
|
"@aws-sdk/nested-clients": "^3.997.34",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
@@ -408,13 +408,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/middleware-websocket": {
|
"node_modules/@aws-sdk/middleware-websocket": {
|
||||||
"version": "3.972.41",
|
"version": "3.972.42",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.41.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.42.tgz",
|
||||||
"integrity": "sha512-LSbGvvYmjc4Br9BPYI2dTLnIclmrSiQbahkP4D6nRGVEv4qsCZ8csVuKBPVEEFCVD+EEngGh8ROls6XpumtwMg==",
|
"integrity": "sha512-dw+GP8DC7QC2C8tUoK7DI8BnrNAjz8tb+uBHSrD2qJvxkCf58kTtFr98pljSrk+umU4n4HDW4eU2k7C2dWMzsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
"@smithy/fetch-http-handler": "^5.6.6",
|
"@smithy/fetch-http-handler": "^5.6.6",
|
||||||
@@ -427,13 +427,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/nested-clients": {
|
"node_modules/@aws-sdk/nested-clients": {
|
||||||
"version": "3.997.33",
|
"version": "3.997.34",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.33.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.34.tgz",
|
||||||
"integrity": "sha512-dVZOroI/r3/ENvqNGgjMPul+jjlz9GddfVusgTXlVjfZj5isibOxecLkGQbRPp8XOuX+RAfjXLFgPkD1JS5xrw==",
|
"integrity": "sha512-Y9REVrSwmLM+Qy6sZJ7ofMC2S3Hr3tPP/4CzL5U1olPP7OGoF+6+Px0E49cVQBtSxJtyeLJMf0UaBErfeSahAA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/core": "^3.975.3",
|
"@aws-sdk/core": "^3.976.0",
|
||||||
"@aws-sdk/signature-v4-multi-region": "^3.996.41",
|
"@aws-sdk/signature-v4-multi-region": "^3.996.41",
|
||||||
"@aws-sdk/types": "^3.974.2",
|
"@aws-sdk/types": "^3.974.2",
|
||||||
"@smithy/core": "^3.29.4",
|
"@smithy/core": "^3.29.4",
|
||||||
@@ -447,13 +447,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler": {
|
"node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler": {
|
||||||
"version": "4.9.7",
|
"version": "4.9.9",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.7.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.9.tgz",
|
||||||
"integrity": "sha512-wCU8HCLjAtAVqxxe0j2xff9LcEPw3yjBbg5IdQDIYFnxnPxbxcSLc7rgex7kqm9L/WYOnJEgaWQlfDkZleozMA==",
|
"integrity": "sha512-xVBZ3hptB99iNO9XyWqEhC7KD9bP9UPXhuy3h5Y2ItCfBv160D9IIC/Fmmp3EbnWwit4C+KVqlSE+E29Nk/pPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^3.29.5",
|
"@smithy/core": "^3.29.7",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
@@ -984,13 +984,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@earendil-works/pi-agent-core": {
|
"node_modules/@earendil-works/pi-agent-core": {
|
||||||
"version": "0.80.10",
|
"version": "0.81.1",
|
||||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.81.1.tgz",
|
||||||
"integrity": "sha512-nwnOR3SuLYGRFfyQm8ri4Nj5VGVAvAM9GuqQd3u7BUQj0d6hmD2F8w7OHAAjThE3CuySIdM+v8E22QJG6/RfCg==",
|
"integrity": "sha512-yqbh68CyhqxMov/jUogFJfMqlu2Gd37GAki+tr59YCmAPHfomiCA5ESzusXtpGzABeiZFC/OrRdQ4GwCCOMIHA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@earendil-works/pi-ai": "^0.80.10",
|
"@earendil-works/pi-ai": "^0.81.1",
|
||||||
"ignore": "7.0.5",
|
"ignore": "7.0.5",
|
||||||
"typebox": "1.1.38",
|
"typebox": "1.1.38",
|
||||||
"yaml": "2.9.0"
|
"yaml": "2.9.0"
|
||||||
@@ -1007,9 +1007,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@earendil-works/pi-ai": {
|
"node_modules/@earendil-works/pi-ai": {
|
||||||
"version": "0.80.10",
|
"version": "0.81.1",
|
||||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.81.1.tgz",
|
||||||
"integrity": "sha512-Moe/H8c87yacDGK9dPbWphZNjVsrb3nTrIHycOQJAkFEnY9PYxOOd74+ny44kATfPU9Dm7aTHefar3pZF+UKUA==",
|
"integrity": "sha512-hzHE7Z8l5mgJk+ke67Lge0rwS2+wbKJrFKl9o5M1R1rh33+cCT7D1AHz1OAtX5wFs90E1/BTGhyJRTUHaMxGvQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1040,16 +1040,16 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@earendil-works/pi-coding-agent": {
|
"node_modules/@earendil-works/pi-coding-agent": {
|
||||||
"version": "0.80.10",
|
"version": "0.81.1",
|
||||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.81.1.tgz",
|
||||||
"integrity": "sha512-aL4apbupCHiVLSXASXvRzH4Q2vmtfrDa+0s909CJuVu/GgGylbDzr7oyF1mPmip5E+VxYYxKWmph4hV04wUcQg==",
|
"integrity": "sha512-r6ovAsZOgAqbC/aU6s+/dPnv/sGZBuWyZNvi3pXjpbuX5wvp3XvGkQI7/VLvX2o9XpmpFaPUxKNym1WfkN/P8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasShrinkwrap": true,
|
"hasShrinkwrap": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@earendil-works/pi-agent-core": "^0.80.10",
|
"@earendil-works/pi-agent-core": "^0.81.1",
|
||||||
"@earendil-works/pi-ai": "^0.80.10",
|
"@earendil-works/pi-ai": "^0.81.1",
|
||||||
"@earendil-works/pi-tui": "^0.80.10",
|
"@earendil-works/pi-tui": "^0.81.1",
|
||||||
"@silvia-odwyer/photon-node": "0.3.4",
|
"@silvia-odwyer/photon-node": "0.3.4",
|
||||||
"chalk": "5.6.2",
|
"chalk": "5.6.2",
|
||||||
"cross-spawn": "7.0.6",
|
"cross-spawn": "7.0.6",
|
||||||
@@ -1539,12 +1539,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": {
|
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": {
|
||||||
"version": "0.80.10",
|
"version": "0.81.1",
|
||||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.81.1.tgz",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@earendil-works/pi-ai": "^0.80.10",
|
"@earendil-works/pi-ai": "^0.81.1",
|
||||||
"ignore": "7.0.5",
|
"ignore": "7.0.5",
|
||||||
"typebox": "1.1.38",
|
"typebox": "1.1.38",
|
||||||
"yaml": "2.9.0"
|
"yaml": "2.9.0"
|
||||||
@@ -1554,8 +1554,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": {
|
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": {
|
||||||
"version": "0.80.10",
|
"version": "0.81.1",
|
||||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.81.1.tgz",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1579,8 +1579,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": {
|
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": {
|
||||||
"version": "0.80.10",
|
"version": "0.81.1",
|
||||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.10.tgz",
|
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.81.1.tgz",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1695,9 +1695,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1715,9 +1712,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1735,9 +1729,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1755,9 +1746,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1775,9 +1763,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2146,9 +2131,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": {
|
"node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": {
|
||||||
"version": "5.0.6",
|
"version": "5.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5345,9 +5330,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/core": {
|
"node_modules/@smithy/core": {
|
||||||
"version": "3.29.5",
|
"version": "3.29.7",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.7.tgz",
|
||||||
"integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==",
|
"integrity": "sha512-BiEE2bnnGoPKdlGe3L+gOYORDHFGPuYVRLP7iUow/Sflm0B4hC4XY3FC1MRuc7ltzpW2xNnXopKi34TTkULlKQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5359,13 +5344,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/credential-provider-imds": {
|
"node_modules/@smithy/credential-provider-imds": {
|
||||||
"version": "4.4.10",
|
"version": "4.4.12",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.12.tgz",
|
||||||
"integrity": "sha512-MJenAe4OKRZUo1LdYYFDCsSHxaHvInIU/z52GsheO9vl1/VSySVCr0zkyKD6TFiGkSUaWGxvKZ/70OvgUZR5HQ==",
|
"integrity": "sha512-ZZPDbl/aRp77aycuoMlo3BTayT4CE2a3uoqETYZU5ySnVbhpl5IJiY7dCZedn+ZusyDLqVv44IvKBiXd2/nK0Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^3.29.5",
|
"@smithy/core": "^3.29.7",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
@@ -5374,13 +5359,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/fetch-http-handler": {
|
"node_modules/@smithy/fetch-http-handler": {
|
||||||
"version": "5.6.7",
|
"version": "5.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.9.tgz",
|
||||||
"integrity": "sha512-3zpg8yqqyXzoK2TsRDdkqVOj2RDBFfLXwCczOZ5c7TWB4eiaebfSCsbMjDPYB3PJ9ihV62QaeadZ+wLadZtNGA==",
|
"integrity": "sha512-EJktha5m5MXCwzdXrlWyqb9UCNHNFKlg+PmTpRsdX3dncJPTiqYleM9OKj2mLgdVJHR01d2tU4alG+z2NdH5rQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^3.29.5",
|
"@smithy/core": "^3.29.7",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
@@ -5417,13 +5402,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/signature-v4": {
|
"node_modules/@smithy/signature-v4": {
|
||||||
"version": "5.6.6",
|
"version": "5.6.8",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.6.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.8.tgz",
|
||||||
"integrity": "sha512-efP6DN3UTFrzIsGO42/xcabv8jU7+9nwEdphFUH7yL0k010ERyAWaO41KFQIDLcFZLZ8xzIQr4wplFxNzslSGQ==",
|
"integrity": "sha512-iGBm6hIwD2MGvVRSgrjVWa4FXtXDq3akxu0DCpnkmBo0xtEHZ/siMRt7ycfZAefYr2UdywUgmGtoRLaq5u56pg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^3.29.5",
|
"@smithy/core": "^3.29.7",
|
||||||
"@smithy/types": "^4.16.1",
|
"@smithy/types": "^4.16.1",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
|
|||||||
+6
-6
@@ -83,9 +83,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@changesets/cli": "^2.31.0",
|
"@changesets/cli": "^2.31.0",
|
||||||
"@earendil-works/pi-agent-core": "^0.80.8",
|
"@earendil-works/pi-agent-core": "^0.81.1",
|
||||||
"@earendil-works/pi-ai": "^0.80.8",
|
"@earendil-works/pi-ai": "^0.81.1",
|
||||||
"@earendil-works/pi-coding-agent": "^0.80.8",
|
"@earendil-works/pi-coding-agent": "^0.81.1",
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@types/node": "^24.13.3",
|
"@types/node": "^24.13.3",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
@@ -114,9 +114,9 @@
|
|||||||
"homepage": "https://pi-web.dev/",
|
"homepage": "https://pi-web.dev/",
|
||||||
"packageManager": "[email protected]",
|
"packageManager": "[email protected]",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@earendil-works/pi-agent-core": ">=0.80.8 <0.81",
|
"@earendil-works/pi-agent-core": ">=0.81.1 <0.82",
|
||||||
"@earendil-works/pi-ai": ">=0.80.8 <0.81",
|
"@earendil-works/pi-ai": ">=0.81.1 <0.82",
|
||||||
"@earendil-works/pi-coding-agent": ">=0.80.8 <0.81"
|
"@earendil-works/pi-coding-agent": ">=0.81.1 <0.82"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"pi-package",
|
"pi-package",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { WorkspaceActivityService } from "./activity/workspaceActivityService.js
|
|||||||
import { registerWorkspaceActivityRoutes } from "./activity/workspaceActivityRoutes.js";
|
import { registerWorkspaceActivityRoutes } from "./activity/workspaceActivityRoutes.js";
|
||||||
import { SessionEventHub } from "./realtime/sessionEventHub.js";
|
import { SessionEventHub } from "./realtime/sessionEventHub.js";
|
||||||
import { AuthService } from "./sessions/authService.js";
|
import { AuthService } from "./sessions/authService.js";
|
||||||
import { installGlobalProviderPolicy } from "./sessions/globalProviderPolicy.js";
|
import { installGlobalProviderPolicy, learnGlobalExtensionProviderIds } from "./sessions/globalProviderPolicy.js";
|
||||||
import { registerAuthRoutes } from "./sessions/authRoutes.js";
|
import { registerAuthRoutes } from "./sessions/authRoutes.js";
|
||||||
import { PiSessionService } from "./sessions/piSessionService.js";
|
import { PiSessionService } from "./sessions/piSessionService.js";
|
||||||
import { createPiSessionManagerGateway } from "./sessions/piSessionManagerGateway.js";
|
import { createPiSessionManagerGateway } from "./sessions/piSessionManagerGateway.js";
|
||||||
@@ -70,9 +70,12 @@ await runSessionDaemonStartup({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
auth.subscribe((change) => { sessions.applyAuthChange(change); });
|
auth.subscribe((change) => { sessions.applyAuthChange(change); });
|
||||||
// PI WEB only supports globally configured providers: reject every
|
// PI WEB providers come from global sources only. Learn which providers
|
||||||
|
// the agent directory's global extensions register (they are identical
|
||||||
|
// for every session, hence daemon-safe), then reject every other
|
||||||
// extension provider registration against the shared daemon-wide runtime.
|
// extension provider registration against the shared daemon-wide runtime.
|
||||||
installGlobalProviderPolicy(auth.runtime, (providerId) => { sessions.noteRejectedProviderRegistration(providerId); });
|
const globalExtensionProviderIds = await learnGlobalExtensionProviderIds(auth.runtime, activeAgentProfile.dir);
|
||||||
|
installGlobalProviderPolicy(auth.runtime, globalExtensionProviderIds, (providerId) => { sessions.noteRejectedProviderRegistration(providerId); });
|
||||||
const terminals = new TerminalService(eventHub, workspaceActivity);
|
const terminals = new TerminalService(eventHub, workspaceActivity);
|
||||||
const runtimeComponent = Object.freeze({
|
const runtimeComponent = Object.freeze({
|
||||||
...getPiWebRuntimeComponent("sessiond", SESSIOND_RUNTIME_CAPABILITIES),
|
...getPiWebRuntimeComponent("sessiond", SESSIOND_RUNTIME_CAPABILITIES),
|
||||||
|
|||||||
@@ -4,22 +4,24 @@ import { join } from "node:path";
|
|||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
||||||
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
||||||
import { installGlobalProviderPolicy, providerRejectionMessage } from "./globalProviderPolicy.js";
|
import { installGlobalProviderPolicy, learnGlobalExtensionProviderIds, providerRejectionMessage } from "./globalProviderPolicy.js";
|
||||||
import { createPiSessionManagerGateway } from "./piSessionManagerGateway.js";
|
import { createPiSessionManagerGateway } from "./piSessionManagerGateway.js";
|
||||||
import { PiSessionService } from "./piSessionService.js";
|
import { PiSessionService } from "./piSessionService.js";
|
||||||
import { CapturingSessionEventHub, createTestModelRuntime, TEST_MODEL_ID, TEST_MODEL_PROVIDER } from "./piSessionService.testSupport.js";
|
import { CapturingSessionEventHub, createTestModelRuntime, TEST_MODEL_ID, TEST_MODEL_PROVIDER } from "./piSessionService.testSupport.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Acceptance tests for the global provider policy, wired exactly as sessiond
|
* Acceptance tests for the global provider policy, wired exactly as sessiond
|
||||||
* wires it in production: one shared ModelRuntime per daemon, the policy shim
|
* wires it in production: one shared ModelRuntime per daemon, the global
|
||||||
* installed on it, and rejections fed into the session service. Sessions are
|
* extensions' provider ids learned at startup, the policy shim installed on
|
||||||
* created through the real default runtime factory, so project extensions in a
|
* the runtime, and rejections fed into the session service. Sessions are
|
||||||
* temp cwd are genuinely loaded by Pi's `createAgentSessionServices`.
|
* created through the real default runtime factory, so extensions in a temp
|
||||||
|
* cwd or temp agent dir are genuinely loaded by Pi's
|
||||||
|
* `createAgentSessionServices`.
|
||||||
*
|
*
|
||||||
* These tests are also the tripwire for the shim's one piece of machinery
|
* These tests are also the tripwire for the shim's one piece of machinery
|
||||||
* (instance-method shadowing of `registerProvider`): if a Pi upgrade changes
|
* (instance-method shadowing of `registerProvider` / `registerNativeProvider`
|
||||||
* how registrations reach the runtime, the load-time and late-registration
|
* / `unregisterProvider`): if a Pi upgrade changes how registrations reach
|
||||||
* tests here fail loudly.
|
* the runtime, the load-time and late-registration tests here fail loudly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const tempDirs: string[] = [];
|
const tempDirs: string[] = [];
|
||||||
@@ -56,11 +58,21 @@ async function policyHarness(options: { runtime?: ModelRuntime; agentDir?: strin
|
|||||||
heartbeatIntervalMs: 60_000,
|
heartbeatIntervalMs: 60_000,
|
||||||
});
|
});
|
||||||
services.push(service);
|
services.push(service);
|
||||||
// The exact sessiond wiring: policy on the shared runtime, rejections to the service.
|
// The exact sessiond wiring: learn the agent dir's global-extension
|
||||||
installGlobalProviderPolicy(runtime, (providerId) => { service.noteRejectedProviderRegistration(providerId); });
|
// providers first, then the policy on the shared runtime, rejections to the
|
||||||
|
// service.
|
||||||
|
const allowedProviderIds = await learnGlobalExtensionProviderIds(runtime, agentDir);
|
||||||
|
installGlobalProviderPolicy(runtime, allowedProviderIds, (providerId) => { service.noteRejectedProviderRegistration(providerId); });
|
||||||
return { service, runtime, agentDir };
|
return { service, runtime, agentDir };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Write a global extension into `<agentDir>/extensions/` (agent-dir extensions load for every session). */
|
||||||
|
async function agentDirWithExtension(agentDir: string, source: string): Promise<string> {
|
||||||
|
await mkdir(join(agentDir, "extensions"), { recursive: true });
|
||||||
|
await writeFile(join(agentDir, "extensions", "global-probe.js"), source);
|
||||||
|
return agentDir;
|
||||||
|
}
|
||||||
|
|
||||||
/** Write a project extension into `<cwd>/.pi/extensions/` and return the cwd. */
|
/** Write a project extension into `<cwd>/.pi/extensions/` and return the cwd. */
|
||||||
async function projectWithExtension(source: string): Promise<string> {
|
async function projectWithExtension(source: string): Promise<string> {
|
||||||
const cwd = await tempDir("pi-web-policy-project-");
|
const cwd = await tempDir("pi-web-policy-project-");
|
||||||
@@ -97,7 +109,7 @@ function providerRegistrationSource(providerId: string): string {
|
|||||||
return `pi.registerProvider(${JSON.stringify(providerId)}, ${providerConfigJson(providerId)});`;
|
return `pi.registerProvider(${JSON.stringify(providerId)}, ${providerConfigJson(providerId)});`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const POLICY_WORDING = "PI WEB only supports globally configured providers";
|
const POLICY_WORDING = "PI WEB providers must come from global configuration";
|
||||||
|
|
||||||
describe("global provider policy acceptance", () => {
|
describe("global provider policy acceptance", () => {
|
||||||
it("rejects a load-time provider registration while the extension's tool and command keep working", async () => {
|
it("rejects a load-time provider registration while the extension's tool and command keep working", async () => {
|
||||||
@@ -231,6 +243,59 @@ describe("global provider policy acceptance", () => {
|
|||||||
expect(runtime.getModel("collide-acme", "model-1")).toBeUndefined();
|
expect(runtime.getModel("collide-acme", "model-1")).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows providers from global (agent-dir) extensions while still rejecting project extensions", async () => {
|
||||||
|
const agentDir = await agentDirWithExtension(await tempDir("pi-web-policy-agent-"), `
|
||||||
|
export default function (pi) {
|
||||||
|
${providerRegistrationSource("global-ext")}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
const { service, runtime } = await policyHarness({ agentDir });
|
||||||
|
const cwd = await projectWithExtension(`
|
||||||
|
export default function (pi) {
|
||||||
|
${providerRegistrationSource("project-ext")}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const session = await service.start(cwd);
|
||||||
|
const ref = { id: session.id, cwd };
|
||||||
|
|
||||||
|
// The global extension's provider reached the shared runtime at daemon
|
||||||
|
// startup and stays usable; the project extension's is rejected.
|
||||||
|
expect(runtime.getModel("global-ext", "model-1")).toBeDefined();
|
||||||
|
expect(runtime.getModel("project-ext", "model-1")).toBeUndefined();
|
||||||
|
expect(runtime.getRegisteredProviderIds()).toEqual(["global-ext"]);
|
||||||
|
const status = await service.status(ref);
|
||||||
|
expect(status.warnings).toEqual([
|
||||||
|
{ severity: "warning", message: providerRejectionMessage("project-ext", cwd), source: "runtime" },
|
||||||
|
]);
|
||||||
|
const models = await service.availableModels(ref);
|
||||||
|
expect(models.some((model) => model.provider === "global-ext")).toBe(true);
|
||||||
|
expect(models.some((model) => model.provider === "project-ext")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lets a global extension re-register its provider late without warnings", async () => {
|
||||||
|
// The pi-tensorx pattern: register on load, then re-register from
|
||||||
|
// session_start with a refreshed model catalog. Both calls carry the
|
||||||
|
// learned id, so neither is a leak.
|
||||||
|
const agentDir = await agentDirWithExtension(await tempDir("pi-web-policy-agent-"), `
|
||||||
|
export default function (pi) {
|
||||||
|
${providerRegistrationSource("global-ext")}
|
||||||
|
pi.on("session_start", () => {
|
||||||
|
${providerRegistrationSource("global-ext")}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
const { service, runtime } = await policyHarness({ agentDir });
|
||||||
|
const plainCwd = await tempDir("pi-web-policy-project-");
|
||||||
|
|
||||||
|
const bystander = await service.start(plainCwd);
|
||||||
|
const status = await service.status({ id: bystander.id, cwd: plainCwd });
|
||||||
|
expect((status.warnings ?? []).filter((warning) => warning.message.includes(POLICY_WORDING))).toEqual([]);
|
||||||
|
const inbox = service.notificationInbox({ id: bystander.id, cwd: plainCwd });
|
||||||
|
expect(inbox.notifications.filter((notification) => notification.message.includes(POLICY_WORDING))).toEqual([]);
|
||||||
|
expect(runtime.getModel("global-ext", "model-1")).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("does not let a project-level models.json alter the shared runtime's provider set", async () => {
|
it("does not let a project-level models.json alter the shared runtime's provider set", async () => {
|
||||||
// Spike assertion for plan §2: the shared runtime reads providers from the
|
// Spike assertion for plan §2: the shared runtime reads providers from the
|
||||||
// agent-dir models.json only. A project-level models.json is not a
|
// agent-dir models.json only. A project-level models.json is not a
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
import type { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
||||||
|
import type { Provider } from "@earendil-works/pi-ai";
|
||||||
import { installGlobalProviderPolicy, providerRejectionMessage } from "./globalProviderPolicy.js";
|
import { installGlobalProviderPolicy, providerRejectionMessage } from "./globalProviderPolicy.js";
|
||||||
import { createTestModelRuntime, TEST_MODEL_ID, TEST_MODEL_PROVIDER } from "./piSessionService.testSupport.js";
|
import { createTestModelRuntime, TEST_MODEL_ID, TEST_MODEL_PROVIDER } from "./piSessionService.testSupport.js";
|
||||||
|
|
||||||
@@ -8,15 +9,33 @@ import { createTestModelRuntime, TEST_MODEL_ID, TEST_MODEL_PROVIDER } from "./pi
|
|||||||
* every test builds a dedicated runtime rather than touching the shared
|
* every test builds a dedicated runtime rather than touching the shared
|
||||||
* `testModelRuntime` from testSupport.
|
* `testModelRuntime` from testSupport.
|
||||||
*/
|
*/
|
||||||
async function policyRuntime(): Promise<{ runtime: ModelRuntime; rejections: string[] }> {
|
async function policyRuntime(
|
||||||
|
allowedExtensionProviderIds: ReadonlySet<string> = new Set(),
|
||||||
|
): Promise<{ runtime: ModelRuntime; rejections: string[] }> {
|
||||||
const runtime = await createTestModelRuntime();
|
const runtime = await createTestModelRuntime();
|
||||||
const rejections: string[] = [];
|
const rejections: string[] = [];
|
||||||
installGlobalProviderPolicy(runtime, (providerId) => { rejections.push(providerId); });
|
installGlobalProviderPolicy(runtime, allowedExtensionProviderIds, (providerId) => { rejections.push(providerId); });
|
||||||
return { runtime, rejections };
|
return { runtime, rejections };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nativeProvider(providerId: string): Provider {
|
||||||
|
return {
|
||||||
|
id: providerId,
|
||||||
|
name: providerId,
|
||||||
|
auth: {
|
||||||
|
apiKey: {
|
||||||
|
name: `${providerId} API key`,
|
||||||
|
resolve: () => Promise.resolve(undefined),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getModels: () => [],
|
||||||
|
stream: () => { throw new Error("stream should not be called in this test"); },
|
||||||
|
streamSimple: () => { throw new Error("streamSimple should not be called in this test"); },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("installGlobalProviderPolicy", () => {
|
describe("installGlobalProviderPolicy", () => {
|
||||||
it("swallows registrations and records each rejection", async () => {
|
it("rejects non-allowed registrations and records each rejection", async () => {
|
||||||
const { runtime, rejections } = await policyRuntime();
|
const { runtime, rejections } = await policyRuntime();
|
||||||
|
|
||||||
runtime.registerProvider("acme", { baseUrl: "https://acme.example.com" });
|
runtime.registerProvider("acme", { baseUrl: "https://acme.example.com" });
|
||||||
@@ -28,11 +47,37 @@ describe("installGlobalProviderPolicy", () => {
|
|||||||
expect(runtime.getRegisteredProviderConfig("acme")).toBeUndefined();
|
expect(runtime.getRegisteredProviderConfig("acme")).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("makes unregisterProvider a no-op that cannot remove global providers", async () => {
|
it("lets allowed (global-extension) providers through to the runtime", async () => {
|
||||||
const { runtime, rejections } = await policyRuntime();
|
const { runtime, rejections } = await policyRuntime(new Set(["tensorx"]));
|
||||||
|
|
||||||
|
runtime.registerProvider("tensorx", { baseUrl: "https://tensorx.example.com" });
|
||||||
|
runtime.registerProvider("acme", { baseUrl: "https://acme.example.com" });
|
||||||
|
|
||||||
|
expect(rejections).toEqual(["acme"]);
|
||||||
|
expect(runtime.getRegisteredProviderIds()).toEqual(["tensorx"]);
|
||||||
|
expect(runtime.getRegisteredProviderConfig("tensorx")).toEqual({ baseUrl: "https://tensorx.example.com" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("applies the same allow rule to native provider registrations", async () => {
|
||||||
|
const { runtime, rejections } = await policyRuntime(new Set(["native-global"]));
|
||||||
|
|
||||||
|
runtime.registerNativeProvider(nativeProvider("native-global"));
|
||||||
|
runtime.registerNativeProvider(nativeProvider("native-project"));
|
||||||
|
|
||||||
|
expect(rejections).toEqual(["native-project"]);
|
||||||
|
expect(runtime.getRegisteredProviderIds()).toEqual(["native-global"]);
|
||||||
|
expect(runtime.getRegisteredNativeProvider("native-global")).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unregisters only allowed providers; other unregisters are a no-op", async () => {
|
||||||
|
const { runtime, rejections } = await policyRuntime(new Set(["tensorx"]));
|
||||||
|
runtime.registerProvider("tensorx", { baseUrl: "https://tensorx.example.com" });
|
||||||
|
|
||||||
runtime.unregisterProvider("acme");
|
runtime.unregisterProvider("acme");
|
||||||
runtime.unregisterProvider(TEST_MODEL_PROVIDER);
|
runtime.unregisterProvider(TEST_MODEL_PROVIDER);
|
||||||
|
expect(runtime.getModel(TEST_MODEL_PROVIDER, TEST_MODEL_ID)).toBeDefined();
|
||||||
|
|
||||||
|
runtime.unregisterProvider("tensorx");
|
||||||
|
|
||||||
expect(rejections).toEqual([]);
|
expect(rejections).toEqual([]);
|
||||||
expect(runtime.getRegisteredProviderIds()).toEqual([]);
|
expect(runtime.getRegisteredProviderIds()).toEqual([]);
|
||||||
@@ -56,7 +101,8 @@ describe("providerRejectionMessage", () => {
|
|||||||
|
|
||||||
expect(message).toContain('Provider "acme"');
|
expect(message).toContain('Provider "acme"');
|
||||||
expect(message).toContain("in /workspace/project");
|
expect(message).toContain("in /workspace/project");
|
||||||
expect(message).toContain("PI WEB only supports globally configured providers");
|
expect(message).toContain("PI WEB providers must come from global configuration");
|
||||||
|
expect(message).toContain("globally installed extension");
|
||||||
expect(message).toContain("All other extension features are unaffected.");
|
expect(message).toContain("All other extension features are unaffected.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,101 @@
|
|||||||
import type { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
import { mkdtemp, rm } from "node:fs/promises";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { createAgentSessionServices, type ModelRuntime } from "@earendil-works/pi-coding-agent";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PI WEB supports only globally configured providers: Pi built-ins, agent-dir
|
* PI WEB providers come from global sources only: Pi built-ins, agent-dir
|
||||||
* `models.json`, and environment credentials. Any provider an extension tries
|
* `models.json`, environment credentials, and providers registered by global
|
||||||
* to register is rejected — the user is told, and everything else the
|
* (agent-dir) extensions. Provider registrations from project extensions are
|
||||||
* extension does keeps working.
|
* rejected — the user is told, and everything else the extension does keeps
|
||||||
|
* working.
|
||||||
*
|
*
|
||||||
* Why: all sessions share one daemon-wide {@link ModelRuntime}. Letting one
|
* Why: all sessions share one daemon-wide {@link ModelRuntime}. Global
|
||||||
* workspace's extensions mutate it corrupts the provider set of every other
|
* extensions are identical for every session, so their providers are
|
||||||
* concurrent session (issue #76). Rather than building scoped-provider
|
* daemon-wide consistent; project extensions differ per workspace, and letting
|
||||||
* isolation, pi-web rejects scoped registrations outright.
|
* them mutate the shared runtime corrupts the provider set of every other
|
||||||
|
* concurrent session (issue #76).
|
||||||
*
|
*
|
||||||
* Mechanism: this deliberately shadows the `registerProvider` /
|
* Mechanism: this deliberately shadows the `registerProvider` /
|
||||||
* `unregisterProvider` instance methods because Pi 0.80.10 offers no
|
* `registerNativeProvider` / `unregisterProvider` instance methods because Pi
|
||||||
* registration hook. Both Pi call sites (the load-time
|
* 0.81.1 offers no registration hook with extension attribution (the internal
|
||||||
* `pendingProviderRegistrations` drain in `createAgentSessionServices` and the
|
* drain, the bind-time flush, and the late `pi.registerProvider` path all
|
||||||
* late `pi.registerProvider` path through `ModelRegistry`) reach the runtime
|
* reach the runtime through call-time property lookup with the extension path
|
||||||
* through call-time property lookup, so instance shadowing intercepts them
|
* already dropped, so instance shadowing intercepts them identically). The
|
||||||
* identically. The acceptance test that exercises both paths is the tripwire:
|
* acceptance suite that exercises the load-time and late paths is the
|
||||||
* if a Pi upgrade changes these internals, that test fails loudly and this
|
* tripwire: if a Pi upgrade changes these internals, those tests fail loudly
|
||||||
* shim must be revisited.
|
* and this shim must be revisited.
|
||||||
|
*
|
||||||
|
* Attribution: Pi drops the registering extension's path before calls reach
|
||||||
|
* the runtime, so the shim cannot tell global from project extensions per
|
||||||
|
* call. Instead the daemon learns once, at startup, which provider ids global
|
||||||
|
* extensions register ({@link learnGlobalExtensionProviderIds}) and allows
|
||||||
|
* exactly those. This is not a security boundary — extensions run in-process
|
||||||
|
* with full trust, and a project extension re-registering an allowed id would
|
||||||
|
* pass. It is a guard against accidental cross-workspace leakage.
|
||||||
*/
|
*/
|
||||||
export function installGlobalProviderPolicy(
|
export function installGlobalProviderPolicy(
|
||||||
runtime: ModelRuntime,
|
runtime: ModelRuntime,
|
||||||
|
allowedExtensionProviderIds: ReadonlySet<string>,
|
||||||
onRejection: (providerId: string) => void,
|
onRejection: (providerId: string) => void,
|
||||||
): void {
|
): void {
|
||||||
runtime.registerProvider = (providerId: string) => {
|
const registerProvider = runtime.registerProvider.bind(runtime);
|
||||||
|
const registerNativeProvider = runtime.registerNativeProvider.bind(runtime);
|
||||||
|
const unregisterProvider = runtime.unregisterProvider.bind(runtime);
|
||||||
|
|
||||||
|
runtime.registerProvider = (providerId, config) => {
|
||||||
|
if (allowedExtensionProviderIds.has(providerId)) {
|
||||||
|
registerProvider(providerId, config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Swallow: the shared runtime is never mutated, the rejection is surfaced.
|
// Swallow: the shared runtime is never mutated, the rejection is surfaced.
|
||||||
onRejection(providerId);
|
onRejection(providerId);
|
||||||
};
|
};
|
||||||
runtime.unregisterProvider = () => {
|
runtime.registerNativeProvider = (provider) => {
|
||||||
// No-op: with every registration rejected, the extension provider layer is
|
if (allowedExtensionProviderIds.has(provider.id)) {
|
||||||
// always empty, so there is never anything to unregister.
|
registerNativeProvider(provider);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onRejection(provider.id);
|
||||||
};
|
};
|
||||||
|
runtime.unregisterProvider = (providerId) => {
|
||||||
|
if (allowedExtensionProviderIds.has(providerId)) {
|
||||||
|
unregisterProvider(providerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// No-op: rejected registrations never reached the runtime, so there is
|
||||||
|
// never anything of theirs to unregister.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Learn which provider ids the agent directory's global extensions register.
|
||||||
|
*
|
||||||
|
* Loads extensions once for a guaranteed-empty temporary cwd — so only global
|
||||||
|
* (agent-dir) extensions load — against the daemon's shared runtime, and
|
||||||
|
* returns the ids that appeared. Those registrations stay on the runtime: they
|
||||||
|
* are the daemon baseline, re-registered identically on every session load.
|
||||||
|
* Must run before {@link installGlobalProviderPolicy} is installed.
|
||||||
|
*
|
||||||
|
* Caveat: global extension code runs one extra time at daemon startup. Any
|
||||||
|
* commands/tools it registers land on the discarded scratch loader.
|
||||||
|
*/
|
||||||
|
export async function learnGlobalExtensionProviderIds(
|
||||||
|
runtime: ModelRuntime,
|
||||||
|
agentDir: string,
|
||||||
|
): Promise<ReadonlySet<string>> {
|
||||||
|
const before = new Set(runtime.getRegisteredProviderIds());
|
||||||
|
const scratchCwd = await mkdtemp(join(tmpdir(), "pi-web-global-ext-"));
|
||||||
|
try {
|
||||||
|
await createAgentSessionServices({ cwd: scratchCwd, agentDir, modelRuntime: runtime });
|
||||||
|
} finally {
|
||||||
|
await rm(scratchCwd, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
const learned = new Set<string>();
|
||||||
|
for (const providerId of runtime.getRegisteredProviderIds()) {
|
||||||
|
if (!before.has(providerId)) learned.add(providerId);
|
||||||
|
}
|
||||||
|
return learned;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,6 +105,6 @@ export function installGlobalProviderPolicy(
|
|||||||
*/
|
*/
|
||||||
export function providerRejectionMessage(providerId: string, cwd?: string): string {
|
export function providerRejectionMessage(providerId: string, cwd?: string): string {
|
||||||
const origin = cwd === undefined ? "registered by an extension" : `registered by an extension in ${cwd}`;
|
const origin = cwd === undefined ? "registered by an extension" : `registered by an extension in ${cwd}`;
|
||||||
return `Provider "${providerId}" ${origin} was ignored — PI WEB only supports globally configured providers. `
|
return `Provider "${providerId}" ${origin} was ignored — PI WEB providers must come from global configuration `
|
||||||
+ "Configure it globally (e.g. agent-dir models.json) to use it here. All other extension features are unaffected.";
|
+ "(agent-dir models.json) or a globally installed extension. All other extension features are unaffected.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ describe("PiSessionService prompt, queue, and auth warnings", () => {
|
|||||||
return stream;
|
return stream;
|
||||||
};
|
};
|
||||||
const hub = new CapturingSessionEventHub();
|
const hub = new CapturingSessionEventHub();
|
||||||
const fake = fakeRuntime("name-session", { model, agent: { streamFn } });
|
const fake = fakeRuntime("name-session", { model, agent: { streamFunction: streamFn } });
|
||||||
const service = new PiSessionService(hub, {
|
const service = new PiSessionService(hub, {
|
||||||
agentDir: TEST_AGENT_DIR,
|
agentDir: TEST_AGENT_DIR,
|
||||||
modelRuntime: testModelRuntime,
|
modelRuntime: testModelRuntime,
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ export function fakeRuntime(sessionId = "session-1", patch: Partial<TestSession>
|
|||||||
setSessionName: (name: string) => { session.sessionName = name; },
|
setSessionName: (name: string) => { session.sessionName = name; },
|
||||||
compact: () => Promise.resolve({ summary: "", tokensBefore: 0 }),
|
compact: () => Promise.resolve({ summary: "", tokensBefore: 0 }),
|
||||||
getUserMessagesForForking: () => [],
|
getUserMessagesForForking: () => [],
|
||||||
agent: { streamFn: () => { throw new Error("streamFn should not be called in this test"); } },
|
agent: { streamFunction: () => { throw new Error("streamFunction should not be called in this test"); } },
|
||||||
...patch,
|
...patch,
|
||||||
};
|
};
|
||||||
const runtime: PiSessionRuntime = {
|
const runtime: PiSessionRuntime = {
|
||||||
|
|||||||
@@ -346,13 +346,13 @@ export interface PiAgentSession {
|
|||||||
setSessionName(name: string): void;
|
setSessionName(name: string): void;
|
||||||
/**
|
/**
|
||||||
* Narrow re-expression of `AgentSession.agent` (an `@earendil-works/pi-agent-core`
|
* Narrow re-expression of `AgentSession.agent` (an `@earendil-works/pi-agent-core`
|
||||||
* `Agent`), exposing only `streamFn` — the resolved-auth/headers/retry "call this
|
* `Agent`), exposing only `streamFunction` — the resolved-auth/headers/retry "call
|
||||||
* model" function pi's own compaction/branch-summarization code uses internally.
|
* this model" function pi's own compaction/branch-summarization code uses
|
||||||
* Lets callers (e.g. session title generation) issue one-off model calls without
|
* internally. Lets callers (e.g. session title generation) issue one-off model
|
||||||
* depending on pi-ai's deprecated `/compat` provider registry or leaking the full
|
* calls without depending on pi-ai's deprecated `/compat` provider registry or
|
||||||
* `Agent`/`AgentSession` surface.
|
* leaking the full `Agent`/`AgentSession` surface.
|
||||||
*/
|
*/
|
||||||
agent: { streamFn: StreamFn };
|
agent: { streamFunction: StreamFn };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PiSessionRuntime {
|
export interface PiSessionRuntime {
|
||||||
@@ -2761,7 +2761,7 @@ export class PiSessionService implements SessionRouteService {
|
|||||||
const model = session.model;
|
const model = session.model;
|
||||||
if (model === undefined) return;
|
if (model === undefined) return;
|
||||||
|
|
||||||
void generateShortSessionName(session.agent.streamFn, model, firstMessage).then((name) => {
|
void generateShortSessionName(session.agent.streamFunction, model, firstMessage).then((name) => {
|
||||||
this.applyGeneratedSessionName(session, name ?? fallbackSessionName(firstMessage));
|
this.applyGeneratedSessionName(session, name ?? fallbackSessionName(firstMessage));
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
this.applyGeneratedSessionName(session, fallbackSessionName(firstMessage));
|
this.applyGeneratedSessionName(session, fallbackSessionName(firstMessage));
|
||||||
|
|||||||
Reference in New Issue
Block a user