@jmfederico/pi-web 1.202609.0 → 1.202609.1

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.
Files changed (196) hide show
  1. package/README.md +3 -3
  2. package/dist/.plugins-ready +1 -0
  3. package/dist/cli.js +5 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/client/assets/index-AroYaCLx.js +4130 -0
  6. package/dist/client/assets/vendor-editor-core-YWog6mAR.js +10 -0
  7. package/dist/client/assets/vendor-editor-languages-DOCw3vc9.js +28 -0
  8. package/dist/client/index.html +4 -4
  9. package/dist/docker/piWebDockerCommandPlan.js +9 -1
  10. package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
  11. package/dist/nativeServices/installedServiceDefinitions.js +24 -17
  12. package/dist/nativeServices/installedServiceDefinitions.js.map +1 -1
  13. package/dist/nativeServices/serviceAction.js +38 -26
  14. package/dist/nativeServices/serviceAction.js.map +1 -1
  15. package/dist/nativeServices/servicePlan.js +3 -3
  16. package/dist/nativeServices/servicePlan.js.map +1 -1
  17. package/dist/pi-packages/captains-log/README.md +11 -0
  18. package/dist/pi-packages/captains-log/dist/browser/channelProtocol.js +29 -0
  19. package/dist/pi-packages/captains-log/dist/browser/index.js +144 -0
  20. package/dist/pi-packages/captains-log/dist/browser/markdown.js +67 -0
  21. package/dist/pi-packages/captains-log/dist/browser/panel.js +105 -0
  22. package/dist/pi-packages/captains-log/dist/browser/protocol.js +26 -0
  23. package/dist/pi-packages/captains-log/dist/companion.js +126 -0
  24. package/dist/pi-packages/captains-log/dist/roundtrip.js +62 -0
  25. package/dist/pi-packages/captains-log/dist/server.js +210 -0
  26. package/dist/pi-packages/captains-log/dist/store.js +75 -0
  27. package/dist/pi-packages/captains-log/docs/usage.md +30 -0
  28. package/dist/pi-packages/captains-log/package.json +35 -0
  29. package/dist/pi-packages/captains-log/tsconfig.json +21 -0
  30. package/dist/pi-packages/captains-log/vite.config.mjs +28 -0
  31. package/dist/pi-packages/relays/pi-web-plugin.js +1 -1
  32. package/dist/pi-packages/relays/prompts/relay.md +1 -1
  33. package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +21 -13
  34. package/dist/pi-web-plugins/files/browser/assets/files-icon-DZObYhxb.svg +3 -0
  35. package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +439 -0
  36. package/dist/pi-web-plugins/files/package.json +15 -0
  37. package/dist/pi-web-plugins/git/browser/git-panel.js +54 -24
  38. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +1 -1
  39. package/dist/pi-web-plugins/git/git-backend.js +5 -5
  40. package/dist/pi-web-plugins/git/server-plugin.js +5 -3
  41. package/dist/pi-web-plugins/info/pi-web-plugin.js +1 -1
  42. package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +3590 -0
  43. package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +6 -0
  44. package/dist/pi-web-plugins/mermaid/package.json +13 -0
  45. package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +210 -0
  46. package/dist/pi-web-plugins/terminal/package.json +16 -0
  47. package/dist/pi-web-plugins/terminal/server-plugin.js +365 -0
  48. package/dist/{server/terminals → pi-web-plugins/terminal}/terminalService.js +159 -79
  49. package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
  50. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +1 -1
  51. package/dist/piWebVersionReport.js +12 -4
  52. package/dist/piWebVersionReport.js.map +1 -1
  53. package/dist/plugin-api.d.ts +222 -23
  54. package/dist/plugin-api.js +1 -0
  55. package/dist/pluginRecoveryCli.js +3 -2
  56. package/dist/pluginRecoveryCli.js.map +1 -1
  57. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  58. package/dist/server/app.js +20 -9
  59. package/dist/server/app.js.map +1 -1
  60. package/dist/server/knownPiPackages.js +12 -0
  61. package/dist/server/knownPiPackages.js.map +1 -0
  62. package/dist/server/machines/machineClient.js +2 -2
  63. package/dist/server/machines/machineClient.js.map +1 -1
  64. package/dist/server/machines/machinePluginProxyRoutes.js +53 -11
  65. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  66. package/dist/server/machines/machineProxyRoutes.js +70 -7
  67. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  68. package/dist/server/notices/serverNoticeStore.js +45 -4
  69. package/dist/server/notices/serverNoticeStore.js.map +1 -1
  70. package/dist/server/piPackageService.js +5 -5
  71. package/dist/server/piPackageService.js.map +1 -1
  72. package/dist/server/piWebPluginCatalog.js +46 -12
  73. package/dist/server/piWebPluginCatalog.js.map +1 -1
  74. package/dist/server/piWebPluginLifecycle.js +26 -3
  75. package/dist/server/piWebPluginLifecycle.js.map +1 -1
  76. package/dist/server/piWebPluginService.js +37 -2
  77. package/dist/server/piWebPluginService.js.map +1 -1
  78. package/dist/server/pluginCallbackDrain.js +35 -0
  79. package/dist/server/pluginCallbackDrain.js.map +1 -0
  80. package/dist/server/plugins/pluginBackendChannelProxyAdmission.js +84 -0
  81. package/dist/server/plugins/pluginBackendChannelProxyAdmission.js.map +1 -0
  82. package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js +234 -0
  83. package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js.map +1 -0
  84. package/dist/server/plugins/pluginBackendChannelProxyRoutes.js +52 -0
  85. package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -0
  86. package/dist/server/plugins/pluginBackendProxyRoutes.js +38 -31
  87. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
  88. package/dist/server/plugins/pluginBackendRegistry.js +740 -0
  89. package/dist/server/plugins/pluginBackendRegistry.js.map +1 -0
  90. package/dist/server/plugins/serverPluginPiSessionEventsCapability.js +34 -0
  91. package/dist/server/plugins/serverPluginPiSessionEventsCapability.js.map +1 -0
  92. package/dist/server/plugins/serverPluginPiSessionsCapability.js +185 -0
  93. package/dist/server/plugins/serverPluginPiSessionsCapability.js.map +1 -0
  94. package/dist/server/plugins/serverPluginRuntime.js +1028 -107
  95. package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
  96. package/dist/server/plugins/serverPluginWorkspacesCapability.js +114 -0
  97. package/dist/server/plugins/serverPluginWorkspacesCapability.js.map +1 -0
  98. package/dist/server/sessiond/pluginBackendChannelRoutes.js +393 -0
  99. package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -0
  100. package/dist/server/sessiond/pluginBackendRoutes.js +10 -7
  101. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
  102. package/dist/server/sessiond/sessionDaemonShutdown.js +9 -2
  103. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -1
  104. package/dist/server/sessiond.js +71 -21
  105. package/dist/server/sessiond.js.map +1 -1
  106. package/dist/server/sessions/builtinCommands.js +2 -2
  107. package/dist/server/sessions/builtinCommands.js.map +1 -1
  108. package/dist/server/sessions/clientSessionPreview.js +14 -0
  109. package/dist/server/sessions/clientSessionPreview.js.map +1 -0
  110. package/dist/server/sessions/piSessionEventConnections.js +65 -0
  111. package/dist/server/sessions/piSessionEventConnections.js.map +1 -0
  112. package/dist/server/sessions/piSessionService.js +269 -86
  113. package/dist/server/sessions/piSessionService.js.map +1 -1
  114. package/dist/server/sessions/sessionActivityMarker.js +105 -0
  115. package/dist/server/sessions/sessionActivityMarker.js.map +1 -0
  116. package/dist/server/sessions/sessionEnvironmentFacts.js +5 -4
  117. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -1
  118. package/dist/server/sessions/sessionNameGenerator.js +3 -2
  119. package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
  120. package/dist/server/sessions/sessionRoutes.js +18 -0
  121. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  122. package/dist/server/sessions/spawnSessionTool.js +1 -1
  123. package/dist/server/sessions/spawnSessionTool.js.map +1 -1
  124. package/dist/server/sessions/spawnSubsessionTool.js +1 -1
  125. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  126. package/dist/server/sessions/transcriptMessages.js +54 -0
  127. package/dist/server/sessions/transcriptMessages.js.map +1 -0
  128. package/dist/server/terminals/requiredTerminalService.js +103 -0
  129. package/dist/server/terminals/requiredTerminalService.js.map +1 -0
  130. package/dist/server/webSocketBridge.js +339 -0
  131. package/dist/server/webSocketBridge.js.map +1 -1
  132. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +23 -3
  133. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -1
  134. package/dist/server/workspaces/workspaceCatalog.js +3 -1
  135. package/dist/server/workspaces/workspaceCatalog.js.map +1 -1
  136. package/dist/server/workspaces/workspaceProviderRegistry.js +64 -149
  137. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
  138. package/dist/server/workspaces/workspaceRemovalService.js +23 -1
  139. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
  140. package/dist/server-plugin-api.d.ts +216 -22
  141. package/dist/server-plugin-api.js +319 -2
  142. package/dist/server-plugin-api.js.map +1 -1
  143. package/dist/serverPluginRecovery.js +4 -0
  144. package/dist/serverPluginRecovery.js.map +1 -1
  145. package/dist/sessiond/sessionDaemonClient.js +3 -3
  146. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  147. package/dist/shared/apiTypes.js +1 -1
  148. package/dist/shared/apiTypes.js.map +1 -1
  149. package/dist/shared/federatedRoutes.js +45 -17
  150. package/dist/shared/federatedRoutes.js.map +1 -1
  151. package/dist/shared/machinePluginIds.js +28 -7
  152. package/dist/shared/machinePluginIds.js.map +1 -1
  153. package/dist/shared/pluginApiTypes.d.ts +21 -1
  154. package/dist/shared/pluginApiTypes.js +0 -1
  155. package/dist/shared/pluginBackendProtocol.js +163 -2
  156. package/dist/shared/pluginBackendProtocol.js.map +1 -1
  157. package/dist/shared/pluginIds.js +8 -1
  158. package/dist/shared/pluginIds.js.map +1 -1
  159. package/dist/shared/requiredTerminalPlugin.js +6 -0
  160. package/dist/shared/requiredTerminalPlugin.js.map +1 -0
  161. package/dist/shared/serverNoticeContract.js +88 -0
  162. package/dist/shared/serverNoticeContract.js.map +1 -0
  163. package/dist/shared/sessionDefaults.js +41 -0
  164. package/dist/shared/sessionDefaults.js.map +1 -0
  165. package/docs/config.md +50 -36
  166. package/docs/plugins.md +146 -1217
  167. package/examples/session-bridge-plugin/README.md +20 -0
  168. package/examples/session-bridge-plugin/docs/usage.md +54 -0
  169. package/examples/session-bridge-plugin/package.json +26 -0
  170. package/examples/session-bridge-plugin/src/browser/index.ts +84 -0
  171. package/examples/session-bridge-plugin/src/browser/protocol.ts +24 -0
  172. package/examples/session-bridge-plugin/src/companion.ts +65 -0
  173. package/examples/session-bridge-plugin/src/reviewRun.ts +39 -0
  174. package/examples/session-bridge-plugin/src/server.ts +85 -0
  175. package/examples/session-bridge-plugin/src/store.ts +57 -0
  176. package/examples/session-bridge-plugin/tsconfig.json +21 -0
  177. package/examples/workspace-provider-plugin/README.md +4 -4
  178. package/examples/workspace-provider-plugin/package.json +1 -1
  179. package/examples/workspace-provider-plugin/src/browser/index.ts +9 -9
  180. package/examples/workspace-provider-plugin/src/server.ts +15 -11
  181. package/package.json +17 -11
  182. package/dist/client/assets/CodeViewer-CGAlg9S8.js +0 -4
  183. package/dist/client/assets/TerminalPanel-xhJRhOas.js +0 -187
  184. package/dist/client/assets/index-DXQKhn1P.js +0 -4326
  185. package/dist/client/assets/vendor-editor-core-CXO8gGab.js +0 -12
  186. package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +0 -46
  187. package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +0 -1
  188. package/dist/client/assets/vendor-terminal-BrP-ENHg.css +0 -1
  189. package/dist/client/assets/vendor-terminal-D8k4UKM2.js +0 -35
  190. package/dist/server/terminalProxyRoutes.js +0 -141
  191. package/dist/server/terminalProxyRoutes.js.map +0 -1
  192. package/dist/server/terminals/terminalRoutes.js +0 -155
  193. package/dist/server/terminals/terminalRoutes.js.map +0 -1
  194. package/dist/server/terminals/terminalService.js.map +0 -1
  195. package/dist/server/terminals/terminalSize.js +0 -17
  196. package/dist/server/terminals/terminalSize.js.map +0 -1
