@jmfederico/pi-web 1.202607.3 → 1.202608.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 (236) hide show
  1. package/README.md +4 -4
  2. package/dist/cli.js +69 -7
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/{CodeViewer-CDVbMiN9.js → CodeViewer-mEiBZFA5.js} +2 -2
  5. package/dist/client/assets/{TerminalPanel-CTS1CgqF.js → TerminalPanel-O9381Eme.js} +6 -6
  6. package/dist/client/assets/index-uI2AGFUy.js +4091 -0
  7. package/dist/client/index.html +1 -1
  8. package/dist/config.js +109 -73
  9. package/dist/config.js.map +1 -1
  10. package/dist/docker/piWebDockerCommandPlan.js +4 -3
  11. package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
  12. package/dist/nativeServices/servicePlan.js +1 -1
  13. package/dist/nativeServices/servicePlan.js.map +1 -1
  14. package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
  15. package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
  16. package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
  17. package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
  18. package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
  19. package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
  20. package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
  21. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
  22. package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
  23. package/dist/{server/git/gitService.js → pi-web-plugins/git/git-backend.js} +209 -60
  24. package/dist/pi-web-plugins/git/package.json +16 -0
  25. package/dist/pi-web-plugins/git/server-plugin.js +176 -0
  26. package/dist/pi-web-plugins/info/infoInternals.js +1 -2
  27. package/dist/pi-web-plugins/info/package.json +1 -1
  28. package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
  29. package/dist/pi-web-plugins/relays/package.json +1 -1
  30. package/dist/pi-web-plugins/relays/pi-web-plugin.js +3 -3
  31. package/dist/pi-web-plugins/relays/relayDiscovery.js +159 -27
  32. package/dist/pi-web-plugins/relays/relaysPanelElement.js +173 -16
  33. package/dist/pi-web-plugins/updates/package.json +1 -1
  34. package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
  35. package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
  36. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
  37. package/dist/plugin-api.d.ts +33 -16
  38. package/dist/pluginRecoveryCli.js +172 -0
  39. package/dist/pluginRecoveryCli.js.map +1 -0
  40. package/dist/server/activity/workspaceActivityService.js +27 -19
  41. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  42. package/dist/server/app.js +35 -37
  43. package/dist/server/app.js.map +1 -1
  44. package/dist/server/browserMessageProjection.js +0 -2
  45. package/dist/server/browserMessageProjection.js.map +1 -1
  46. package/dist/server/configRoutes.js +4 -31
  47. package/dist/server/configRoutes.js.map +1 -1
  48. package/dist/server/machines/machineClient.js +23 -4
  49. package/dist/server/machines/machineClient.js.map +1 -1
  50. package/dist/server/machines/machinePluginProxyRoutes.js +74 -17
  51. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  52. package/dist/server/machines/machineProxyRoutes.js +167 -21
  53. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  54. package/dist/server/machines/machineService.js +22 -0
  55. package/dist/server/machines/machineService.js.map +1 -1
  56. package/dist/server/piWebPluginCatalog.js +584 -0
  57. package/dist/server/piWebPluginCatalog.js.map +1 -0
  58. package/dist/server/piWebPluginLifecycle.js +162 -0
  59. package/dist/server/piWebPluginLifecycle.js.map +1 -0
  60. package/dist/server/piWebPluginService.js +159 -261
  61. package/dist/server/piWebPluginService.js.map +1 -1
  62. package/dist/server/piWebStatus.js +40 -45
  63. package/dist/server/piWebStatus.js.map +1 -1
  64. package/dist/server/plugins/pluginBackendProxyRoutes.js +69 -0
  65. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
  66. package/dist/server/plugins/serverPluginExec.js +184 -0
  67. package/dist/server/plugins/serverPluginExec.js.map +1 -0
  68. package/dist/server/plugins/serverPluginRuntime.js +480 -0
  69. package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
  70. package/dist/server/realtime/sessionEventHub.js +26 -11
  71. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  72. package/dist/server/requestCancellation.js +32 -0
  73. package/dist/server/requestCancellation.js.map +1 -0
  74. package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
  75. package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
  76. package/dist/server/sessiond/agentProcessEnvironment.js +61 -0
  77. package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
  78. package/dist/server/sessiond/pluginBackendRoutes.js +64 -0
  79. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
  80. package/dist/server/sessiond/sessionDaemonShutdown.js +24 -0
  81. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
  82. package/dist/server/sessiond/sessionProxyRoutes.js +2 -2
  83. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  84. package/dist/server/sessiond/sessionServiceDependencies.js +3 -1
  85. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
  86. package/dist/server/sessiond/sessiondStateOwnership.js +235 -0
  87. package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
  88. package/dist/server/sessiond/workspaceCatalogRoutes.js +31 -0
  89. package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
  90. package/dist/server/sessiond/workspaceRemovalRoutes.js +41 -0
  91. package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
  92. package/dist/server/sessiond.js +283 -96
  93. package/dist/server/sessiond.js.map +1 -1
  94. package/dist/server/sessions/askUserTool.js +0 -3
  95. package/dist/server/sessions/askUserTool.js.map +1 -1
  96. package/dist/server/sessions/authRoutes.js +0 -10
  97. package/dist/server/sessions/authRoutes.js.map +1 -1
  98. package/dist/server/sessions/authService.js +15 -78
  99. package/dist/server/sessions/authService.js.map +1 -1
  100. package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
  101. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
  102. package/dist/server/sessions/messagePaging.js +2 -2
  103. package/dist/server/sessions/messagePaging.js.map +1 -1
  104. package/dist/server/sessions/modelCatalogRefresher.js +4 -3
  105. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  106. package/dist/server/sessions/oauthLoginFlowService.js +2 -5
  107. package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
  108. package/dist/server/sessions/pendingAskStore.js +0 -3
  109. package/dist/server/sessions/pendingAskStore.js.map +1 -1
  110. package/dist/server/sessions/piSessionManagerGateway.js +134 -3
  111. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  112. package/dist/server/sessions/piSessionService.js +318 -253
  113. package/dist/server/sessions/piSessionService.js.map +1 -1
  114. package/dist/server/sessions/sessionCommandService.js +24 -1
  115. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  116. package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
  117. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
  118. package/dist/server/sessions/sessionFileFormat.js +27 -0
  119. package/dist/server/sessions/sessionFileFormat.js.map +1 -0
  120. package/dist/server/sessions/sessionFileHeader.js +82 -23
  121. package/dist/server/sessions/sessionFileHeader.js.map +1 -1
  122. package/dist/server/sessions/sessionRoutes.js +88 -56
  123. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  124. package/dist/server/sessions/sessionSummaryScanner.js +491 -0
  125. package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
  126. package/dist/server/sessions/spawnSessionTool.js +8 -1
  127. package/dist/server/sessions/spawnSessionTool.js.map +1 -1
  128. package/dist/server/sessions/spawnSubsessionTool.js +11 -9
  129. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  130. package/dist/server/sessions/spawnTargetResolver.js +1 -1
  131. package/dist/server/status/machineStatusRoutes.js +8 -0
  132. package/dist/server/status/machineStatusRoutes.js.map +1 -0
  133. package/dist/server/status/machineStatusService.js +181 -0
  134. package/dist/server/status/machineStatusService.js.map +1 -0
  135. package/dist/server/status/workspaceAttribution.js +87 -0
  136. package/dist/server/status/workspaceAttribution.js.map +1 -0
  137. package/dist/server/terminalProxyRoutes.js +2 -1
  138. package/dist/server/terminalProxyRoutes.js.map +1 -1
  139. package/dist/server/workspaceExplorerRoutes.js +20 -13
  140. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  141. package/dist/server/workspaces/effectivePathAccess.js +0 -19
  142. package/dist/server/workspaces/effectivePathAccess.js.map +1 -1
  143. package/dist/server/workspaces/fileContentService.js +18 -9
  144. package/dist/server/workspaces/fileContentService.js.map +1 -1
  145. package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
  146. package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
  147. package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
  148. package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
  149. package/dist/server/workspaces/filePreviewService.js +81 -0
  150. package/dist/server/workspaces/filePreviewService.js.map +1 -0
  151. package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
  152. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
  153. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +402 -0
  154. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
  155. package/dist/server/workspaces/workspaceCatalog.js +47 -0
  156. package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
  157. package/dist/server/workspaces/workspaceContext.js +1 -3
  158. package/dist/server/workspaces/workspaceContext.js.map +1 -1
  159. package/dist/server/workspaces/workspaceDeletionRoutes.js +39 -103
  160. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
  161. package/dist/server/workspaces/workspaceProviderRegistry.js +651 -0
  162. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
  163. package/dist/server/workspaces/workspaceRemovalService.js +256 -0
  164. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
  165. package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
  166. package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
  167. package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
  168. package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
  169. package/dist/server-plugin-api.d.ts +140 -0
  170. package/dist/server-plugin-api.js +2 -0
  171. package/dist/server-plugin-api.js.map +1 -0
  172. package/dist/serverPluginRecovery.js +138 -0
  173. package/dist/serverPluginRecovery.js.map +1 -0
  174. package/dist/sessiond/activeAgentProfile.js +3 -22
  175. package/dist/sessiond/activeAgentProfile.js.map +1 -1
  176. package/dist/sessiond/config.js +13 -2
  177. package/dist/sessiond/config.js.map +1 -1
  178. package/dist/sessiond/sessionDaemonClient.js +11 -9
  179. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  180. package/dist/shared/activeAgentProfile.js +2 -41
  181. package/dist/shared/activeAgentProfile.js.map +1 -1
  182. package/dist/shared/activity.js +0 -3
  183. package/dist/shared/activity.js.map +1 -1
  184. package/dist/shared/apiTypes.js +7 -14
  185. package/dist/shared/apiTypes.js.map +1 -1
  186. package/dist/shared/capabilities.js +8 -40
  187. package/dist/shared/capabilities.js.map +1 -1
  188. package/dist/shared/federatedRoutes.js +28 -8
  189. package/dist/shared/federatedRoutes.js.map +1 -1
  190. package/dist/shared/machinePluginIds.js +4 -2
  191. package/dist/shared/machinePluginIds.js.map +1 -1
  192. package/dist/shared/machineStatus.js +94 -0
  193. package/dist/shared/machineStatus.js.map +1 -0
  194. package/dist/shared/piWebStatusParsing.js +29 -0
  195. package/dist/shared/piWebStatusParsing.js.map +1 -1
  196. package/dist/shared/pluginApiTypes.d.ts +148 -0
  197. package/dist/shared/pluginApiTypes.js +3 -0
  198. package/dist/shared/pluginApiTypes.js.map +1 -0
  199. package/dist/shared/pluginBackendProtocol.js +110 -0
  200. package/dist/shared/pluginBackendProtocol.js.map +1 -0
  201. package/dist/shared/pluginIds.js +5 -0
  202. package/dist/shared/pluginIds.js.map +1 -1
  203. package/dist/shared/pluginRecoveryCommands.js +14 -0
  204. package/dist/shared/pluginRecoveryCommands.js.map +1 -0
  205. package/dist/shared/workspaceFiles.js +41 -2
  206. package/dist/shared/workspaceFiles.js.map +1 -1
  207. package/dist/shared/workspaceRemovalProtocol.js +24 -0
  208. package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
  209. package/docs/config.md +140 -47
  210. package/docs/plugins.md +386 -137
  211. package/examples/workspace-provider-plugin/README.md +52 -0
  212. package/examples/workspace-provider-plugin/package.json +25 -0
  213. package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
  214. package/examples/workspace-provider-plugin/src/server.ts +64 -0
  215. package/examples/workspace-provider-plugin/tsconfig.json +21 -0
  216. package/package.json +32 -12
  217. package/server-plugin-api.d.ts +1 -0
  218. package/dist/client/assets/UnifiedDiffViewer-Cqke12ZX.js +0 -39
  219. package/dist/client/assets/index-LME0LfPb.js +0 -4068
  220. package/dist/plugin-api/unstable.d.ts +0 -22
  221. package/dist/server/activity/workspaceActivityRoutes.js +0 -4
  222. package/dist/server/activity/workspaceActivityRoutes.js.map +0 -1
  223. package/dist/server/git/gitService.js.map +0 -1
  224. package/dist/server/gitRoutes.js +0 -23
  225. package/dist/server/gitRoutes.js.map +0 -1
  226. package/dist/server/sessions/parentSessionLocator.js +0 -75
  227. package/dist/server/sessions/parentSessionLocator.js.map +0 -1
  228. package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
  229. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
  230. package/dist/server/workspaces/imagePreviewService.js +0 -40
  231. package/dist/server/workspaces/imagePreviewService.js.map +0 -1
  232. package/dist/server/workspaces/workspaceService.js +0 -52
  233. package/dist/server/workspaces/workspaceService.js.map +0 -1
  234. package/dist/shared/apiTypes.d.ts +0 -1234
  235. package/dist/shared/thinkingLevels.d.ts +0 -27
  236. package/plugin-api/unstable.d.ts +0 -1
