@jmfederico/pi-web 1.202608.0 → 1.202608.2

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 (258) hide show
  1. package/README.md +4 -4
  2. package/dist/cli.js +287 -113
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/CodeViewer-BMWwxG7q.js +4 -0
  5. package/dist/client/assets/{TerminalPanel-fYrKc3jh.js → TerminalPanel-CacQDIYn.js} +6 -6
  6. package/dist/client/assets/index-DUW2xnoV.js +4279 -0
  7. package/dist/client/assets/vendor-editor-core-CXO8gGab.js +12 -0
  8. package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +46 -0
  9. package/dist/client/index.html +3 -3
  10. package/dist/config.js +110 -73
  11. package/dist/config.js.map +1 -1
  12. package/dist/docker/piWebDockerCommandPlan.js +4 -3
  13. package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
  14. package/dist/environment.js +4 -0
  15. package/dist/environment.js.map +1 -0
  16. package/dist/nativeServices/installedServiceDefinitions.js +602 -0
  17. package/dist/nativeServices/installedServiceDefinitions.js.map +1 -0
  18. package/dist/nativeServices/serviceAction.js +174 -0
  19. package/dist/nativeServices/serviceAction.js.map +1 -0
  20. package/dist/nativeServices/serviceDoctor.js +374 -71
  21. package/dist/nativeServices/serviceDoctor.js.map +1 -1
  22. package/dist/nativeServices/servicePlan.js +1 -1
  23. package/dist/nativeServices/servicePlan.js.map +1 -1
  24. package/dist/{pi-web-plugins → pi-packages}/relays/markdownDocument.js +1 -1
  25. package/dist/pi-packages/relays/package.json +11 -0
  26. package/dist/{pi-web-plugins → pi-packages}/relays/pi-web-plugin.js +4 -4
  27. package/dist/pi-packages/relays/prompts/relay-worktree.md +157 -0
  28. package/dist/pi-packages/relays/prompts/relay.md +152 -0
  29. package/dist/{pi-web-plugins → pi-packages}/relays/relayDiscovery.js +1 -1
  30. package/dist/{pi-web-plugins → pi-packages}/relays/relaysPanelElement.js +1 -1
  31. package/dist/pi-packages/relays/skills/relay/SKILL.md +123 -0
  32. package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
  33. package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
  34. package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
  35. package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
  36. package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
  37. package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
  38. package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
  39. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
  40. package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
  41. package/dist/{server/git/gitService.js → pi-web-plugins/git/git-backend.js} +209 -60
  42. package/dist/pi-web-plugins/git/package.json +16 -0
  43. package/dist/pi-web-plugins/git/server-plugin.js +176 -0
  44. package/dist/pi-web-plugins/info/infoInternals.js +16 -2
  45. package/dist/pi-web-plugins/info/package.json +1 -1
  46. package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
  47. package/dist/pi-web-plugins/updates/package.json +1 -1
  48. package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
  49. package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
  50. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
  51. package/dist/piWebVersionReport.js +63 -10
  52. package/dist/piWebVersionReport.js.map +1 -1
  53. package/dist/plugin-api.d.ts +33 -16
  54. package/dist/pluginRecoveryCli.js +172 -0
  55. package/dist/pluginRecoveryCli.js.map +1 -0
  56. package/dist/server/activity/workspaceActivityService.js +27 -19
  57. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  58. package/dist/server/app.js +41 -15
  59. package/dist/server/app.js.map +1 -1
  60. package/dist/server/configRoutes.js +3 -21
  61. package/dist/server/configRoutes.js.map +1 -1
  62. package/dist/server/knownAutoInstallPiPackages.js +30 -0
  63. package/dist/server/knownAutoInstallPiPackages.js.map +1 -0
  64. package/dist/server/machines/machineClient.js +23 -4
  65. package/dist/server/machines/machineClient.js.map +1 -1
  66. package/dist/server/machines/machinePluginProxyRoutes.js +74 -17
  67. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  68. package/dist/server/machines/machineProxyRoutes.js +165 -8
  69. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  70. package/dist/server/machines/machineService.js +23 -0
  71. package/dist/server/machines/machineService.js.map +1 -1
  72. package/dist/server/piPackageIdentity.js +25 -0
  73. package/dist/server/piPackageIdentity.js.map +1 -0
  74. package/dist/server/piPackageService.js +69 -8
  75. package/dist/server/piPackageService.js.map +1 -1
  76. package/dist/server/piWebPluginCatalog.js +584 -0
  77. package/dist/server/piWebPluginCatalog.js.map +1 -0
  78. package/dist/server/piWebPluginLifecycle.js +162 -0
  79. package/dist/server/piWebPluginLifecycle.js.map +1 -0
  80. package/dist/server/piWebPluginService.js +159 -261
  81. package/dist/server/piWebPluginService.js.map +1 -1
  82. package/dist/server/piWebStatus.js +41 -9
  83. package/dist/server/piWebStatus.js.map +1 -1
  84. package/dist/server/plugins/pluginBackendProxyRoutes.js +69 -0
  85. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
  86. package/dist/server/plugins/serverPluginExec.js +184 -0
  87. package/dist/server/plugins/serverPluginExec.js.map +1 -0
  88. package/dist/server/plugins/serverPluginRuntime.js +480 -0
  89. package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
  90. package/dist/server/projectTrustRoutes.js +80 -0
  91. package/dist/server/projectTrustRoutes.js.map +1 -0
  92. package/dist/server/projects/projectService.js +3 -1
  93. package/dist/server/projects/projectService.js.map +1 -1
  94. package/dist/server/realtime/sessionEventHub.js +26 -11
  95. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  96. package/dist/server/requestCancellation.js +32 -0
  97. package/dist/server/requestCancellation.js.map +1 -0
  98. package/dist/server/sessiond/agentProcessEnvironment.js +29 -30
  99. package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -1
  100. package/dist/server/sessiond/autoInstallPiPackages.js +63 -0
  101. package/dist/server/sessiond/autoInstallPiPackages.js.map +1 -0
  102. package/dist/server/sessiond/pluginBackendRoutes.js +64 -0
  103. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
  104. package/dist/server/sessiond/sessionDaemonShutdown.js +24 -0
  105. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
  106. package/dist/server/sessiond/sessionProxyRoutes.js +2 -2
  107. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  108. package/dist/server/sessiond/sessionServiceDependencies.js +2 -1
  109. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
  110. package/dist/server/sessiond/sessiondStateOwnership.js +235 -0
  111. package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
  112. package/dist/server/sessiond/workspaceCatalogRoutes.js +31 -0
  113. package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
  114. package/dist/server/sessiond/workspaceRemovalRoutes.js +41 -0
  115. package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
  116. package/dist/server/sessiond.js +284 -98
  117. package/dist/server/sessiond.js.map +1 -1
  118. package/dist/server/sessions/authService.js +15 -54
  119. package/dist/server/sessions/authService.js.map +1 -1
  120. package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
  121. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
  122. package/dist/server/sessions/modelCatalogRefresher.js +4 -3
  123. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  124. package/dist/server/sessions/piSessionManagerGateway.js +18 -67
  125. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  126. package/dist/server/sessions/piSessionService.js +495 -142
  127. package/dist/server/sessions/piSessionService.js.map +1 -1
  128. package/dist/server/sessions/sessionCommandService.js +24 -1
  129. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  130. package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
  131. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
  132. package/dist/server/sessions/sessionFileFormat.js +27 -0
  133. package/dist/server/sessions/sessionFileFormat.js.map +1 -0
  134. package/dist/server/sessions/sessionFileHeader.js +1 -14
  135. package/dist/server/sessions/sessionFileHeader.js.map +1 -1
  136. package/dist/server/sessions/sessionModelScope.js +160 -0
  137. package/dist/server/sessions/sessionModelScope.js.map +1 -0
  138. package/dist/server/sessions/sessionNameGenerator.js +6 -6
  139. package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
  140. package/dist/server/sessions/sessionRoutes.js +55 -0
  141. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  142. package/dist/server/sessions/sessionSummaryScanner.js +96 -224
  143. package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
  144. package/dist/server/sessions/spawnSubsessionTool.js +5 -9
  145. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  146. package/dist/server/sessions/spawnTargetResolver.js +1 -1
  147. package/dist/server/status/machineStatusRoutes.js +8 -0
  148. package/dist/server/status/machineStatusRoutes.js.map +1 -0
  149. package/dist/server/status/machineStatusService.js +181 -0
  150. package/dist/server/status/machineStatusService.js.map +1 -0
  151. package/dist/server/status/workspaceAttribution.js +87 -0
  152. package/dist/server/status/workspaceAttribution.js.map +1 -0
  153. package/dist/server/storage/piPackageDismissalStore.js +78 -0
  154. package/dist/server/storage/piPackageDismissalStore.js.map +1 -0
  155. package/dist/server/terminalProxyRoutes.js +2 -1
  156. package/dist/server/terminalProxyRoutes.js.map +1 -1
  157. package/dist/server/workspaceExplorerRoutes.js +20 -13
  158. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  159. package/dist/server/workspaces/fileContentService.js +18 -9
  160. package/dist/server/workspaces/fileContentService.js.map +1 -1
  161. package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
  162. package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
  163. package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
  164. package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
  165. package/dist/server/workspaces/filePreviewService.js +81 -0
  166. package/dist/server/workspaces/filePreviewService.js.map +1 -0
  167. package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
  168. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
  169. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +402 -0
  170. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
  171. package/dist/server/workspaces/workspaceCatalog.js +47 -0
  172. package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
  173. package/dist/server/workspaces/workspaceContext.js +1 -3
  174. package/dist/server/workspaces/workspaceContext.js.map +1 -1
  175. package/dist/server/workspaces/workspaceDeletionRoutes.js +35 -125
  176. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
  177. package/dist/server/workspaces/workspaceProviderRegistry.js +651 -0
  178. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
  179. package/dist/server/workspaces/workspaceRemovalService.js +256 -0
  180. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
  181. package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
  182. package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
  183. package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
  184. package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
  185. package/dist/server-plugin-api.d.ts +140 -0
  186. package/dist/server-plugin-api.js +2 -0
  187. package/dist/server-plugin-api.js.map +1 -0
  188. package/dist/serverPluginRecovery.js +138 -0
  189. package/dist/serverPluginRecovery.js.map +1 -0
  190. package/dist/sessiond/activeAgentProfile.js +3 -22
  191. package/dist/sessiond/activeAgentProfile.js.map +1 -1
  192. package/dist/sessiond/config.js +11 -0
  193. package/dist/sessiond/config.js.map +1 -1
  194. package/dist/sessiond/sessionDaemonClient.js +11 -9
  195. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  196. package/dist/shared/activeAgentProfile.js +2 -41
  197. package/dist/shared/activeAgentProfile.js.map +1 -1
  198. package/dist/shared/activity.js +0 -3
  199. package/dist/shared/activity.js.map +1 -1
  200. package/dist/shared/apiTypes.js +7 -5
  201. package/dist/shared/apiTypes.js.map +1 -1
  202. package/dist/shared/capabilities.js +8 -6
  203. package/dist/shared/capabilities.js.map +1 -1
  204. package/dist/shared/federatedRoutes.js +36 -5
  205. package/dist/shared/federatedRoutes.js.map +1 -1
  206. package/dist/shared/machinePluginIds.js +4 -2
  207. package/dist/shared/machinePluginIds.js.map +1 -1
  208. package/dist/shared/machineStatus.js +94 -0
  209. package/dist/shared/machineStatus.js.map +1 -0
  210. package/dist/shared/piWebStatusParsing.js +33 -0
  211. package/dist/shared/piWebStatusParsing.js.map +1 -1
  212. package/dist/shared/pluginApiTypes.d.ts +150 -0
  213. package/dist/shared/pluginApiTypes.js +3 -0
  214. package/dist/shared/pluginApiTypes.js.map +1 -0
  215. package/dist/shared/pluginBackendProtocol.js +110 -0
  216. package/dist/shared/pluginBackendProtocol.js.map +1 -0
  217. package/dist/shared/pluginIds.js +5 -0
  218. package/dist/shared/pluginIds.js.map +1 -1
  219. package/dist/shared/pluginRecoveryCommands.js +14 -0
  220. package/dist/shared/pluginRecoveryCommands.js.map +1 -0
  221. package/dist/shared/workspaceFiles.js +41 -2
  222. package/dist/shared/workspaceFiles.js.map +1 -1
  223. package/dist/shared/workspaceRemovalProtocol.js +24 -0
  224. package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
  225. package/docs/config.md +105 -52
  226. package/docs/plugins.md +403 -146
  227. package/examples/workspace-provider-plugin/README.md +52 -0
  228. package/examples/workspace-provider-plugin/package.json +25 -0
  229. package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
  230. package/examples/workspace-provider-plugin/src/server.ts +64 -0
  231. package/examples/workspace-provider-plugin/tsconfig.json +21 -0
  232. package/package.json +30 -11
  233. package/server-plugin-api.d.ts +1 -0
  234. package/dist/client/assets/CodeViewer-D91Sp61M.js +0 -4
  235. package/dist/client/assets/UnifiedDiffViewer-BafLWGtF.js +0 -39
  236. package/dist/client/assets/index-CA8q9_o7.js +0 -4025
  237. package/dist/client/assets/vendor-editor-core-vUi74DnD.js +0 -12
  238. package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +0 -46
  239. package/dist/pi-web-plugins/relays/package.json +0 -9
  240. package/dist/plugin-api/unstable.d.ts +0 -22
  241. package/dist/server/activity/workspaceActivityRoutes.js +0 -4
  242. package/dist/server/activity/workspaceActivityRoutes.js.map +0 -1
  243. package/dist/server/git/gitService.js.map +0 -1
  244. package/dist/server/gitRoutes.js +0 -23
  245. package/dist/server/gitRoutes.js.map +0 -1
  246. package/dist/server/sessions/parentSessionLocator.js +0 -75
  247. package/dist/server/sessions/parentSessionLocator.js.map +0 -1
  248. package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
  249. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
  250. package/dist/server/workspaces/imagePreviewService.js +0 -40
  251. package/dist/server/workspaces/imagePreviewService.js.map +0 -1
  252. package/dist/server/workspaces/workspaceService.js +0 -52
  253. package/dist/server/workspaces/workspaceService.js.map +0 -1
  254. package/dist/shared/apiTypes.d.ts +0 -1217
  255. package/dist/shared/thinkingLevels.d.ts +0 -27
  256. package/plugin-api/unstable.d.ts +0 -1
  257. /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/README.md +0 -0
  258. /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/marked.esm.js +0 -0
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,11 +11,11 @@ PI WEB uses two config files:
11
11
  - **Global PI WEB config:** `$PI_WEB_CONFIG`, or `$XDG_CONFIG_HOME/pi-web/config.json`, or `~/.config/pi-web/config.json`.