package/docs/config.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  PI WEB configuration covers the machine-local and project-local settings you usually need: the web/API bind address, trusted development-host settings, UI preferences, desired plugin enablement/settings, server-plugin recovery, file-explorer path access, manual upload defaults, upload limits, the Pi agent state directory, and session-daemon tools.
4
4
 
5
- This file is the markdown reference for agents and package consumers. The website page is <https://pi-web.dev/config>.
5
+ Use this reference for detailed configuration and operational behavior. For scannable settings tables with defaults, scopes, and restart requirements, see <https://pi-web.dev/config>.
6
6
 
7
7
  ## Config files
8
8
 
@@ -15,7 +15,25 @@ Each PI WEB machine has its own config. When using Fleet/machine federation, Set
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 controls desired enablement/settings for discovered browser-only, server-only, and dual-entry PI WEB plugins on that machine; it does not install, remove, or update Pi packages.
17
17
 
18
- If you installed services with a custom config path, `pi-web start`, `pi-web restart`, and `pi-web doctor` automatically use the `PI_WEB_CONFIG` saved in those service definitions for their readiness checks. A nonempty `PI_WEB_CONFIG` supplied when invoking one of those commands overrides the installed path for that command. On systemd, these commands fail rather than guess if `EnvironmentFile=` inputs, stale manager state, a different loaded fragment, or an effective environment mismatch make the loaded definition untrustworthy. Drop-ins that do not alter the inspected environment (such as distribution-provided global hardening drop-ins) are tolerated. On launchd, `start` and `doctor` likewise fail if an already-loaded label came from another plist or retains a different config path; `restart` reloads the installed plists and can repair that stale state. Rerun `pi-web install --config /path/to/config.json` after changing the managed path or after upgrading from a version that only applied it to the web service; this regenerates service files so the web/API and session daemon use the same config.
18
+ ### Custom config paths in installed services
19
+
20
+ `start`, `restart`, and `doctor` use the config path saved in the installed services for readiness checks unless the caller supplies a nonempty `PI_WEB_CONFIG` override. `doctor` checks the managed setup, not every custom runtime environment.
21
+
22
+ | Situation | Behavior / action |
23
+ | --- | --- |
24
+ | Use the installed config | Run `pi-web start`, `pi-web restart`, or `pi-web doctor` normally. |
25
+ | Override the path for one command | Supply a nonempty `PI_WEB_CONFIG` when invoking that command. This does not rewrite service definitions. |
26
+ | Change the managed service config path | Run `pi-web install --config /path/to/config.json` to regenerate both web/API and sessiond service definitions. |
27
+ | Upgrade from an installation that set the path only for the web service | Rerun that same install command so both services use the same config. |
28
+ | systemd cannot verify the loaded service environment | The command fails rather than guesses if manager state is stale, the loaded fragment differs, or a PI WEB-managed environment value cannot be verified. Managed `Environment=` values (currently `PI_WEB_CONFIG`) must match the installed definition; unrelated variables are ignored. Drop-ins that leave managed values unchanged are allowed. |
29
+ | systemd uses `EnvironmentFile=` | Accepted with a nonfatal warning. File contents are not included in systemctl's `Environment` property and PI WEB does not inspect them, so config overrides in those files cannot be verified. |
30
+ | launchd has an old config path or a label loaded from another plist | `start` and `doctor` fail. `restart` reloads installed plists and can repair stale loaded state. |
31
+
32
+ ## Startup model and thinking defaults
33
+
34
+ Open the model or thinking-level selector and click a row’s star under **New session default** to save it for new sessions. A filled star marks the saved default. Clicking the option itself changes only the current session; setting the default leaves the current session unchanged.
35
+
36
+ Defaults are saved in Pi’s global `settings.json` on the selected session’s machine (`~/.pi/agent/settings.json` by default), using `defaultProvider`, `defaultModel`, and `defaultThinkingLevel`. They apply to new sessions without restarting. Project `.pi/settings.json` overrides, explicit startup choices, and per-model thinking settings still take precedence. A default model must be enabled; otherwise startup falls back to the first enabled model. Resumed sessions keep their saved model and thinking level.
19
37
 