package/docs/config.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # PI WEB configuration reference
2
2
 
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, plugin enablement, file-explorer path access, manual upload defaults, upload limits, Pi-compatible agent profiles and companion CLIs, and session-daemon tools.
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
5
  This file is the markdown reference for agents and package consumers. The website page is <https://pi-web.dev/config>.
6
6
 
@@ -11,9 +11,9 @@ 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. Remote servers that do not advertise selected-machine settings support report those settings as unavailable instead of silently falling back to the gateway.
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: session daemon tools, desired PI WEB plugin enablement/settings, 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
- 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.
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
18
  If you installed services with a custom config path, rerun `pi-web install --config /path/to/config.json` after changing that path or after upgrading from a version that only applied the custom path to the web service. This regenerates service files so the web/API and session daemon use the same `PI_WEB_CONFIG`.
19
19
 
@@ -33,17 +33,18 @@ 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`, `PI_WEB_SUBSESSIONS`, and `PI_WEB_ASK_USER`.
36
+ Environment overrides include `PI_WEB_HOST`, `PI_WEB_PORT` / `PORT`, `PI_WEB_ALLOWED_HOSTS`, `PI_WEB_MAX_UPLOAD_BYTES`, `PI_CODING_AGENT_DIR`, `PI_CODING_AGENT_SESSION_DIR`, `PI_WEB_SPAWN_SESSIONS`, `PI_WEB_SUBSESSIONS`, `PI_WEB_ASK_USER`, and `PI_WEB_ENVIRONMENT_FACTS`.
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` / `askUser` / `extensionDialogsTimeoutMs`: restart the session daemon on that machine.
42
+ - `spawnSessions` / `subsessions` / `askUser` / `extensionDialogsTimeoutMs` / `environmentFacts`: 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
- - `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 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).
45
+ - `plugins`: browser-only changes apply after a browser-tab reload. Any enablement, settings, package-source, or package-revision change affecting a `serverModule` requires a manual session-daemon restart, then a browser reload for its paired UI.
46
+ - `serverPlugins.safeStart`: persistent offline recovery state applied before server-plugin discovery/import on the next sessiond start; use the `pi-web plugins safe-start ...` CLI rather than hand-editing it.
47
+ - 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. For a PI WEB package with `serverModule`, manually restart `pi-web-sessiond.service`, then reload the browser. If a global Pi extension adds or removes a model provider, or changes a provider's connection settings, the same manual sessiond restart is required; `/reload` cannot change either startup snapshot. A known Pi model provider refreshing only its own model list is applied without a restart. See [Pi extension provider baseline](#pi-extension-provider-baseline).
47
48
  - `shortcuts`: saved settings apply in the browser after config refresh/save.
48
49
 
49
50
  ## Global config example
@@ -59,12 +60,8 @@ Process restarts depend on the key:
59
60
  "defaultFolder": ".pi-web/uploads"
60
61
  },
61
62
  "maxUploadBytes": 67108864,
62
- "agent": {
63
- "command": "pi",
64
- "dir": "~/agent-profiles/research"
65
- },
66
63
  "spawnSessions": true,
67
- "subsessions": false,
64
+ "subsessions": true,
68
65
  "askUser": true,
69
66
  "extensionDialogsTimeoutMs": 300000,
70
67
  "plugins": {
@@ -97,13 +94,56 @@ Project-local config lives at `<project>/.pi-web/config.json`. Use it for settin
97
94
 
98
95
  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.
99
96
 
100
- Project-local `uploads.defaultFolder` overrides the global upload destination for workspaces in that project. Current PI WEB servers include this workspace-effective value on the existing workspace responses used locally and through machine federation. Older remote servers may omit the optional field; the browser falls back to the global/default upload folder.
97
+ 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.
101
98
 
102
99
  Plugins may own separate project files, such as `.pi-web/tasks.json` for the built-in Workspace Tasks plugin.
103
100
 
101
+ PI WEB also honors one optional project hook; see [Worktree pre-remove hook](#worktree-pre-remove-hook).
102
+
103
+ ## Worktree pre-remove hook
104
+
105
+ Before PI WEB removes a workspace — for Git projects, a secondary worktree — it gives the repository one chance to tear down project-owned infrastructure tied to that workspace. To use the hook, provide an executable script at:
106
+
107
+ ```text
108
+ .pi-web/hooks/worktree-pre-remove
109
+ ```
110
+
111
+ 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.
112
+
113
+ When the hook is present and executable, PI WEB dispatches the hook and the removal as one composed terminal command:
114
+
115
+ ```sh
116
+ '<hook path>' '<workspace path>' && <workspace removal command>
117
+ ```
118
+
119
+ For Git projects the removal command is `git worktree remove '<worktree path>'`.
120
+
121
+ Contract:
122
+
123
+ - **Arguments:** exactly one — the absolute path of the workspace being removed.
124
+ - **Working directory:** the workspace the removal command runs in, not the workspace being removed.
125
+ - **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.
126
+ - **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 the removal command on its own.
127
+
128
+ 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.
129
+
130
+ 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:
131
+
132
+ ```sh
133
+ #!/bin/sh
134
+ # .pi-web/hooks/worktree-pre-remove
135
+ set -eu
136
+
137
+ worktree_path="$1"
138
+
139
+ # Stop/remove local dev containers bind-mounting "$worktree_path",
140
+ # release other per-worktree resources, etc.
141
+ # Exit non-zero to block the worktree removal.
142
+ ```
143
+
104
144
  ## Configuration matrix
105
145
 
106
- 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.
146
+ 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`, `spawnSessions`, `subsessions`, `askUser`, and `plugins`) are edited for the selected machine; gateway host/port/allowed-hosts, keyboard shortcuts, and machine registry/tokens stay local.
107
147
 