12
12
  - **Project-local PI WEB config:** `<project>/.pi-web/config.json` for commit-able project settings.
13
13
 
14
- Each PI WEB machine has its own config. When using Fleet/machine federation, Settings uses the selected machine for config that affects work running there: the Pi-compatible agent profile and companion CLI, session daemon tools, PI WEB plugin enablement, external path access, and upload defaults. Gateway/browser-only settings stay local to the gateway: keyboard shortcuts, remote machine registry/tokens, and gateway host/port/allowed-hosts.
14
+ Each PI WEB machine has its own config. When using Fleet/machine federation, Settings uses the selected machine for config that affects work running there: 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
- 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`.
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 active drop-ins, `EnvironmentFile=` inputs, stale manager state, a different loaded fragment, or an effective environment mismatch make the loaded definition untrustworthy. 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.
19
19
 
20
20
  ## Reverse-proxy deployment paths
21
21
 
@@ -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": {
@@ -105,7 +102,7 @@ PI WEB also honors one optional project hook; see [Worktree pre-remove hook](#wo
105
102
 
106
103
  ## Worktree pre-remove hook
107
104
 
108
- Before PI WEB deletes a workspace (a secondary Git worktree), it gives the repository one chance to tear down project-owned infrastructure tied to that worktree. To use the hook, provide an executable script at:
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:
109
106
 
110
107
  ```text