20
38
  ## Reverse-proxy deployment paths
21
39
 
@@ -101,7 +119,7 @@ Project-local config lives at `<project>/.pi-web/config.json`. Use it for settin
101
119
 
102
120
  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.
103
121
 
104
- Project-local `uploads.defaultFolder` overrides the global upload destination for workspaces in that project, and project-local `attachments.defaultFolder` overrides the global prompt-attachment destination the same way. PI WEB servers always include these workspace-effective values on the workspace responses used locally and through machine federation.
122
+ Project-local `uploads.defaultFolder` overrides the global upload destination for workspaces in that project, and project-local `attachments.defaultFolder` overrides the global prompt-attachment destination the same way. These defaults also apply when accessing the project through Fleet.
105
123
 
106
124
  Plugins may own separate project files, such as `.pi-web/tasks.json` for the built-in Workspace Tasks plugin.
107
125
 
@@ -193,7 +211,7 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
193
211
 
194
212
  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.
195
213
 
196
- One live session daemon owns each data directory. At startup the daemon records its ownership in `sessiond-owner.json` inside the data directory; a second session daemon pointed at the same directory while the first is still running fails loudly at startup with an error naming the owning process and the distinct `PI_WEB_DATA_DIR`, `PI_WEB_SESSIOND_SOCKET` (or `PI_WEB_SESSIOND_PORT` / `PI_WEB_SESSIOND_HOST`), and `PI_WEB_PORT` values a second instance needs. The web/API process of the same instance shares the data directory without claiming it, and a short startup grace covers ordinary service restarts. A marker left behind by a daemon that is no longer running is taken over automatically; if startup still refuses because of a marker whose owner is gone, delete the stale `sessiond-owner.json` as the error message suggests.
214
+ Only one live session daemon may use a data directory. For a second instance, set a distinct `PI_WEB_DATA_DIR`, `PI_WEB_SESSIOND_SOCKET` (or `PI_WEB_SESSIOND_PORT` / `PI_WEB_SESSIOND_HOST`), and `PI_WEB_PORT`. Stale ownership markers are normally recovered automatically. If startup still refuses, verify the named owner is no longer running before deleting `sessiond-owner.json` as the error suggests.
197
215
 