108
148
  | Config | JSON key | Env var | Scope | Project-local behavior | Applies / restart |
109
149
  | --- | --- | --- | --- | --- | --- |
@@ -114,13 +154,13 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
114
154
  | External filesystem roots | `pathAccess.allowedPaths` | — | Global + project | **Merges**: global roots first, then project roots; duplicates removed | Next file request; refresh existing views if needed |
115
155
  | Manual file upload default folder | `uploads.defaultFolder` | — | Global + project | **Overrides**: project value wins for workspaces in that project; otherwise global/default applies | New Upload dialogs and direct drag/drop batches after config/workspace refresh |
116
156
  | Upload/body limit | `maxUploadBytes` | `PI_WEB_MAX_UPLOAD_BYTES` | Global | Not supported locally | Restart web/API and session daemon on that machine |
117
- | Companion CLI command | `agent.command` | `PI_WEB_AGENT_COMMAND` | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects doctor/status/update checks |
118
- | 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 |
119
157
  | Agent can spawn sessions | `spawnSessions` | `PI_WEB_SPAWN_SESSIONS` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
120
- | Tracked subsessions (beta) | `subsessions` | `PI_WEB_SUBSESSIONS` | Global/session daemon | Not supported locally; also requires `spawnSessions` | Restart session daemon on that machine |
158
+ | Tracked subsessions | `subsessions` | `PI_WEB_SUBSESSIONS` | Global/session daemon | Not supported locally; also requires `spawnSessions` | Restart session daemon on that machine |
121
159
  | Agent can post question forms | `askUser` | `PI_WEB_ASK_USER` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