111
108
  .pi-web/hooks/worktree-pre-remove
@@ -116,15 +113,17 @@ relative to the workspace where the deletion command runs. PI WEB runs the delet
116
113
  When the hook is present and executable, PI WEB dispatches the hook and the removal as one composed terminal command:
117
114
 
118
115
  ```sh
119
- '<hook path>' '<worktree path>' && git worktree remove '<worktree path>'
116
+ '<hook path>' '<workspace path>' && <workspace removal command>
120
117
  ```
121
118
 
119
+ For Git projects the removal command is `git worktree remove '<worktree path>'`.
120
+
122
121
  Contract:
123
122
 
124
- - **Arguments:** exactly one — the absolute path of the worktree being deleted.
125
- - **Working directory:** the workspace the deletion command runs in, not the worktree being deleted.
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.
126
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.
127
- - **Absent hook:** a missing file, or a file without the executable bit (for example after a checkout that lost it), is treated as no hook; PI WEB then runs a plain `git worktree remove`.
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.
128
127
 
129
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.
130
129
 
@@ -144,7 +143,7 @@ worktree_path="$1"
144
143
 
145
144
  ## Configuration matrix
146
145
 
147
- Rows with JSON key `—` are runtime-only environment variables, not config-file keys. `Global` means machine-global. In Settings, selected-machine-safe global keys (`pathAccess`, `uploads`, `maxUploadBytes`, `agent`, `spawnSessions`, `subsessions`, `askUser`, and `plugins`) are edited for the selected machine; gateway host/port/allowed-hosts, keyboard shortcuts, and machine registry/tokens stay local.
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.
148
147
 
