@jmfederico/pi-web 1.202607.2 → 1.202608.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/client/assets/{CodeViewer-B4M13447.js → CodeViewer-D91Sp61M.js} +1 -1
- package/dist/client/assets/{TerminalPanel-Bmvj-Ecn.js → TerminalPanel-fYrKc3jh.js} +2 -2
- package/dist/client/assets/{UnifiedDiffViewer-C0hygwRo.js → UnifiedDiffViewer-BafLWGtF.js} +1 -1
- package/dist/client/assets/index-CA8q9_o7.js +4025 -0
- package/dist/client/index.html +1 -1
- package/dist/config.js +39 -0
- package/dist/config.js.map +1 -1
- package/dist/pi-web-plugins/info/infoInternals.js +208 -0
- package/dist/pi-web-plugins/info/pi-web-plugin.js +12 -15
- package/dist/pi-web-plugins/relays/markdownDocument.js +81 -0
- package/dist/pi-web-plugins/relays/package.json +9 -0
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +44 -0
- package/dist/pi-web-plugins/relays/relayDiscovery.js +228 -0
- package/dist/pi-web-plugins/relays/relaysPanelElement.js +550 -0
- package/dist/pi-web-plugins/relays/vendor/README.md +24 -0
- package/dist/pi-web-plugins/relays/vendor/marked.esm.js +76 -0
- package/dist/plugin-api.d.ts +7 -1
- package/dist/server/app.js +0 -22
- package/dist/server/app.js.map +1 -1
- package/dist/server/browserMessageProjection.js +0 -2
- package/dist/server/browserMessageProjection.js.map +1 -1
- package/dist/server/configRoutes.js +13 -11
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +2 -13
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/piWebStatus.js +12 -38
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
- package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
- package/dist/server/sessiond/agentProcessEnvironment.js +62 -0
- package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
- package/dist/server/sessiond/sessionServiceDependencies.js +33 -0
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -0
- package/dist/server/sessiond.js +145 -116
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserTool.js +102 -0
- package/dist/server/sessions/askUserTool.js.map +1 -0
- package/dist/server/sessions/authRoutes.js +0 -10
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +0 -24
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/extensionDialogWaiters.js +76 -0
- package/dist/server/sessions/extensionDialogWaiters.js.map +1 -0
- package/dist/server/sessions/globalProviderPolicy.js +79 -10
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
- package/dist/server/sessions/messagePaging.js +2 -2
- package/dist/server/sessions/messagePaging.js.map +1 -1
- package/dist/server/sessions/modelCatalogRefresher.js +8 -0
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +2 -5
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/parentSessionLocator.js +75 -0
- package/dist/server/sessions/parentSessionLocator.js.map +1 -0
- package/dist/server/sessions/pendingAskStore.js +275 -0
- package/dist/server/sessions/pendingAskStore.js.map +1 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js +196 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +182 -2
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +721 -203
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionFileHeader.js +117 -0
- package/dist/server/sessions/sessionFileHeader.js.map +1 -0
- package/dist/server/sessions/sessionRoutes.js +169 -54
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +619 -0
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
- package/dist/server/sessions/spawnSessionTool.js +8 -1
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +7 -1
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +3 -16
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -1
- package/dist/server/workspaces/effectivePathAccess.js +0 -19
- package/dist/server/workspaces/effectivePathAccess.js.map +1 -1
- package/dist/server/workspaces/gitWorktreeDiscovery.js +9 -0
- package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -1
- package/dist/server/workspaces/projectWorkspaceCwds.js +22 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -0
- package/dist/server/workspaces/workspaceDeletionRoutes.js +30 -4
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
- package/dist/server/workspaces/workspaceService.js +15 -2
- package/dist/server/workspaces/workspaceService.js.map +1 -1
- package/dist/sessiond/config.js +2 -2
- package/dist/sessiond/config.js.map +1 -1
- package/dist/shared/activity.js +9 -1
- package/dist/shared/activity.js.map +1 -1
- package/dist/shared/apiTypes.d.ts +300 -24
- package/dist/shared/apiTypes.js +30 -15
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +10 -41
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +4 -3
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/docs/config.md +115 -11
- package/docs/plugins.md +82 -14
- package/package.json +11 -9
- package/dist/client/assets/index-TvPsRyS1.js +0 -3545
- package/dist/server/sessiond/sessionDaemonStartup.js +0 -49
- package/dist/server/sessiond/sessionDaemonStartup.js.map +0 -1
- package/dist/server/sessions/sessionArchiveMigration.js +0 -590
- package/dist/server/sessions/sessionArchiveMigration.js.map +0 -1
package/docs/config.md
CHANGED
|
@@ -11,7 +11,7 @@ PI WEB uses two config files:
|
|
|
11
11
|
- **Global PI WEB config:** `$PI_WEB_CONFIG`, or `$XDG_CONFIG_HOME/pi-web/config.json`, or `~/.config/pi-web/config.json`.
|
|
12
12
|
- **Project-local PI WEB config:** `<project>/.pi-web/config.json` for commit-able project settings.
|
|
13
13
|
|
|
14
|
-
Each PI WEB machine has its own config. When using Fleet/machine federation, Settings uses the selected machine for config that affects work running there: the Pi-compatible agent profile and companion CLI, session daemon tools, PI WEB plugin enablement, external path access, and upload defaults. Gateway/browser-only settings stay local to the gateway: keyboard shortcuts, remote machine registry/tokens, and gateway host/port/allowed-hosts.
|
|
14
|
+
Each PI WEB machine has its own config. When using Fleet/machine federation, Settings uses the selected machine for config that affects work running there: the Pi-compatible agent profile and companion CLI, session daemon tools, PI WEB plugin enablement, external path access, and upload defaults. Gateway/browser-only settings stay local to the gateway: keyboard shortcuts, remote machine registry/tokens, and gateway host/port/allowed-hosts.
|
|
15
15
|
|
|
16
16
|
Pi package settings are separate from PI WEB config. They live in Pi's package-manager settings on the target machine and are managed by Pi (`pi install`, `pi remove`, `pi update`) or **Settings → Pi packages**. In a federated setup, **Settings → Pi packages** targets the currently selected machine. The PI WEB `plugins` config key only enables or disables discovered PI WEB browser plugins on the machine whose config you are editing; it does not install, remove, or update Pi packages.
|
|
17
17
|
|
|
@@ -33,17 +33,17 @@ defaults → global config file → environment overrides
|
|
|
33
33
|
|
|
34
34
|
Supported project-local settings are then applied for that project's workspaces. For upload defaults, `<project>/.pi-web/config.json` overrides the global value.
|
|
35
35
|
|
|
36
|
-
Environment overrides include `PI_WEB_HOST`, `PI_WEB_PORT` / `PORT`, `PI_WEB_ALLOWED_HOSTS`, `PI_WEB_MAX_UPLOAD_BYTES`, `PI_WEB_AGENT_COMMAND`, `PI_WEB_AGENT_DIR`, `PI_WEB_AGENT_SESSION_DIR`, `PI_CODING_AGENT_DIR` / `PI_CODING_AGENT_SESSION_DIR` for Pi compatibility, `PI_WEB_SPAWN_SESSIONS`, and `
|
|
36
|
+
Environment overrides include `PI_WEB_HOST`, `PI_WEB_PORT` / `PORT`, `PI_WEB_ALLOWED_HOSTS`, `PI_WEB_MAX_UPLOAD_BYTES`, `PI_WEB_AGENT_COMMAND`, `PI_WEB_AGENT_DIR`, `PI_WEB_AGENT_SESSION_DIR`, `PI_CODING_AGENT_DIR` / `PI_CODING_AGENT_SESSION_DIR` for Pi compatibility, `PI_WEB_SPAWN_SESSIONS`, `PI_WEB_SUBSESSIONS`, and `PI_WEB_ASK_USER`.
|
|
37
37
|
|
|
38
38
|
Process restarts depend on the key:
|
|
39
39
|
|
|
40
40
|
- `host` / `port`: restart the gateway web/API service or process.
|
|
41
41
|
- `maxUploadBytes`: restart both the web/API process and the session daemon on that machine.
|
|
42
|
-
- `agent.command` / `agent.dir` / `spawnSessions` / `subsessions`: restart the session daemon on that machine.
|
|
42
|
+
- `agent.command` / `agent.dir` / `spawnSessions` / `subsessions` / `askUser` / `extensionDialogsTimeoutMs`: restart the session daemon on that machine.
|
|
43
43
|
- `pathAccess`: applies on the next request; existing file views may need a browser refresh.
|
|
44
44
|
- `uploads.defaultFolder`: applies to newly opened Files upload dialogs and new direct drag/drop batches after config/workspace refresh.
|
|
45
45
|
- `plugins`: reload the browser tab after changing PI WEB plugin enablement.
|
|
46
|
-
- Pi package install/remove/update: not a PI WEB config key; after a mutation, type `/reload` in each idle PI WEB session on the target machine to refresh ordinary Pi resources such as extensions, skills, prompt templates, themes, and context/system prompt files. Reload the browser page separately for PI WEB browser plugin changes. If a global Pi extension adds
|
|
46
|
+
- Pi package install/remove/update: not a PI WEB config key; after a mutation, type `/reload` in each idle PI WEB session on the target machine to refresh ordinary Pi resources such as extensions, skills, prompt templates, themes, and context/system prompt files. Reload the browser page separately for PI WEB browser plugin changes. If a global Pi extension adds or removes a provider, or changes a provider's connection settings, manually restart `pi-web-sessiond.service`; `/reload` cannot change the startup provider baseline. A known provider refreshing only its own model list is applied without a restart. See [Pi extension provider baseline](#pi-extension-provider-baseline).
|
|
47
47
|
- `shortcuts`: saved settings apply in the browser after config refresh/save.
|
|
48
48
|
|
|
49
49
|
## Global config example
|
|
@@ -65,6 +65,8 @@ Process restarts depend on the key:
|
|
|
65
65
|
},
|
|
66
66
|
"spawnSessions": true,
|
|
67
67
|
"subsessions": false,
|
|
68
|
+
"askUser": true,
|
|
69
|
+
"extensionDialogsTimeoutMs": 300000,
|
|
68
70
|
"plugins": {
|
|
69
71
|
"workspace-tasks": { "enabled": true },
|
|
70
72
|
"updates": { "enabled": true },
|
|
@@ -95,13 +97,54 @@ Project-local config lives at `<project>/.pi-web/config.json`. Use it for settin
|
|
|
95
97
|
|
|
96
98
|
Project-local `pathAccess.allowedPaths` entries are merged after the global list and deduplicated. Paths must still be host-absolute or `~`-prefixed; relative roots are not supported.
|
|
97
99
|
|
|
98
|
-
Project-local `uploads.defaultFolder` overrides the global upload destination for workspaces in that project.
|
|
100
|
+
Project-local `uploads.defaultFolder` overrides the global upload destination for workspaces in that project. PI WEB servers always include this workspace-effective value on the workspace responses used locally and through machine federation.
|
|
99
101
|
|
|
100
102
|
Plugins may own separate project files, such as `.pi-web/tasks.json` for the built-in Workspace Tasks plugin.
|
|
101
103
|
|
|
104
|
+
PI WEB also honors one optional project hook; see [Worktree pre-remove hook](#worktree-pre-remove-hook).
|
|
105
|
+
|
|
106
|
+
## Worktree pre-remove hook
|
|
107
|
+
|
|
108
|
+
Before PI WEB deletes a workspace (a secondary Git worktree), it gives the repository one chance to tear down project-owned infrastructure tied to that worktree. To use the hook, provide an executable script at:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
.pi-web/hooks/worktree-pre-remove
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
relative to the workspace where the deletion command runs. PI WEB runs the deletion command from the project's main workspace when it exists, so commit the hook there and it follows the repository.
|
|
115
|
+
|
|
116
|
+
When the hook is present and executable, PI WEB dispatches the hook and the removal as one composed terminal command:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
'<hook path>' '<worktree path>' && git worktree remove '<worktree path>'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Contract:
|
|
123
|
+
|
|
124
|
+
- **Arguments:** exactly one — the absolute path of the worktree being deleted.
|
|
125
|
+
- **Working directory:** the workspace the deletion command runs in, not the worktree being deleted.
|
|
126
|
+
- **Exit codes:** `0` lets the removal proceed; any non-zero exit blocks it. The `&&` chain is the fail-closed guarantee — a failing hook keeps the worktree on disk.
|
|
127
|
+
- **Absent hook:** a missing file, or a file without the executable bit (for example after a checkout that lost it), is treated as no hook; PI WEB then runs a plain `git worktree remove`.
|
|
128
|
+
|
|
129
|
+
The composed command is dispatched like any other workspace deletion — same `Delete workspace: <branch>` terminal title — so hook output and failures are visible in the terminal run. If PI WEB cannot probe the hook path because of an unexpected filesystem error, the deletion request fails before any workspace terminals are closed.
|
|
130
|
+
|
|
131
|
+
Example: a hook that stops and removes local dev containers that bind-mount the worktree, so deletion does not leave stale containers behind. The hook is an opaque extension point — the contract does not assume any specific tooling, so use whatever the repository standardizes on:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
#!/bin/sh
|
|
135
|
+
# .pi-web/hooks/worktree-pre-remove
|
|
136
|
+
set -eu
|
|
137
|
+
|
|
138
|
+
worktree_path="$1"
|
|
139
|
+
|
|
140
|
+
# Stop/remove local dev containers bind-mounting "$worktree_path",
|
|
141
|
+
# release other per-worktree resources, etc.
|
|
142
|
+
# Exit non-zero to block the worktree removal.
|
|
143
|
+
```
|
|
144
|
+
|
|
102
145
|
## Configuration matrix
|
|
103
146
|
|
|
104
|
-
Rows with JSON key `—` are runtime-only environment variables, not config-file keys. `Global` means machine-global. In Settings, selected-machine-safe global keys (`pathAccess`, `uploads`, `maxUploadBytes`, `agent`, `spawnSessions`, `subsessions`, and `plugins`) are edited for the selected machine; gateway host/port/allowed-hosts, keyboard shortcuts, and machine registry/tokens stay local.
|
|
147
|
+
Rows with JSON key `—` are runtime-only environment variables, not config-file keys. `Global` means machine-global. In Settings, selected-machine-safe global keys (`pathAccess`, `uploads`, `maxUploadBytes`, `agent`, `spawnSessions`, `subsessions`, `askUser`, and `plugins`) are edited for the selected machine; gateway host/port/allowed-hosts, keyboard shortcuts, and machine registry/tokens stay local.
|
|
105
148
|
|
|
106
149
|
| Config | JSON key | Env var | Scope | Project-local behavior | Applies / restart |
|
|
107
150
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -116,6 +159,8 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
|
|
|
116
159
|
| Agent profile state directory | `agent.dir` | `PI_WEB_AGENT_DIR` (`PI_CODING_AGENT_DIR` for Pi compatibility) | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects auth, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugins |
|
|
117
160
|
| Agent can spawn sessions | `spawnSessions` | `PI_WEB_SPAWN_SESSIONS` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
|
|
118
161
|
| Tracked subsessions (beta) | `subsessions` | `PI_WEB_SUBSESSIONS` | Global/session daemon | Not supported locally; also requires `spawnSessions` | Restart session daemon on that machine |
|
|
162
|
+
| Agent can post question forms | `askUser` | `PI_WEB_ASK_USER` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
|
|
163
|
+
| Extension dialog auto-cancel timeout | `extensionDialogsTimeoutMs` | — | Global/session daemon | Not supported locally | Restart session daemon on that machine |
|
|
119
164
|
| Plugin enablement/settings | `plugins.<id>.enabled`, `plugins.<id>.settings` | — | Global | Not core local config; plugins may read their own project files | Reload browser tab |
|
|
120
165
|
| Keyboard shortcuts | `shortcuts.<actionId>` | — | Global | Not supported locally | Applies after settings save/config refresh |
|
|
121
166
|
| Project config version | `version` | — | Project | Project-local only; must be `1` when present | Next project-config read |
|
|
@@ -139,8 +184,14 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
|
|
|
139
184
|
|
|
140
185
|
`PI_WEB_DATA_DIR` sets the root for PI WEB-managed runtime state and defaults to `~/.pi-web`. Unless a more specific path override is configured, PI WEB stores its project and machine registries, locally discovered plugins, default session-daemon socket, and session archives beneath this root.
|
|
141
186
|
|
|
187
|
+
Each data directory is independent: after pointing PI WEB at a new root, it starts there with empty registries and no session archives. To carry session archives over, stop PI WEB, then copy `archived-sessions.json` and the `archived-sessions/` directory from the old data directory into the new one before starting it again.
|
|
188
|
+
|
|
142
189
|
This setting does not change the PI WEB config file selected by `PI_WEB_CONFIG` or Pi-owned state such as the active session files selected by `PI_CODING_AGENT_SESSION_DIR`.
|
|
143
190
|
|
|
191
|
+
### Agent process environment
|
|
192
|
+
|
|
193
|
+
Agent shells, terminals, and spawned sessions do not inherit the session daemon's own configuration. When the daemon starts, it removes its `PI_WEB_*` configuration keys, `NODE_ENV`, `PORT`, and `PI_CODING_AGENT_SESSION_DIR` from the environment agent processes see, so development commands behave normally inside sessions — for example, `npm install` is not affected by a production `NODE_ENV` meant for the daemon, and a second PI WEB instance started from a session does not pick up the live daemon's data directory or socket. `PI_CODING_AGENT_DIR` and ordinary variables (`PATH`, `HOME`, proxy settings, and the like) remain visible. The daemon itself keeps using the values it captured at startup.
|
|
194
|
+
|
|
144
195
|
### External path access
|
|
145
196
|
|
|
146
197
|
`pathAccess.allowedPaths` grants PI WEB's file explorer and absolute `@` path completions access to specific filesystem roots outside the current workspace.
|
|
@@ -180,7 +231,7 @@ The value must be a non-empty workspace-relative folder. PI WEB normalizes repea
|
|
|
180
231
|
|
|
181
232
|
Manual uploads use the workspace file-write path: paths stay workspace-relative, parent folder creation is enabled by default, and overwrite is disabled by default. Direct drag/drop always keeps `overwrite` off; the review dialog lets you explicitly enable overwrite when needed. Browser-owned XHR progress is shown per batch/file, conflicts and errors stay visible in the upload progress UI, and the final file-write response is the source of truth.
|
|
182
233
|
|
|
183
|
-
For machine federation, Settings saves the global upload default on the selected machine.
|
|
234
|
+
For machine federation, Settings saves the global upload default on the selected machine. Remote PI WEB servers always return `workspace.effectiveConfig.uploads.defaultFolder` on the workspace-list response, and the Files panel uses it as the default upload destination.
|
|
184
235
|
|
|
185
236
|
The per-request size limit is still controlled by `maxUploadBytes` / `PI_WEB_MAX_UPLOAD_BYTES` on the machine serving the upload.
|
|
186
237
|
|
|
@@ -205,7 +256,7 @@ Environment variables take precedence over the config file. `PI_WEB_AGENT_COMMAN
|
|
|
205
256
|
|
|
206
257
|
The session daemon resolves the persisted desired values plus its environment once at startup. That secret-free active profile stays fixed for the daemon lifetime. **Settings → Session daemon** saves command and directory together as desired configuration and shows whether the profile is active, needs a restart, or cannot be compared. Until the daemon restarts, sessions, Pi package operations, Pi-package-backed PI WEB plugin discovery, status/install detection, and update planning continue to use the daemon-owned active profile; a web/API restart recovers that same active profile instead of applying the newly saved values.
|
|
207
258
|
|
|
208
|
-
If the session daemon cannot report a valid active profile, profile-dependent Pi package and PI WEB plugin operations report unavailable instead of falling back to independently resolved config. A package-managed update command is shown only when PI WEB can preserve the active profile with a recognized, safe Pi companion CLI; otherwise the command is omitted.
|
|
259
|
+
If the session daemon cannot report a valid active profile, profile-dependent Pi package and PI WEB plugin operations report unavailable instead of falling back to independently resolved config. A package-managed update command is shown only when PI WEB can preserve the active profile with a recognized, safe Pi companion CLI; otherwise the command is omitted. Restart the session daemon on the selected machine to establish the next active profile.
|
|
209
260
|
|
|
210
261
|
### Pi extension provider baseline
|
|
211
262
|
|
|
@@ -213,15 +264,34 @@ This policy applies to **Pi runtime extensions**, not PI WEB browser plugins. Pi
|
|
|
213
264
|
|
|
214
265
|
PI WEB shares one model runtime across all sessions. When the session daemon starts, before any project resources load, it initializes global Pi extensions from the active agent profile (`agent.dir`), including extensions supplied by globally configured Pi packages. Provider registrations made by synchronous or awaited asynchronous extension factories during this bootstrap join the shared baseline. PI WEB captures both config-form registrations (`pi.registerProvider("id", config)`) and native-provider registrations (`pi.registerProvider(provider)`), alongside Pi built-ins, environment credentials, and providers from the active agent directory's `models.json`.
|
|
215
266
|
|
|
216
|
-
After startup capture,
|
|
267
|
+
After startup capture, a provider's connection settings are fixed for the daemon lifetime. Later attempts to add a provider, replace an existing provider's configuration, register a native provider, or unregister a provider are no-ops, regardless of source or provider ID. This includes project extensions attempting to add or replace a provider, lifecycle callbacks such as `session_start`, and `/reload`. Non-provider Pi extension features continue to load and reload normally.
|
|
268
|
+
|
|
269
|
+
#### Model list refresh for a known provider
|
|
270
|
+
|
|
271
|
+
One narrow update is applied after startup: a provider captured in the baseline may refresh **its own model list**. Extensions that fetch an updated catalog typically re-send their complete provider configuration, so PI WEB compares the incoming registration against the recorded baseline and applies it only when both hold:
|
|
217
272
|
|
|
218
|
-
|
|
273
|
+
- the provider ID is already in the startup baseline, and
|
|
274
|
+
- every field except the model list is unchanged — `name`, `baseUrl`, `apiKey`, `api`, `streamSimple`, `headers`, `authHeader`, `oauth`, and `refreshModels`.
|
|
275
|
+
|
|
276
|
+
Anything else stays a no-op, including a provider that was not in the baseline and a known provider whose credentials, base URL, or API surface differ from startup. Function-valued fields cannot be compared by value, so a registration that supplies a new `streamSimple`, `refreshModels`, or `oauth` implementation is treated as a change and ignored.
|
|
277
|
+
|
|
278
|
+
An applied refresh becomes the new comparison point, so a provider can refresh repeatedly. Re-sending an unchanged model list is a replay rather than an update and is ignored. Refreshed models are visible to sessions immediately; no restart and no network request is involved, because the extension has already produced the catalog.
|
|
279
|
+
|
|
280
|
+
Model lists are shared daemon-wide state. If extensions in two workspaces register different model lists for the same provider ID, the last registration wins. A model entry may also carry its own `baseUrl` and `headers`, which take precedence over the provider-level values for that model, so an accepted refresh can change where requests for those models are sent. Both are accepted trade-offs: a catalog is treated as a property of the provider rather than of the project, and Pi extensions are trusted daemon code.
|
|
281
|
+
|
|
282
|
+
#### Provider decisions in the daemon log
|
|
283
|
+
|
|
284
|
+
Ignored mutations are written to the session-daemon log once per operation and provider ID, so a replaying extension cannot flood the log. Applied model list refreshes are logged every time, with the resulting model count, because each one changes shared runtime state. Neither entry contains provider configuration or credentials, and PI WEB does not show a session warning or notification.
|
|
285
|
+
|
|
286
|
+
This prevents accidental provider, configuration, or credential contamination between projects; it is not a security boundary because Pi extensions remain trusted daemon code.
|
|
219
287
|
|
|
220
288
|
Configure providers before the daemon starts: use the active agent directory's `models.json`, or install the Pi extension globally in that agent profile. Project Pi extensions and project-level `models.json` files cannot add providers to PI WEB's shared baseline. After updating PI WEB—or after installing, removing, or updating a global 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 baseline.
|
|
221
289
|
|
|
222
290
|
### Background model catalog refresh
|
|
223
291
|
|
|
224
|
-
PI WEB shares one model runtime across all sessions, and provider model catalogs are refreshed over the network only on the session daemon's own background schedule.
|
|
292
|
+
PI WEB shares one model runtime across all sessions, and provider model catalogs are refreshed over the network only on the session daemon's own background schedule. Requests never start a catalog fetch of their own, so a slow or unreachable provider cannot stall opening the model selector, starting a session, or the auth dialogs on its own account.
|
|
293
|
+
|
|
294
|
+
A refresh that is *already* in flight can still briefly delay starting or opening a session, because the shared runtime is read while that refresh is running. PI WEB says so while you wait: the session's activity line names the startup step it is on and adds `provider model lists are refreshing` when a background refresh is running at the same time. That note reports what is happening concurrently, not a proven cause.
|
|
225
295
|
|
|
226
296
|
The session daemon runs the refresh:
|
|
227
297
|
|
|
@@ -248,8 +318,34 @@ A completion notice wakes an idle parent or queues behind in-flight work. Each n
|
|
|
248
318
|
|
|
249
319
|
`list_subsessions`, `check_subsession`, and `read_subsession` never yield or change control flow. They are for deliberate inspection or recovery, not completion polling. While a child works, agent-facing `check_subsession` and `read_subsession` withhold partial output and direct the parent to continue independent work or yield at the join point. Output becomes available when the child stops. Included output and transcripts follow a labeled marker and come last, after PI WEB guidance.
|
|
250
320
|
|
|
321
|
+
Both `spawn_session` and `spawn_subsession` accept an optional `model` parameter, given as an exact `provider/model-id` such as `anthropic/claude-sonnet-4-5`. When set, the new session starts on that model instead of inheriting the dispatching session's model. The match is strict: an unknown or malformed value is rejected with an error. A `#provider/model-id` reference in the prompt (see [Prompt completions](#prompt-completions)) is how users ask for a specific model; agents forward that reference as this parameter. The new session also inherits the dispatching session's thinking level, clamped to its model's capabilities.
|
|
322
|
+
|
|
251
323
|
In **Settings → Session daemon**, these keys are saved on the selected machine. Restart the session daemon on that machine after changing them.
|
|
252
324
|
|
|
325
|
+
#### `askUser` and `ask_user`
|
|
326
|
+
|
|
327
|
+
`askUser` controls whether agents receive the core `ask_user` tool. It defaults to `true`; set it to `false`, or set `PI_WEB_ASK_USER=false`, to remove the tool. The environment override accepts `0|1|true|false` and takes precedence over the config file.
|
|
328
|
+
|
|
329
|
+
Use **Settings → Session daemon → Allow agents to ask questions** to change `askUser` on the selected machine. An environment override makes the toggle read-only.
|
|
330
|
+
|
|
331
|
+
The tool accepts one set of 1–20 questions. Each question has a unique `id`, its `question` text, optional supporting `detail`, up to 12 options with stable values and user-facing labels, and an optional `multiple` flag. The browser always adds a **Custom** free-text answer, including when the model supplies no options. No question is required: the user may leave any of them unanswered.
|
|
332
|
+
|
|
333
|
+
Calling `ask_user` posts the whole set as one browser form and ends the current agent run instead of waiting for the user. The open form is owned by the session daemon, so it survives a browser disconnect, browser reload, or web/API restart while that daemon keeps running. When the user submits, the answers arrive as a follow-up that wakes the session; each question is reported with its selected option values or free text, or explicitly as unanswered.
|
|
334
|
+
|
|
335
|
+
PI WEB confirms a partial submission before sending it and names the unanswered questions. Only one ask can be open per session: a later `ask_user` call supersedes the earlier one, reports that fact and its unanswered questions to the model, and turns the earlier card into a read-only transcript record. Submitted and cancelled asks likewise remain readable in the transcript.
|
|
336
|
+
|
|
337
|
+
Sending an ordinary chat message while a form is open voids the form: the card closes as cancelled and the model is told its questions went unanswered as part of the turn the message itself starts.
|
|
338
|
+
|
|
339
|
+
Restart the session daemon after changing `askUser` or after upgrading PI WEB to a version that introduces this tool. For the systemd user service, run `systemctl --user restart pi-web-sessiond`.
|
|
340
|
+
|
|
341
|
+
### Extension dialogs
|
|
342
|
+
|
|
343
|
+
Pi extensions can ask the user questions from `ctx.ui.confirm()`, `ctx.ui.select()`, and `ctx.ui.input()` — including from `session_start` hooks and in-flight `tool_call` hooks. PI WEB renders these dialogs inline in the session transcript and answers them through a dedicated session-daemon channel, never the prompt queue, so a dialog parked inside a `tool_call` hook cannot deadlock the run. Dialog support is always on; there is no enable flag. See [Pi extension dialogs in PI WEB](https://pi-web.dev/plugins#pi-extension-dialogs) for behavior details and author guidance.
|
|
344
|
+
|
|
345
|
+
`extensionDialogsTimeoutMs` is the unattended-dialog safety valve: how long the session daemon waits for an answer before settling the dialog with its kind's cancel value (`false` for confirm, `undefined` for select and input). It defaults to `300000` (5 minutes); set it to `0` to wait forever. An extension's own `timeout` option still applies, and the effective deadline is the sooner of the two.
|
|
346
|
+
|
|
347
|
+
The key is edited directly in the global config file. Restart the session daemon after changing it — for the systemd user service, run `systemctl --user restart pi-web-sessiond`.
|
|
348
|
+
|
|
253
349
|
### Plugin config
|
|
254
350
|
|
|
255
351
|
The `plugins` key is only for PI WEB browser plugin enablement/settings on the machine whose config you are editing. It does not install, remove, or update Pi packages; use **Settings → Pi packages** or Pi's package manager for package operations. In a federated setup, **Settings → PI WEB plugins** and **Settings → Pi packages** both target the currently selected machine, and each panel labels where changes will be saved or run.
|
|
@@ -282,6 +378,14 @@ Shortcut values are keyed by action id. Values are shortcut strings such as `mod
|
|
|
282
378
|
|
|
283
379
|
Prefer Settings → Keyboard for editing shortcuts interactively.
|
|
284
380
|
|
|
381
|
+
## Prompt completions
|
|
382
|
+
|
|
383
|
+
The chat composer opens completion menus on three trigger characters:
|
|
384
|
+
|
|
385
|
+
- `/` at the very start of the draft completes session commands.
|
|
386
|
+
- `@` completes file paths: `@` for tracked files, `@ ` (at, then space) or `!@` for all files. Picking one inserts an `@path` reference into the draft, quoted automatically when the path contains spaces.
|
|
387
|
+
- `#` completes the models available to the session, filtered case-insensitively as you type (at most 12 entries). Picking one inserts a `#provider/model-id` reference into the draft, which tells agents the request should run on that model — for example as the `model` parameter of `spawn_session`.
|
|
388
|
+
|
|
285
389
|
## Optional completion tools
|
|
286
390
|
|
|
287
391
|
File and path `@` completions work without extra tools. If `fzf` is available on the PI WEB server's `PATH`, PI WEB uses it to improve completion filtering/ranking; otherwise it falls back to built-in ranking.
|
package/docs/plugins.md
CHANGED
|
@@ -23,12 +23,26 @@ They do **not** run in the session daemon, do not get a server-side hook API, an
|
|
|
23
23
|
|
|
24
24
|
Use **Settings → Pi packages** to view configured Pi packages or install/remove/update a package. Enter only the package source, such as `npm:@scope/package`, a git/URL source, or a local path. PI WEB uses Pi's default package location, equivalent to `pi install <source>`, and does not ask for an install location.
|
|
25
25
|
|
|
26
|
-
When machine federation is enabled, **Settings → Pi packages** targets the currently selected machine. The panel labels whether changes will run on the local/gateway machine or on a selected remote PI WEB machine.
|
|
26
|
+
When machine federation is enabled, **Settings → Pi packages** targets the currently selected machine. The panel labels whether changes will run on the local/gateway machine or on a selected remote PI WEB machine.
|
|
27
27
|
|
|
28
|
-
Use **Settings → PI WEB plugins** to enable or disable discovered PI WEB browser plugins before the browser imports them. In a federated setup, this plugin enablement surface targets the currently selected machine and labels where changes are saved.
|
|
28
|
+
Use **Settings → PI WEB plugins** to enable or disable discovered PI WEB browser plugins before the browser imports them. In a federated setup, this plugin enablement surface targets the currently selected machine and labels where changes are saved.
|
|
29
29
|
|
|
30
30
|
After installing, removing, or updating a Pi package, type `/reload` in each idle PI WEB session on the target machine to refresh ordinary Pi resources such as extensions, skills, prompt templates, themes, and context/system prompt files. Reload the browser page separately for newly discovered or changed PI WEB browser plugins. A provider-registering Pi extension follows a separate daemon-start policy; see [Pi extension provider baseline](https://pi-web.dev/config#pi-extension-provider-baseline).
|
|
31
31
|
|
|
32
|
+
## Pi extension dialogs in PI WEB
|
|
33
|
+
|
|
34
|
+
Pi extensions running under PI WEB's session daemon can ask the user questions with `ctx.ui.confirm()`, `ctx.ui.select()`, and `ctx.ui.input()`. PI WEB reports `ctx.hasUI === true`, and for these three dialog methods that is true in fact: the call renders a dialog card inline in the session transcript and the returned Promise resolves with the user's actual answer — a boolean for confirm, the chosen option for select, the typed text for input.
|
|
35
|
+
|
|
36
|
+
- **Works from hooks, without the prompt queue.** Answers travel over a dedicated session-daemon channel, so a dialog opened inside an in-flight `tool_call` hook parks safely — the agent loop waits for the hook and the run continues with the answer. Consent-gating a tool from a `tool_call` hook is a supported pattern.
|
|
37
|
+
- **`session_start` dialogs are reachable.** A dialog opened from a `session_start` hook is answerable while the session is still starting, both when creating a session and when opening an existing one; startup completes once the dialog settles.
|
|
38
|
+
- **Survives browser reloads; first answer wins.** Reloading the browser re-renders open dialogs from the session status. With several tabs on the same session, the first answer settles the dialog and the other tabs re-render the settled card.
|
|
39
|
+
- **Settled cards stay until dismissed.** An answered or closed dialog leaves its outcome card in the transcript so the user can see what became of it — answers travel to the extension alone, so the card is the only record of the exchange. The card is browser-local: only a browser that saw the dialog open renders it, and switching sessions or reloading drops it.
|
|
40
|
+
- **Timeouts.** The extension's own `timeout` option applies, and the daemon adds an unattended-dialog safety valve, `extensionDialogsTimeoutMs` (default 5 minutes, `0` waits forever — see [Extension dialogs](https://pi-web.dev/config#extension-dialogs)). The effective deadline is the sooner of the two. A dialog that closes without an answer resolves with its kind's cancel value: `false` for confirm, `undefined` for select and input.
|
|
41
|
+
- **Abort and runtime replacement.** Aborting the current run settles a dialog opened during that run immediately, at abort-request time, with its cancel value. Replacing the session runtime (`/reload`, session disposal) settles any still-open dialog the same way; hooks on the new runtime open fresh dialogs. The extension's own `AbortSignal` is honored: aborting it dismisses the dialog and resolves with the cancel value.
|
|
42
|
+
- **Other UI surfaces are still no-ops.** `ExtensionUIContext` methods beyond the three dialogs (widgets, status, editor, `custom`) remain unimplemented under PI WEB even though `hasUI` is `true`; do not rely on `hasUI` alone to detect them.
|
|
43
|
+
|
|
44
|
+
One browser-local caveat: reloading the browser while a new session is still being created loses the browser-local pending-start row, so the dialog card disappears from view. The daemon-side dialog still settles at its deadline and the session appears in the sidebar once creation completes.
|
|
45
|
+
|
|
32
46
|
## Trust model
|
|
33
47
|
|
|
34
48
|
Plugins run as JavaScript in the browser app. Treat them as trusted code:
|
|
@@ -90,8 +104,11 @@ Source files:
|
|
|
90
104
|
```text
|
|
91
105
|
pi-web-plugins/info/package.json
|
|
92
106
|
pi-web-plugins/info/pi-web-plugin.ts
|
|
107
|
+
pi-web-plugins/info/infoInternals.ts
|
|
93
108
|
```
|
|
94
109
|
|
|
110
|
+
`pi-web-plugin.ts` is the plugin skeleton: metadata plus contribution definitions. `infoInternals.ts` holds everything the bundled panel and action actually render, so you can ignore or replace it when copying the plugin.
|
|
111
|
+
|
|
95
112
|
Built module:
|
|
96
113
|
|
|
97
114
|
```text
|
|
@@ -130,6 +147,8 @@ export default {
|
|
|
130
147
|
|
|
131
148
|
When copying the Info plugin, choose a new plugin id so it does not conflict with the bundled `info` plugin.
|
|
132
149
|
|
|
150
|
+
The Info panel doubles as an always-available PI WEB status view: it renders the host-provided `context.state.piWebStatus` (versions, installation, release state, machine, and workspace details) without issuing its own requests, and its action copies a plain-text diagnostics summary suitable for bug reports.
|
|
151
|
+
|
|
133
152
|
PI WEB also ships an `updates` plugin that demonstrates dynamic `visible` and `badge` callbacks for tabs that only appear when the host has status messages or needs extra install visibility.
|
|
134
153
|
|
|
135
154
|
## Local plugin usage
|
|
@@ -156,7 +175,7 @@ When [machine federation](https://pi-web.dev/machines) is enabled, PI WEB also l
|
|
|
156
175
|
- remote theme contributions are ignored for now because themes are app-wide;
|
|
157
176
|
- mixed PI WEB versions across federated machines are best-effort and not guaranteed compatible.
|
|
158
177
|
|
|
159
|
-
Remote plugin enablement is controlled by the remote machine's PI WEB plugin config. To edit or disable a remote machine plugin, select that machine and use **Settings → PI WEB plugins
|
|
178
|
+
Remote plugin enablement is controlled by the remote machine's PI WEB plugin config. To edit or disable a remote machine plugin, select that machine and use **Settings → PI WEB plugins**, or open that machine directly/update its config file.
|
|
160
179
|
|
|
161
180
|
Plugin package metadata may set `machineSpecific: true` when the plugin's meaning is tied to the selected PI WEB machine:
|
|
162
181
|
|
|
@@ -275,6 +294,27 @@ Task fields:
|
|
|
275
294
|
|
|
276
295
|
Review task configs before running them, especially in shared projects. Workspace Tasks runs trusted shell commands from your repositories.
|
|
277
296
|
|
|
297
|
+
### Relays
|
|
298
|
+
|
|
299
|
+
**Plugin id:** `relays`
|
|
300
|
+
**What it does:** adds a read-only **Relays** workspace tab for browsing the workspace's relays, plus an **Open Workspace Relays** action for the selected workspace that opens the same tab.
|
|
301
|
+
|
|
302
|
+
A relay is a directory of markdown notes under `.pi-web/relays/<name>/` in the workspace root — the convention used by the Relay method for chaining agent sessions. The tab lists each relay's documents with `status.md`, `charter.md`, and `log.md` first (in that order), followed by any other files alphabetically, and opens `status.md` by default. Markdown documents render as sanitized HTML; other files render as preformatted text, and binary files have no preview. Truncated documents show a notice, and **Refresh** re-scans the workspace and reloads the open document.
|
|
303
|
+
|
|
304
|
+
Documents in subfolders are listed too. Folders appear as chips in the document strip, and expanding one inserts its files inline right after it — accordion-style, so expanding a folder collapses its siblings on the same level. An expanded folder wraps its chip and documents in a group bubble, so nested entries stay visually contained. Collapsing the folder that holds the open document keeps the selection and highlights the folder instead. Relay trees deeper than five levels, larger than 200 documents, or with more than 50 folders are listed partially, with a notice.
|
|
305
|
+
|
|
306
|
+
With several relays, a picker pre-selects the most recently modified one; a single relay opens directly. A workspace without `.pi-web/relays/` shows an empty state explaining the convention. The tab never creates, edits, or deletes relay files.
|
|
307
|
+
|
|
308
|
+
Relays is enabled by default. To hide it, disable `relays` in **Settings → PI WEB plugins** or set:
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"plugins": {
|
|
313
|
+
"relays": { "enabled": false }
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
278
318
|
## Discovery and packaging
|
|
279
319
|
|
|
280
320
|
PI WEB builds the gateway `/pi-web-plugins/manifest.json` from these sources:
|
|
@@ -433,14 +473,13 @@ Actions appear in the action palette. They can inspect app state and call UI/run
|
|
|
433
473
|
```js
|
|
434
474
|
actions: [
|
|
435
475
|
{
|
|
436
|
-
id: "
|
|
437
|
-
title: "
|
|
438
|
-
description: "
|
|
439
|
-
shortcut: "mod+shift+p",
|
|
476
|
+
id: "copy-diagnostics",
|
|
477
|
+
title: "Copy PI WEB Diagnostics",
|
|
478
|
+
description: "Copy version, installation, and status details for this machine",
|
|
440
479
|
group: "Info",
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
480
|
+
run: async (context) => {
|
|
481
|
+
const version = context.state.piWebStatus?.components.web.runtimeVersion ?? "unknown";
|
|
482
|
+
await navigator.clipboard.writeText(`PI WEB ${version}`);
|
|
444
483
|
},
|
|
445
484
|
},
|
|
446
485
|
]
|
|
@@ -468,6 +507,7 @@ Stable runtime context fields:
|
|
|
468
507
|
```ts
|
|
469
508
|
interface PluginRuntimeContext {
|
|
470
509
|
state: {
|
|
510
|
+
selectedMachine?: PluginMachine;
|
|
471
511
|
selectedWorkspace?: Workspace;
|
|
472
512
|
selectedSession?: unknown;
|
|
473
513
|
piWebStatus?: PiWebStatusResponse;
|
|
@@ -492,7 +532,7 @@ interface PluginRuntimeContext {
|
|
|
492
532
|
Notes:
|
|
493
533
|
|
|
494
534
|
- `state` is a snapshot of current UI state when actions are built.
|
|
495
|
-
- The stable state fields are `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`. `state.piWebStatus` describes the currently selected machine's PI WEB runtime, or the gateway/local runtime when the local machine is selected.
|
|
535
|
+
- The stable state fields are `state.selectedMachine`, `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`. `state.selectedMachine` identifies the currently selected machine. `state.piWebStatus` describes the currently selected machine's PI WEB runtime, or the gateway/local runtime when the local machine is selected.
|
|
496
536
|
- Other `state` fields may exist at runtime, but they are private PI WEB internals that may graduate into stable helpers, change shape, or disappear.
|
|
497
537
|
- `enabled` is evaluated when the action palette asks for actions.
|
|
498
538
|
- `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
|
|
@@ -580,6 +620,7 @@ interface WorkspacePanelContext {
|
|
|
580
620
|
state?: PluginRuntimeState;
|
|
581
621
|
files: {
|
|
582
622
|
readFile(path: string): Promise<FileContentResponse>;
|
|
623
|
+
listFiles(path: string): Promise<FileTreeResponse>;
|
|
583
624
|
writeFile(path: string, content: string | Uint8Array, options?: WriteWorkspaceFileOptions): Promise<WriteWorkspaceFileResponse>;
|
|
584
625
|
deleteFile(path: string): Promise<DeleteWorkspaceFileResponse>;
|
|
585
626
|
moveFile(fromPath: string, toPath: string, options?: MoveWorkspaceFileOptions): Promise<MoveWorkspaceFileResponse>;
|
|
@@ -602,7 +643,7 @@ interface WorkspacePanelContext {
|
|
|
602
643
|
|
|
603
644
|
`icon` is optional and is used in the compact mobile tab bar. Prefer an SVG rendered with the `svg` helper from `PluginActivationContext`; use `currentColor` so PI WEB themes can style it. If `icon` is omitted, mobile tabs fall back to initials from the panel title, or to the full title when initials collide.
|
|
604
645
|
|
|
605
|
-
`machine`, `workspace`, `files`, `prompt`, `terminal`, and `host` are documented as stable for panel callbacks. The `files` helper supports `readFile`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files) and [Writing workspace files](#writing-workspace-files). The `prompt` helper supports panel interactions that insert workspace context into the current prompt — see [Prompt editor API](#prompt-editor-api). Use `terminal.open()` to switch to the built-in terminal panel; pass `{ terminalId }` to deep-link to a specific terminal. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate panel callbacks such as `badge`, `visible`, or `render`.
|
|
646
|
+
`machine`, `workspace`, `files`, `prompt`, `terminal`, and `host` are documented as stable for panel callbacks. The `files` helper supports `readFile`, `listFiles`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files), [Listing workspace files](#listing-workspace-files), and [Writing workspace files](#writing-workspace-files). The `prompt` helper supports panel interactions that insert workspace context into the current prompt — see [Prompt editor API](#prompt-editor-api). Use `terminal.open()` to switch to the built-in terminal panel; pass `{ terminalId }` to deep-link to a specific terminal. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate panel callbacks such as `badge`, `visible`, or `render`.
|
|
606
647
|
|
|
607
648
|
For compatibility, PI WEB still provides the old `context.openTerminal()` workspace-panel helper at runtime. It is deprecated, intentionally omitted from the public TypeScript declarations, and planned for removal in v2. Existing JavaScript plugins keep working, while typed plugins should migrate to `context.terminal.open()`.
|
|
608
649
|
|
|
@@ -670,6 +711,7 @@ interface WorkspaceLabelContext {
|
|
|
670
711
|
state?: PluginRuntimeState;
|
|
671
712
|
files: {
|
|
672
713
|
readFile(path: string): Promise<FileContentResponse>;
|
|
714
|
+
listFiles(path: string): Promise<FileTreeResponse>;
|
|
673
715
|
writeFile(path: string, content: string | Uint8Array, options?: WriteWorkspaceFileOptions): Promise<WriteWorkspaceFileResponse>;
|
|
674
716
|
deleteFile(path: string): Promise<DeleteWorkspaceFileResponse>;
|
|
675
717
|
moveFile(fromPath: string, toPath: string, options?: MoveWorkspaceFileOptions): Promise<MoveWorkspaceFileResponse>;
|
|
@@ -680,7 +722,7 @@ interface WorkspaceLabelContext {
|
|
|
680
722
|
}
|
|
681
723
|
```
|
|
682
724
|
|
|
683
|
-
`machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. The `files` helper supports `readFile`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files) and [Writing workspace files](#writing-workspace-files). Include `machine.id` in any label caches that depend on workspace data. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate label `visible` or `items` callbacks.
|
|
725
|
+
`machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. The `files` helper supports `readFile`, `listFiles`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files), [Listing workspace files](#listing-workspace-files), and [Writing workspace files](#writing-workspace-files). Include `machine.id` in any label caches that depend on workspace data. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate label `visible` or `items` callbacks.
|
|
684
726
|
|
|
685
727
|
Items are sorted by `order` and then id. Return an empty array to render nothing. Keep callbacks synchronous and lightweight; start async work from the callback, return cached items, then call `host.requestRender()` when the cache changes.
|
|
686
728
|
|
|
@@ -819,6 +861,32 @@ workspaceLabels: [
|
|
|
819
861
|
|
|
820
862
|
The file response includes fields such as `path`, `content`, `truncated`, and `binary`. Be careful with sensitive files such as `.env`: plugins are trusted browser code, and file contents are exposed to the plugin.
|
|
821
863
|
|
|
864
|
+
## Listing workspace files
|
|
865
|
+
|
|
866
|
+
`files.listFiles(path)` lists the entries of a workspace directory. Pass `""` for the workspace root. Like `readFile`, PI WEB binds the call to the callback's machine and workspace, so it works the same for local and federated machines.
|
|
867
|
+
|
|
868
|
+
```js
|
|
869
|
+
const listing = await context.files.listFiles("src");
|
|
870
|
+
for (const entry of listing.entries) {
|
|
871
|
+
// entry: { name, path, type: "file" | "directory" | "symlink", size?, modifiedAt? }
|
|
872
|
+
}
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
The listing response includes `path`, `entries`, `scannedAt`, and `truncated`. When `truncated` is true, the server cut the listing short, so treat the entries as partial.
|
|
876
|
+
|
|
877
|
+
`listFiles` rejects when the directory does not exist or cannot be read, matching `readFile` error behavior. When a directory is optional, catch the error and treat it as an empty listing:
|
|
878
|
+
|
|
879
|
+
```js
|
|
880
|
+
async function listSubdirectoryNames(context, path) {
|
|
881
|
+
try {
|
|
882
|
+
const listing = await context.files.listFiles(path);
|
|
883
|
+
return listing.entries.filter((entry) => entry.type === "directory").map((entry) => entry.name);
|
|
884
|
+
} catch {
|
|
885
|
+
return [];
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
```
|
|
889
|
+
|
|
822
890
|
## Writing, deleting, and moving workspace files
|
|
823
891
|
|
|
824
892
|
Workspace panels and workspace labels can write, delete, and move files through the documented `files` helper. Like `readFile`, PI WEB binds these helpers to the callback's machine and workspace, so they work the same for local and federated machines.
|
|
@@ -957,7 +1025,7 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
|
|
|
957
1025
|
9. Add workspace panels for larger workspace UI.
|
|
958
1026
|
10. Add workspace labels for compact inline metadata.
|
|
959
1027
|
11. Return arrays from workspace label `items()`; return an empty array to render nothing.
|
|
960
|
-
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebStatus`, and `prompt`.
|
|
1028
|
+
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedMachine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebStatus`, and `prompt`.
|
|
961
1029
|
13. Do not fetch PI WEB `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers.
|
|
962
1030
|
14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
|
|
963
1031
|
15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmfederico/pi-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.202608.0",
|
|
4
4
|
"description": "Web UI for persistent Pi Coding Agent sessions in real workspaces.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Federico Jaramillo Martinez",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@codemirror/state": "^6.7.1",
|
|
70
70
|
"@codemirror/view": "^6.43.6",
|
|
71
71
|
"@fastify/compress": "^9.0.0",
|
|
72
|
-
"@fastify/static": "^
|
|
72
|
+
"@fastify/static": "^10.1.2",
|
|
73
73
|
"@fastify/websocket": "^11.3.0",
|
|
74
74
|
"@xterm/addon-fit": "^0.11.0",
|
|
75
75
|
"@xterm/xterm": "^6.0.0",
|
|
@@ -78,19 +78,21 @@
|
|
|
78
78
|
"lit": "^3.3.3",
|
|
79
79
|
"marked": "^18.0.6",
|
|
80
80
|
"node-pty": "^1.1.0",
|
|
81
|
-
"typebox": "1.3.
|
|
81
|
+
"typebox": "1.3.7",
|
|
82
|
+
"undici": "^8.5.0",
|
|
82
83
|
"ws": "^8.21.0"
|
|
83
84
|
},
|
|
84
85
|
"devDependencies": {
|
|
85
86
|
"@changesets/cli": "^2.31.0",
|
|
86
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
87
|
-
"@earendil-works/pi-ai": "^0.
|
|
88
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
87
|
+
"@earendil-works/pi-agent-core": "^0.83.0",
|
|
88
|
+
"@earendil-works/pi-ai": "^0.83.0",
|
|
89
|
+
"@earendil-works/pi-coding-agent": "^0.83.0",
|
|
89
90
|
"@eslint/js": "^10.0.1",
|
|
90
91
|
"@types/node": "^24.13.3",
|
|
91
92
|
"@types/ws": "^8.18.1",
|
|
92
93
|
"eslint": "^10.6.0",
|
|
93
94
|
"globals": "^17.7.0",
|
|
95
|
+
"happy-dom": "^20.11.1",
|
|
94
96
|
"knip": "^6.25.0",
|
|
95
97
|
"tsx": "^4.23.0",
|
|
96
98
|
"typescript": "^6.0.3",
|
|
@@ -114,9 +116,9 @@
|
|
|
114
116
|
"homepage": "https://pi-web.dev/",
|
|
115
117
|
"packageManager": "npm@11.11.0",
|
|
116
118
|
"peerDependencies": {
|
|
117
|
-
"@earendil-works/pi-agent-core": ">=0.
|
|
118
|
-
"@earendil-works/pi-ai": ">=0.
|
|
119
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
119
|
+
"@earendil-works/pi-agent-core": ">=0.83.0",
|
|
120
|
+
"@earendil-works/pi-ai": ">=0.83.0",
|
|
121
|
+
"@earendil-works/pi-coding-agent": ">=0.83.0"
|
|
120
122
|
},
|
|
121
123
|
"keywords": [
|
|
122
124
|
"pi-package",
|