122
160
  | Extension dialog auto-cancel timeout | `extensionDialogsTimeoutMs` | — | Global/session daemon | Not supported locally | Restart session daemon on that machine |
123
- | Plugin enablement/settings | `plugins.<id>.enabled`, `plugins.<id>.settings` | — | Global | Not core local config; plugins may read their own project files | Reload browser tab |
161
+ | Session environment facts | `environmentFacts` | `PI_WEB_ENVIRONMENT_FACTS` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
162
+ | PI WEB plugin desired enablement/settings | `plugins.<id>.enabled`, `plugins.<id>.settings` | — | Global + sessiond startup snapshot for server entries | Not core local config; plugins may read their own project files | Browser-only: reload tab. Server-backed: manually restart sessiond, then reload tab |
163
+ | Server-plugin safe start | `serverPlugins.safeStart` | — | Global/offline recovery | Not supported locally; manage with `pi-web plugins safe-start ...` | Applied before discovery/import on next sessiond start |
124
164
  | Keyboard shortcuts | `shortcuts.<actionId>` | — | Global | Not supported locally | Applies after settings save/config refresh |
125
165
  | Project config version | `version` | — | Project | Project-local only; must be `1` when present | Next project-config read |
126
166
  | **Runtime-only environment variables** | | | | | |