149
148
  | Config | JSON key | Env var | Scope | Project-local behavior | Applies / restart |
150
149
  | --- | --- | --- | --- | --- | --- |
@@ -155,13 +154,13 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
155
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 |
156
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 |
157
156
  | Upload/body limit | `maxUploadBytes` | `PI_WEB_MAX_UPLOAD_BYTES` | Global | Not supported locally | Restart web/API and session daemon on that machine |
158
- | 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 |
159
- | Agent profile state directory | `agent.dir` | `PI_WEB_AGENT_DIR` (`PI_CODING_AGENT_DIR` for Pi compatibility) | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects auth, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugins |
160
157
  | Agent can spawn sessions | `spawnSessions` | `PI_WEB_SPAWN_SESSIONS` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
161
- | 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 |
162
159
  | Agent can post question forms | `askUser` | `PI_WEB_ASK_USER` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
163
160
  | Extension dialog auto-cancel timeout | `extensionDialogsTimeoutMs` | — | Global/session daemon | Not supported locally | Restart session daemon on that machine |
164
- | Plugin enablement/settings | `plugins.<id>.enabled`, `plugins.<id>.settings` | — | Global | Not core local config; plugins may read their own project files | Reload browser tab |
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 |
165
164
  | Keyboard shortcuts | `shortcuts.<actionId>` | — | Global | Not supported locally | Applies after settings save/config refresh |