198
216
  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`.
199
217
 
@@ -242,9 +260,9 @@ The Files panel can upload one or more files in two ways:
242
260
 
243
261
  The value must be a non-empty workspace-relative folder. PI WEB normalizes repeated separators and backslashes to `/`, and rejects absolute paths or `..` traversal. In the upload dialog only, clearing the destination field uploads that batch to the workspace root.
244
262
 
245
- 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.
263
+ Uploads stay inside the workspace, create parent folders by default, and do not overwrite existing files unless you enable overwrite in the review dialog. Direct drag/drop never overwrites. Check the upload progress UI for completion, conflicts, and errors.
246
264
 
247
- 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.
265
+ In Fleet, Settings saves the global upload default on the selected machine; the Files panel uses that project's effective destination.
248
266
 
249
267
  The per-request size limit is still controlled by `maxUploadBytes` / `PI_WEB_MAX_UPLOAD_BYTES` on the machine serving the upload.
250
268
 
@@ -264,7 +282,7 @@ When the chat composer has pending attachments, its delivery selector offers **S
264
282
 
265
283
  The value must be a non-empty workspace-relative folder. PI WEB normalizes repeated separators and backslashes to `/`, and rejects absolute paths or `..` traversal. Saved attachments always stay inside the workspace root, and an explicit per-request folder on the attachments API overrides the configured default.
266
284
 
267
- For machine federation, Settings saves the global attachments default on the selected machine. Remote PI WEB servers always return `workspace.effectiveConfig.attachments.defaultFolder` on the workspace-list response, and the composer uses it as the default save destination.
285
+ In Fleet, Settings saves the global attachment default on the selected machine; the composer uses that project's effective destination.
268
286
 
269
287
  ### Agent state directory
270
288
 
@@ -286,28 +304,19 @@ If the session daemon cannot report a valid active directory, profile-dependent
286
304
 
287
305
  ### Pi extension provider baseline
288
306
 
289
- This policy applies to **Pi runtime extensions that register model providers**, not PI WEB workspace-provider plugins. Pi extensions can call `pi.registerProvider(...)` and follow Pi's extension API. A PI WEB plugin may have a browser `module` and/or a sessiond `serverModule`, but its server entry follows the separate `@jmfederico/pi-web/server-plugin-api` lifecycle and cannot register Pi model providers or arbitrary hooks. See the [PI WEB plugin guide](https://pi-web.dev/plugins).
290
-
291
- 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 directory, 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`.
307
+ Model providers are shared across all sessions on a machine. PI WEB loads them when the session daemon starts, using Pi's built-in providers, environment credentials, the active agent directory's `models.json`, and globally installed Pi extensions/packages. PI WEB workspace plugins are separate; see the [plugin guide](https://pi-web.dev/plugins).
292
308
 
293
- 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.
309
+ Provider connection settings stay fixed until the daemon restarts. Project extensions and `/reload` cannot add, replace, or remove providers. Other Pi extension features continue to load and reload normally.
294
310
 
295
311
  #### Model list refresh for a known provider
296
312
 
297
- 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:
298
-
299
- - the provider ID is already in the startup baseline, and
300
- - every field except the model list is unchanged — `name`, `baseUrl`, `apiKey`, `api`, `streamSimple`, `headers`, `authHeader`, `oauth`, and `refreshModels`.
301
-
302
- 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.
303
-
304
- 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.
313
+ An extension may refresh an existing provider's **model list** without a restart, provided all other provider settings remain unchanged. Changes to credentials, connection settings, or provider implementation require a daemon restart. Accepted model-list updates are available to sessions immediately.
305
314
 
306
315
  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.
307
316
 
308
317
  #### Provider decisions in the daemon log
309
318
 
310
- 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.
319
+ Check the session-daemon log for ignored provider changes and applied model-list refreshes; these do not produce browser notifications. Log entries omit provider configuration and credentials.
311
320
 
312
321
  This prevents accidental provider, configuration, or credential contamination between projects; it is not a security boundary because Pi extensions remain trusted daemon code.
313
322
 
@@ -355,11 +364,7 @@ Tracked subsessions are join-oriented. Calling `spawn_subsession` returns immedi
355
364
 
356
365
  A tracked subsession always runs in the spawning session's working directory, so it stays in that workspace's session tree next to its parent. `spawn_subsession` takes no `cwd`. To get work done elsewhere, instruct the child to work there from this workspace, or use `spawn_session`, which still targets any workspace of the project, for an independent session there.
357
366
 
358
- At a join point, after finishing its independent work, the parent calls `yield_to_subsessions` alone as the final action in its tool batch. Pi ends a tool batch early only when every result in that batch is terminating. If any tracked child is still working, the action ends the current agent run so the parent becomes idle. If none are working, it does not end the run and clearly reports that there is nothing to wait for.
359
-
360
- A completion notice wakes an idle parent or queues behind in-flight work. Each notice lists any other tracked children still working, so the parent can continue work or call `yield_to_subsessions` again at the next join point. Further notices arrive automatically; do not poll. The notice includes the child's final output when it fits. If that output is too long, PI WEB omits it entirely instead of adding a truncated duplicate to the parent's context and directs the parent to retrieve it with `check_subsession`.
361
-
362
- `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.
367
+ The parent can continue independent work or wait for its tracked children. Completion notices arrive automatically and wake an idle parent; no polling is needed. Child output is available to the parent when the child stops.
363
368
 
364
369
  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.
365
370
 
@@ -371,7 +376,7 @@ In **Settings → Session daemon**, these keys are saved on the selected machine
371
376
 
372
377
  Use **Settings → Session daemon → Allow agents to ask questions** to change `askUser` on the selected machine. An environment override makes the toggle read-only.
373
378
 
374
- 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.
379
+ Agents can post a form with 1–20 questions, with free-text answers or up to 12 choices per question. Some questions allow multiple selections. A **Custom** free-text answer is always available, and you may leave any question unanswered.
375
380
 
376
381
  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.
377
382
 
@@ -383,7 +388,7 @@ Restart the session daemon after changing `askUser` or after upgrading PI WEB to
383
388
 
384
389
  ### Extension dialogs
385
390
 
386
- 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.
391
+ Pi extensions can show confirmation, selection, and text-input dialogs inline in the session transcript, including while a session starts or a tool runs. 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 details.
387
392
 
388
393
  `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.
389
394
 
@@ -403,17 +408,17 @@ The `plugins` key controls desired enablement and JSON settings for PI WEB brows
403
408
  }