@@ -132,8 +172,8 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
132
172
  | Web-to-daemon URL | — | `PI_WEB_SESSIOND_URL` | Web/API env | Not supported locally | Restart web/API |
133
173
  | Projects storage file | — | `PI_WEB_PROJECTS_FILE` | Web/API + session daemon env | Not supported locally | Restart services; advanced state override |
134
174
  | Remote machines storage file | — | `PI_WEB_MACHINES_FILE` | Web/API env | Not supported locally | Restart web/API; advanced state override |
135
- | Agent profile session storage directory | — | `PI_WEB_AGENT_SESSION_DIR` (`PI_CODING_AGENT_SESSION_DIR` for Pi compatibility) | Session daemon env | Not supported locally | Restart session daemon; env-only session storage override |
136
- | Agent profile state directory | — | `PI_WEB_AGENT_DIR` (`PI_CODING_AGENT_DIR` for Pi compatibility) | Web/API + session daemon env | Not supported locally | Restart services |
175
+ | Agent state directory | — | `PI_CODING_AGENT_DIR` | Session daemon env | Not supported locally | Restart session daemon on that machine; affects auth, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugins |
176
+ | Agent session storage directory | — | `PI_CODING_AGENT_SESSION_DIR` | Session daemon env | Not supported locally | Restart session daemon on that machine; env-only session storage override |
137
177
  | Skip update checks | — | `PI_WEB_SKIP_VERSION_CHECK`, `PI_WEB_OFFLINE`, `PI_SKIP_VERSION_CHECK`, `PI_OFFLINE` | Web/API env | Not supported locally | Restart web/API after env changes |