166
165
  | Project config version | `version` | — | Project | Project-local only; must be `1` when present | Next project-config read |
167
166
  | **Runtime-only environment variables** | | | | | |
@@ -173,8 +172,8 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
173
172
  | Web-to-daemon URL | — | `PI_WEB_SESSIOND_URL` | Web/API env | Not supported locally | Restart web/API |
174
173
  | Projects storage file | — | `PI_WEB_PROJECTS_FILE` | Web/API + session daemon env | Not supported locally | Restart services; advanced state override |
175
174
  | Remote machines storage file | — | `PI_WEB_MACHINES_FILE` | Web/API env | Not supported locally | Restart web/API; advanced state override |
176
- | 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 |
177
- | 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 |
178
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 |
179
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) |
180
179
 
@@ -186,11 +185,17 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
186
185
 
187
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.
188
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
+
189
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`.
190
191
 
191
192
  ### Agent process environment
192
193
 
193
- Agent shells, terminals, and spawned sessions do not inherit the session daemon's own configuration. When the daemon starts, it removes its `PI_WEB_*` configuration keys, `NODE_ENV`, `PORT`, and `PI_CODING_AGENT_SESSION_DIR` from the environment agent processes see, so development commands behave normally inside sessions — for example, `npm install` is not affected by a production `NODE_ENV` meant for the daemon, and a second PI WEB instance started from a session does not pick up the live daemon's data directory or socket. `PI_CODING_AGENT_DIR` and ordinary variables (`PATH`, `HOME`, proxy settings, and the like) remain visible. The daemon itself keeps using the values it captured at startup.
194
+ 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.
194
199
 
195
200
  ### External path access
196
201
 
@@ -235,34 +240,29 @@ For machine federation, Settings saves the global upload default on the selected
235
240
 
236
241
  The per-request size limit is still controlled by `maxUploadBytes` / `PI_WEB_MAX_UPLOAD_BYTES` on the machine serving the upload.
237
242
 
238
- ### Pi-compatible agent profile and companion CLI
243
+ ### Agent state directory
239
244
 
240
- `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`.
241
246
 
242
- `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.
243
248
 