404
409
  ```
405
410
 
406
- Plugins are enabled by default. `plugins.<id>.enabled: false` hides a browser-only entry on the next page load. For a server-backed entry, desired disablement takes effect on the next sessiond start; its paired browser entry continues to follow the still-active backend until that restart. Server settings are copied into sessiond's startup snapshot, and diagnostics expose only a fingerprint, never the values.
411
+ Plugins are enabled by default unless their package metadata declares `defaultEnabled: false`, as Captain's Log does. Explicit `plugins.<id>.enabled` config overrides the package default. `plugins.<id>.enabled: false` hides a browser-only entry on the next page load. For a server-backed entry, desired disablement takes effect on the next sessiond start; its paired browser entry continues to follow the still-active server entry until that restart. The bundled `pi-web.terminal` plugin is required during normal startup: ordinary config cannot disable it, and Settings renders it non-editable. Server settings take effect at daemon startup; diagnostics do not expose their values.
407
412
 
408
413
  #### Desired versus active plugin state
409
414
 
410
- Sessiond is the single workspace authority and resolves one immutable server-plugin/provider snapshot when it starts. Saving `plugins` config or replacing package files changes **desired** state but does not hot-reload, unload, or replace active server code. The old provider and its paired browser entry can remain active until a restart after desired disablement. A paired browser entry is withheld when desired source, scope, settings fingerprint, browser revision, or server revision differs from the active snapshot, or when active health/lifecycle compatibility is unsuitable.
415
+ Saving `plugins` config or replacing package files changes **desired** state, not the running server code. A disabled server plugin and its paired UI may remain active until the session daemon restarts. PI WEB withholds a paired UI when its package/settings no longer match the running server code or the server plugin is unhealthy or incompatible.
411
416
 
412
- **Settings → PI WEB plugins** shows desired and active state separately, including active, failed, incompatible, disabled, not-active/missing, unknown, conflict, stale-revision, health, safe-mode, and restart-required state. Desired config remains editable when sessiond is unavailable as long as the selected machine's config endpoint works, but PI WEB reports active state as unavailable rather than constructing a second workspace authority.
417
+ **Settings → PI WEB plugins** distinguishes desired from active state and shows failures, compatibility problems, safe mode, and required restarts. If the daemon is unavailable, desired config may still be editable, but active state is unavailable.
413
418
 
414
- For machine federation, the panel targets the selected machine. Remote desired state is saved in that target's config and active state comes from that target's sessiond through the gateway. If the versioned plugin lifecycle, the remote manifest, or provider backend routes are unavailable/incompatible, PI WEB reports an explicit unsupported or compatibility error and does not silently use gateway config/code.
419
+ In Fleet, this panel targets the selected machine. Unsupported or incompatible remote plugin features report errors rather than using gateway config or code.
415
420
 
416
- Mixed-version plugin/provider operation is not supported in either upgrade order. A newer gateway rejects an older target's whole remote plugin manifest, including browser-only contributions, when the target lacks the current lifecycle contract; its Git panel is therefore unavailable. An older gateway still calls legacy core Git routes removed by an updated target, so remote Git status/diff returns `404`. Upgrade gateway and target together, restart their updated web/API processes and the target session daemon, then reload the browser. Other selected-machine settings and features report their own explicit errors.
421
+ Mixed-version plugin operation is unsupported in either upgrade order. Remote plugins, including Git, may be unavailable or return `404`. Upgrade gateway and target together, restart their updated web/API processes and the target session daemon, then reload the browser. Other selected-machine features may report their own compatibility errors.
417
422
 
418
423
  Apply changes in this order:
419
424
 
@@ -436,7 +441,7 @@ pi-web plugins safe-start set none --restart
436
441
  pi-web plugins safe-start clear --restart
437
442
  ```