138
178
  | Offline mode | — | `PI_WEB_OFFLINE`, `PI_OFFLINE` | Web/API + session daemon env | Not supported locally | Restart session daemon and web/API after env changes; also disables the [background model catalog refresh](#background-model-catalog-refresh) |
139
179
 
@@ -145,8 +185,18 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
145
185
 
146
186
  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.
147
187
 
188
+ 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.
189
+
148
190
  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`.
149
191
 
192
+ ### Agent process environment
193
+
194
+ Agent shells, terminals, and spawned sessions inherit the session daemon's environment almost as-is. When the daemon starts, it removes only `NODE_ENV` and `PORT` 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. Ordinary variables (`PATH`, `HOME`, proxy settings, and the like) stay visible, and so do the daemon's `PI_WEB_*` configuration keys and the resolved `PI_CODING_AGENT_DIR` / `PI_CODING_AGENT_SESSION_DIR` values, so a `pi` CLI started from inside a session uses the same agent state — auth, models, and session storage — as the daemon. The daemon itself keeps using the values it captured at startup.
195
+
196
+ Every process spawned from a session also inherits `PI_WEB_SESSION=1`, marking it as nested inside the running PI WEB instance. The inherited `PI_WEB_*` values point at that live instance, so starting another PI WEB instance from inside a session fails loudly at startup because the live instance owns the state (see [Managed data directory](#managed-data-directory)); running one deliberately requires a distinct `PI_WEB_DATA_DIR`, `PI_WEB_SESSIOND_SOCKET` (or `PI_WEB_SESSIOND_PORT` / `PI_WEB_SESSIOND_HOST`), and `PI_WEB_PORT`.
197
+
198
+ Session system prompts state these nesting facts — and, in a Docker deployment, the container layout facts — so agents learn the rules before discovering them by breaking their own session, including the precautions never to restart the hosting session daemon and to restart the web/API process before the session daemon. Set the `environmentFacts` config key to `false`, or `PI_WEB_ENVIRONMENT_FACTS=false` in the session daemon's environment, to leave environment facts out of session system prompts; they default to on.
199
+
150
200
  ### External path access
151
201
 
152
202
  `pathAccess.allowedPaths` grants PI WEB's file explorer and absolute `@` path completions access to specific filesystem roots outside the current workspace.
@@ -186,38 +236,33 @@ The value must be a non-empty workspace-relative folder. PI WEB normalizes repea
186
236
 
187
237
  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.
188
238
 
189
- For machine federation, Settings saves the global upload default on the selected machine. Current remote PI WEB servers also return `workspace.effectiveConfig.uploads.defaultFolder` on the existing workspace-list response. Older remote servers can omit that optional field without breaking clients; the Files panel falls back to the global/default upload folder.
239
+ 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.
190
240
 
191
241
  The per-request size limit is still controlled by `maxUploadBytes` / `PI_WEB_MAX_UPLOAD_BYTES` on the machine serving the upload.
192
242
 
193
- ### Pi-compatible agent profile and companion CLI
243
+ ### Agent state directory
194
244
 
195
- `agent.command` selects the Pi-compatible companion CLI used by `pi-web doctor` and, when it can be generated safely, package-managed update commands. It defaults to `pi`. This setting does **not** replace the embedded runtime: every session continues to use PI WEB's bundled Pi SDK.
245
+ PI WEB runs every session on its bundled Pi SDK. `pi-web doctor` and the status/update flow probe the `pi` command on the machine's `PATH`.
196
246
 
197
- `agent.dir` selects the Pi-compatible state profile used for auth providers, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugin discovery. It defaults to `~/.pi/agent` only for a canonical Pi companion command. The directory must use the data layout supported by the bundled Pi SDK; PI WEB does not load or convert incompatible fork formats, migrate profile data, or repartition PI WEB-managed archives when the profile changes.
247
+ `PI_CODING_AGENT_DIR` selects the Pi agent state directory used for auth providers, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugin discovery. It defaults to Pi's own default, `~/.pi/agent`. `PI_CODING_AGENT_SESSION_DIR` overrides session storage separately from the state directory. Both are environment-only; there is no config-file key.
198
248
 
199
- ```json
200
- {
201
- "agent": {
202
- "command": "pi-lab",
203
- "dir": "/opt/pi-profiles/lab"
204
- }
205
- }
249
+ ```sh
250
+ # Session daemon environment
251
+ PI_CODING_AGENT_DIR=/opt/pi-profiles/lab
252
+ PI_CODING_AGENT_SESSION_DIR=/opt/pi-profiles/lab-sessions
206
253
  ```
207
254
 
208
- An alternate command always requires an explicit state directory. The command must be a safe bare executable name such as `pi-lab` or a host-absolute executable path such as `/opt/pi/bin/pi`; relative paths, shell expressions, and launcher strings are rejected. The state directory must be host-absolute or start with `~`. In a federated save, the gateway transports Unix and Windows absolute paths without reinterpreting them, and the target machine validates and returns the persisted profile.
209
-
210
- Environment variables take precedence over the config file. `PI_WEB_AGENT_COMMAND` selects the companion CLI, `PI_WEB_AGENT_DIR` sets the profile state directory, and `PI_WEB_AGENT_SESSION_DIR` overrides session storage separately from `agent.dir`. The legacy `PI_CODING_AGENT_DIR` and `PI_CODING_AGENT_SESSION_DIR` names apply only to a canonical Pi companion command; PI WEB never derives ambient environment-variable names from an arbitrary command. Use the explicit `PI_WEB_AGENT_*` names for alternate commands. `PI_WEB_AGENT_DIR` is an unconditional override, while a legacy `PI_CODING_AGENT_DIR` override stops applying when Settings selects an alternate command so the command and directory can transition together.
255
+ The directory must use the data layout supported by the bundled Pi SDK; PI WEB does not load or convert incompatible formats, migrate profile data, or repartition PI WEB-managed archives when the directory changes.
211
256
 
212
- 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.
257
+ The session daemon resolves the directory once at startup and exports the resolved values to everything it starts, so sessions, terminals, the bash tool, and subsessions all observe the same `PI_CODING_AGENT_DIR` / `PI_CODING_AGENT_SESSION_DIR`. That resolved active directory stays fixed for the daemon lifetime: changing the environment takes effect on the next session-daemon restart on that machine, and until then sessions, Pi package operations, Pi-package-backed PI WEB plugin discovery, status/install detection, and update planning continue to use the daemon-owned active directory; a web/API restart recovers that same active directory instead of applying the new value.
213
258
 
214
- 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. Remote profile editing likewise requires advertised support, and the gateway rejects a remote save if the target does not return the requested profile. Restart the session daemon on the selected machine to establish the next active profile.
259
+ If the session daemon cannot report a valid active directory, profile-dependent Pi package and PI WEB plugin operations report unavailable instead of falling back to independently resolved values. A package-managed update command is shown only when the daemon reports a valid active directory and the `pi` command is on `PATH`, and the command pins that directory for the update. Restart the session daemon on the selected machine to establish the next active directory.
215
260
 
216
261
  ### Pi extension provider baseline
217
262
 
218
- This policy applies to **Pi runtime extensions**, not PI WEB browser plugins. Pi extensions are runtime modules loaded by the session daemon and can call `pi.registerProvider(...)`; PI WEB plugins are browser-side UI modules and never run in the session daemon.
263
+ 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).
219
264
 