244
- ```json
245
- {
246
- "agent": {
247
- "command": "pi-lab",
248
- "dir": "/opt/pi-profiles/lab"
249
- }
250
- }
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
251
253
  ```
252
254
 
253
- 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.
254
-
255
- 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.
256
256
 
257
- The session daemon resolves the persisted desired values plus its environment once at startup. That secret-free active profile stays fixed for the daemon lifetime. **Settings → Session daemon** saves command and directory together as desired configuration and shows whether the profile is active, needs a restart, or cannot be compared. Until the daemon restarts, sessions, Pi package operations, Pi-package-backed PI WEB plugin discovery, status/install detection, and update planning continue to use the daemon-owned active profile; a web/API restart recovers that same active profile instead of applying the newly saved values.
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.
258
258
 
259
- If the session daemon cannot report a valid active profile, profile-dependent Pi package and PI WEB plugin operations report unavailable instead of falling back to independently resolved config. A package-managed update command is shown only when PI WEB can preserve the active profile with a recognized, safe Pi companion CLI; otherwise the command is omitted. Restart the session daemon on the selected machine to establish the next active profile.
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.
260
260
 
261
261
  ### Pi extension provider baseline
262
262
 
263
- 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).
264
264
 
265
- PI WEB shares one model runtime across all sessions. When the session daemon starts, before any project resources load, it initializes global Pi extensions from the active agent profile (`agent.dir`), including extensions supplied by globally configured Pi packages. Provider registrations made by synchronous or awaited asynchronous extension factories during this bootstrap join the shared baseline. PI WEB captures both config-form registrations (`pi.registerProvider("id", config)`) and native-provider registrations (`pi.registerProvider(provider)`), alongside Pi built-ins, environment credentials, and providers from the active agent directory's `models.json`.
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`.
266
266
 
267
267
  After startup capture, a provider's connection settings are fixed for the daemon lifetime. Later attempts to add a provider, replace an existing provider's configuration, register a native provider, or unregister a provider are no-ops, regardless of source or provider ID. This includes project extensions attempting to add or replace a provider, lifecycle callbacks such as `session_start`, and `/reload`. Non-provider Pi extension features continue to load and reload normally.
268
268
 
@@ -285,7 +285,7 @@ Ignored mutations are written to the session-daemon log once per operation and p
285
285
 
286
286
  This prevents accidental provider, configuration, or credential contamination between projects; it is not a security boundary because Pi extensions remain trusted daemon code.
287
287
 
288
- Configure providers before the daemon starts: use the active agent directory's `models.json`, or install the Pi extension globally in that agent profile. Project Pi extensions and project-level `models.json` files cannot add providers to PI WEB's shared baseline. After updating PI WEB—or after installing, removing, or updating a global Pi extension that registers providers—manually restart `pi-web-sessiond.service` (`systemctl --user restart pi-web-sessiond`). Restarting only the web/API service and running `/reload` do not rebuild the baseline.
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.
289
289
 
290
290
  ### Background model catalog refresh
291
291
 
@@ -302,16 +302,33 @@ Each run is bounded: it is aborted after **60 seconds**, and a run that times ou
302
302
 
303
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.
304
304
 
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 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.
306
+
307
+ ### Project trust for project-local resources
308
+
309
+ PI WEB always honors Pi's project-trust settings before loading a workspace's project-local `.pi/` resources — `.pi/extensions/*`, the `packages` declared in `.pi/settings.json`, and the other `.pi/` settings and resources. There is no opt-out config key: trust applies at every session start, the way `pi` itself applies it.
310
+
311
+ A project-local `.pi/extension` is arbitrary code that runs inside the agent process on every session for that workspace, so an untrusted workspace must not load one.
312
+
313
+ Trust is resolved the way `pi` resolves it with no trust prompt to show:
314
+
315
+ - A workspace with no trust-requiring `.pi/` resources is always loaded (there is nothing to gate).
316
+ - User/global extensions (loaded before the decision, exactly as `pi` does) may decide trust through the `project_trust` extension event, and may request `remember` to persist their decision to the agent directory's `trust.json`. When the event decides, it wins — the same order `pi` uses.
317
+ - Otherwise a saved decision in the agent directory's `trust.json` (from the Pi CLI's trust prompt, the workspace trust toggle, or a `remember`-ing extension) wins.
318
+ - Otherwise the agent's `defaultProjectTrust` setting decides: `always` loads the project resources, and `never` skips them. `ask` skips them too, because PI WEB has no browser trust prompt yet and a non-interactive `pi` also treats `ask` as untrusted.
319
+
320
+ This mirrors the Pi CLI: with `defaultProjectTrust: "never"`, an opened workspace's `.pi/` extensions and packages are ignored rather than loaded silently.
306
321
 
307
322
  ### Session daemon tools
308
323
 
309
324
  `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.
310
325
 
311
- `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.
326
+ `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.
312
327
 
313
328
  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.
314
329
 
330
+ 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.
331
+
315
332
  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.
316
333
 
317
334
  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`.
@@ -346,22 +363,58 @@ Pi extensions can ask the user questions from `ctx.ui.confirm()`, `ctx.ui.select
346
363
 
347
364
  The key is edited directly in the global config file. Restart the session daemon after changing it — for the systemd user service, run `systemctl --user restart pi-web-sessiond`.
348
365
 
349
- ### Plugin config
366
+ ### PI WEB plugin config and recovery
350
367
 
351
- The `plugins` key is only for PI WEB browser plugin enablement/settings on the machine whose config you are editing. It does not install, remove, or update Pi packages; use **Settings → Pi packages** or Pi's package manager for package operations. In a federated setup, **Settings → PI WEB plugins** and **Settings → Pi packages** both target the currently selected machine, and each panel labels where changes will be saved or run.
352
-
353
- 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.
368
+ 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.
354
369
 
355
370
  ```json
356
371
  {
357
372
  "plugins": {
358
- "workspace-tasks": { "enabled": true, "settings": {} },
373
+ "git": { "enabled": true, "settings": {} },
374
+ "workspace-tasks": { "enabled": true },
359
375
  "updates": { "enabled": false }
360
376
  }
361
377
  }
362
378
  ```
363
379
 
364
- Reload the browser tab after changing plugin enablement. Already-loaded plugin JavaScript is not unloaded from the current page.
380
+ 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.
381
+
382
+ #### Desired versus active plugin state
383
+
384
+ 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.
385
+
386
+ **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.
387
+
388
+ 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.
389
+
390
+ 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.
391
+
392
+ Apply changes in this order:
393
+
394
+ 1. Install or update the package on the target machine.
395
+ 2. Save desired enablement/settings.
396
+ 3. For a browser-only plugin, reload the browser tab.
397
+ 4. For a server-backed plugin, manually restart the target session daemon, wait for it, then reload the browser tab.
398
+
399
+ > **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.
400
+
401
+ #### Offline disable and safe start
402
+
403
+ 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`.
404
+
405
+ ```bash
406
+ pi-web plugins disable <plugin-id> --restart
407
+ pi-web plugins safe-start show
408
+ pi-web plugins safe-start set bundled-only --restart
409
+ pi-web plugins safe-start set none --restart
410
+ pi-web plugins safe-start clear --restart
411
+ ```
412
+
413
+ `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.
414
+
415
+ `--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.
416
+
417
+ 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.
365
418
 
366
419
  ### Shortcut config
367
420