438
443
 
439
- `disable` persists `plugins.<id>.enabled: false`. Safe-start state is stored under `serverPlugins.safeStart`: `bundled-only` filters external server packages before discovery/import, while `none` imports no server plugins and retains the kernel project-folder workspace. `clear` restores ordinary configured discovery on the next start. An unsupported `serverPlugins.safeStart` shape or value in otherwise valid JSON fails closed as effective `none`; use `safe-start show`, then `set` or `clear`, to repair it offline.
444
+ `disable` persists `plugins.<id>.enabled: false`, but rejects required `pi-web.terminal` with no-plugin safe-start recovery guidance. Safe-start state is stored under `serverPlugins.safeStart`: `bundled-only` filters external server packages before discovery/import while still requiring bundled Terminal, whereas `none` imports no server plugins and retains the kernel project-folder and diagnosis/settings surfaces without Terminal or Terminal-backed commands. `clear` restores ordinary configured discovery on the next start. An unsupported `serverPlugins.safeStart` shape or value in otherwise valid JSON fails closed as effective `none`; use `safe-start show`, then `set` or `clear`, to repair it offline.
440
445
 
441
446
  `--restart` performs a restart only for a recognized safe installed-service plan; otherwise it prints manual instructions. The config mutation is durable before PI WEB attempts the restart. If the service-manager command itself fails, restart sessiond manually.