220
- 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`.
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 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`.
221
266
 
222
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.
223
268
 
@@ -240,7 +285,7 @@ Ignored mutations are written to the session-daemon log once per operation and p
240
285
 
241
286
  This prevents accidental provider, configuration, or credential contamination between projects; it is not a security boundary because Pi extensions remain trusted daemon code.
242
287
 
243
- 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.
288
+ Configure providers before the daemon starts: use the active agent directory's `models.json`, or install the Pi extension globally in that agent directory. 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.
244
289
 
245
290
  ### Background model catalog refresh
246
291
 
@@ -257,22 +302,26 @@ Each run is bounded: it is aborted after **60 seconds**, and a run that times ou
257
302
 
258
303
  Models fetched by a background refresh appear the next time a client asks for the model list, so a model selector left open across a refresh may need to be reopened.
259
304
 
260
- To turn the background refresh off entirely, set `PI_WEB_OFFLINE` or `PI_OFFLINE` in the session daemon's environment and restart it. In offline mode PI WEB performs no provider catalog network requests, including after logins, and sessions use the catalogs already stored in the agent profile. The `PI_WEB_SKIP_VERSION_CHECK` and `PI_SKIP_VERSION_CHECK` keys do **not** affect this refresh; they only suppress PI WEB release checks.
305
+ To turn the background refresh off entirely, set `PI_WEB_OFFLINE` or `PI_OFFLINE` in the session daemon's environment and restart it. In offline mode PI WEB performs no provider catalog network requests, including after logins, and sessions use the catalogs already stored in the agent directory. The `PI_WEB_SKIP_VERSION_CHECK` and `PI_SKIP_VERSION_CHECK` keys do **not** affect this refresh; they only suppress PI WEB release checks.
261
306
 
262
307
  ### Session daemon tools
263
308
 
264
309
  `spawnSessions` controls whether agents receive the `spawn_session` tool. It defaults to `true`; set it to `false` if you do not want an agent to start independent PI WEB sessions.
265
310
 