442
447
 
@@ -444,18 +449,27 @@ Ordinary import/activation/start/health failures are quarantined when possible,
444
449
 
445
450
  ### Shortcut config
446
451
 
447
- Shortcut values are keyed by action id. Values are shortcut strings such as `mod+k` or `mod+g p`; `null` disables that action's shortcut.
452
+ Shortcut values are keyed by action id. Values are shortcut strings such as `mod+k`, `g p`, or `shift+enter`; `null` disables that action's shortcut.
448
453
 
449
454
  ```json
450
455
  {
451
456
  "shortcuts": {
452
457
  "core:view.chat": "mod+1",
453
- "core:session.stop": null
458
+ "core:session.stop": null,
459
+ "app.navigation.focus-projects": "g p",
460
+ "composer.send.desktop": "mod+enter",
461
+ "composer.send.mobile": "shift+enter"
454
462
  }
455
463
  }
456
464
  ```
457
465
 
458
- Prefer Settings → Keyboard for editing shortcuts interactively.
466
+ Prefer Settings → Keyboard for editing, recording, disabling, or resetting shortcuts. `mod` accepts Ctrl or ⌘. Browsers and operating systems may reserve some combinations.
467
+
468
+ App shortcuts can be single keys or sequences. Unmodified and Shift-only shortcuts do not start inside inputs, textareas, selects, or contenteditable editors. Sequences expire after 1.2 seconds; Escape or a focus change cancels them. Custom bindings win over defaults; ties resolve by action id. A shorter binding shadows sequences with that prefix (for example, `g` shadows `g p`).
469
+
470
+ The two **Chat composer** send bindings accept one key combination each, not sequences. **Send message — desktop** defaults to Enter; **Send message — touch or narrow screen** defaults to Shift+Enter. The latter applies when the browser reports a coarse primary pointer (typically touch) or a viewport at most 760px wide; otherwise, desktop applies. Its config key remains `composer.send.mobile`. Enter and Shift+Enter insert newlines when not assigned to send. Composer send bindings take priority over app shortcuts only inside the message editor, even when the draft is empty or sending is unavailable. Plain Enter accepts a selected completion first. `null` disables keyboard submission for that context; the send button remains available.
471
+
472
+ Existing browser-local Enter preferences remain the fallback until the corresponding composer binding is configured. Reset removes the override and returns to that fallback. New bindings are saved in the gateway config, like other shortcuts; the old preference is not copied into shared configuration. Automatic touch-keyboard capitalization is ignored when interpreting Shift+Enter.
459
473
 
460
474
  ## Prompt completions
461
475