266
- `subsessions` is beta and controls whether agents receive the tracked-subsession tools: `spawn_subsession`, `list_subsessions`, `check_subsession`, `read_subsession`, and `yield_to_subsessions`. It defaults to `false` and also requires `spawnSessions` to be enabled.
311
+ `subsessions` controls whether agents receive the tracked-subsession tools: `spawn_subsession`, `list_subsessions`, `check_subsession`, `read_subsession`, and `yield_to_subsessions`. It defaults to `true` and also requires `spawnSessions` to be enabled.
267
312
 
268
313
  Tracked subsessions are join-oriented. Calling `spawn_subsession` returns immediately, so the parent can continue independent work while the child runs. Work whose result the parent does not need to join belongs in the fire-and-forget `spawn_session` tool instead.
269
314
 
315
+ 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.
316
+
270
317
  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.
271
318
 
272
319
  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`.
273
320
 
274
321
  `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.
275
322
 
323
+ 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.
324
+
276
325
  In **Settings → Session daemon**, these keys are saved on the selected machine. Restart the session daemon on that machine after changing them.
277
326
 
278
327
  #### `askUser` and `ask_user`
@@ -299,22 +348,58 @@ Pi extensions can ask the user questions from `ctx.ui.confirm()`, `ctx.ui.select
299
348
 
300
349
  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`.
301
350
 
302
- ### Plugin config
303
-
304
- 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.
351
+ ### PI WEB plugin config and recovery
305
352
 
306
- Plugins are enabled by default. Set `plugins.<id>.enabled` to `false` to remove a plugin from that machine's `/pi-web-plugins/manifest.json` before the browser imports it. Settings lists discovered plugins from the selected machine, including disabled entries exposed by that machine.
353
+ The `plugins` key controls desired enablement and JSON settings for PI WEB browser-only, server-only, and dual-entry plugins 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.
307
354
 
308
355
  ```json
309
356
  {
310
357
  "plugins": {
311
- "workspace-tasks": { "enabled": true, "settings": {} },
358
+ "git": { "enabled": true, "settings": {} },
359
+ "workspace-tasks": { "enabled": true },
312
360
  "updates": { "enabled": false }
313
361
  }
314
362
  }
315
363
  ```
316
364
 
317
- Reload the browser tab after changing plugin enablement. Already-loaded plugin JavaScript is not unloaded from the current page.
365
+ 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.
366
+
367
+ #### Desired versus active plugin state
368
+
369
+ 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.
370
+
371
+ **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.
372
+
373
+ 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.
374
+
375
+ 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.
376
+
377
+ Apply changes in this order:
378
+
379
+ 1. Install or update the package on the target machine.
380
+ 2. Save desired enablement/settings.
381
+ 3. For a browser-only plugin, reload the browser tab.
382
+ 4. For a server-backed plugin, manually restart the target session daemon, wait for it, then reload the browser tab.
383
+
384
+ > **Manual restart warning:** for the native user service, run `systemctl --user restart pi-web-sessiond` (unit `pi-web-sessiond.service`). Restarting sessiond may interrupt active sessions and runtime ownership. A browser reload, web/UI autoreload, restarting only web/API, and Pi's `/reload` do not activate server-plugin state.
385
+
386
+ #### Offline disable and safe start
387
+
388
+ The recovery CLI edits global config offline. It does not contact sessiond, discover packages, import plugin modules, or include machine credentials. Run it directly on the affected machine; for a custom service config, add `--config /path/to/config.json`.
389
+
390
+ ```bash
391
+ pi-web plugins disable <plugin-id> --restart
392
+ pi-web plugins safe-start show
393
+ pi-web plugins safe-start set bundled-only --restart
394
+ pi-web plugins safe-start set none --restart
395
+ pi-web plugins safe-start clear --restart
396
+ ```
397
+
398
+ `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.
399
+
400
+ `--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.
401
+
402
+ Ordinary import/activation/start/health failures are quarantined when possible, but server plugins are trusted in-process code, share sessiond's event loop, and are not crash-isolated. `bundled-only` bypasses external plugin failures; `none` is the emergency level that also bypasses bundled server plugins. Setting, clearing, or disabling takes effect for server code only after sessiond restarts, and that restart may interrupt active sessions/runtime ownership.
318
403
 
319
404
  ### Shortcut config
320
405
 
@@ -331,6 +416,14 @@ Shortcut values are keyed by action id. Values are shortcut strings such as `mod
331
416
 
332
417
  Prefer Settings → Keyboard for editing shortcuts interactively.
333
418
 
419
+ ## Prompt completions
420
+
421
+ The chat composer opens completion menus on three trigger characters:
422
+
423
+ - `/` at the very start of the draft completes session commands.
424
+ - `@` 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.
425
+ - `#` 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`.
426
+
334
427
  ## Optional completion tools
335
428
 
336
429
  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.