@jmfederico/pi-web 1.202607.2 → 1.202607.3

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 (68) hide show
  1. package/dist/client/assets/{CodeViewer-B4M13447.js → CodeViewer-CDVbMiN9.js} +1 -1
  2. package/dist/client/assets/{TerminalPanel-Bmvj-Ecn.js → TerminalPanel-CTS1CgqF.js} +2 -2
  3. package/dist/client/assets/{UnifiedDiffViewer-C0hygwRo.js → UnifiedDiffViewer-Cqke12ZX.js} +1 -1
  4. package/dist/client/assets/index-LME0LfPb.js +4068 -0
  5. package/dist/client/index.html +1 -1
  6. package/dist/config.js +39 -0
  7. package/dist/config.js.map +1 -1
  8. package/dist/pi-web-plugins/info/infoInternals.js +208 -0
  9. package/dist/pi-web-plugins/info/pi-web-plugin.js +12 -15
  10. package/dist/pi-web-plugins/relays/markdownDocument.js +81 -0
  11. package/dist/pi-web-plugins/relays/package.json +9 -0
  12. package/dist/pi-web-plugins/relays/pi-web-plugin.js +44 -0
  13. package/dist/pi-web-plugins/relays/relayDiscovery.js +96 -0
  14. package/dist/pi-web-plugins/relays/relaysPanelElement.js +393 -0
  15. package/dist/pi-web-plugins/relays/vendor/README.md +24 -0
  16. package/dist/pi-web-plugins/relays/vendor/marked.esm.js +76 -0
  17. package/dist/plugin-api.d.ts +7 -1
  18. package/dist/server/configRoutes.js +11 -0
  19. package/dist/server/configRoutes.js.map +1 -1
  20. package/dist/server/sessiond/sessionServiceDependencies.js +32 -0
  21. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -0
  22. package/dist/server/sessiond.js +122 -114
  23. package/dist/server/sessiond.js.map +1 -1
  24. package/dist/server/sessions/askUserTool.js +105 -0
  25. package/dist/server/sessions/askUserTool.js.map +1 -0
  26. package/dist/server/sessions/extensionDialogWaiters.js +76 -0
  27. package/dist/server/sessions/extensionDialogWaiters.js.map +1 -0
  28. package/dist/server/sessions/globalProviderPolicy.js +79 -10
  29. package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
  30. package/dist/server/sessions/modelCatalogRefresher.js +8 -0
  31. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  32. package/dist/server/sessions/parentSessionLocator.js +75 -0
  33. package/dist/server/sessions/parentSessionLocator.js.map +1 -0
  34. package/dist/server/sessions/pendingAskStore.js +278 -0
  35. package/dist/server/sessions/pendingAskStore.js.map +1 -0
  36. package/dist/server/sessions/pendingExtensionDialogStore.js +196 -0
  37. package/dist/server/sessions/pendingExtensionDialogStore.js.map +1 -0
  38. package/dist/server/sessions/piSessionService.js +504 -44
  39. package/dist/server/sessions/piSessionService.js.map +1 -1
  40. package/dist/server/sessions/sessionFileHeader.js +45 -0
  41. package/dist/server/sessions/sessionFileHeader.js.map +1 -0
  42. package/dist/server/sessions/sessionRoutes.js +99 -2
  43. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  44. package/dist/server/sessions/spawnTargetResolver.js +3 -16
  45. package/dist/server/sessions/spawnTargetResolver.js.map +1 -1
  46. package/dist/server/workspaces/gitWorktreeDiscovery.js +9 -0
  47. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -1
  48. package/dist/server/workspaces/projectWorkspaceCwds.js +22 -0
  49. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -0
  50. package/dist/server/workspaces/workspaceService.js +15 -2
  51. package/dist/server/workspaces/workspaceService.js.map +1 -1
  52. package/dist/shared/activity.js +9 -1
  53. package/dist/shared/activity.js.map +1 -1
  54. package/dist/shared/apiTypes.d.ts +294 -1
  55. package/dist/shared/apiTypes.js +24 -0
  56. package/dist/shared/apiTypes.js.map +1 -1
  57. package/dist/shared/capabilities.js +3 -0
  58. package/dist/shared/capabilities.js.map +1 -1
  59. package/dist/shared/federatedRoutes.js +4 -0
  60. package/dist/shared/federatedRoutes.js.map +1 -1
  61. package/docs/config.md +56 -7
  62. package/docs/plugins.md +77 -11
  63. package/package.json +2 -1
  64. package/dist/client/assets/index-TvPsRyS1.js +0 -3545
  65. package/dist/server/sessiond/sessionDaemonStartup.js +0 -49
  66. package/dist/server/sessiond/sessionDaemonStartup.js.map +0 -1
  67. package/dist/server/sessions/sessionArchiveMigration.js +0 -590
  68. package/dist/server/sessions/sessionArchiveMigration.js.map +0 -1
package/docs/plugins.md CHANGED
@@ -29,6 +29,20 @@ Use **Settings → PI WEB plugins** to enable or disable discovered PI WEB brows
29
29
 
30
30
  After installing, removing, or updating a Pi package, type `/reload` in each idle PI WEB session on the target machine to refresh ordinary Pi resources such as extensions, skills, prompt templates, themes, and context/system prompt files. Reload the browser page separately for newly discovered or changed PI WEB browser plugins. A provider-registering Pi extension follows a separate daemon-start policy; see [Pi extension provider baseline](https://pi-web.dev/config#pi-extension-provider-baseline).
31
31
 
32
+ ## Pi extension dialogs in PI WEB
33
+
34
+ Pi extensions running under PI WEB's session daemon can ask the user questions with `ctx.ui.confirm()`, `ctx.ui.select()`, and `ctx.ui.input()`. PI WEB reports `ctx.hasUI === true`, and for these three dialog methods that is true in fact: the call renders a dialog card inline in the session transcript and the returned Promise resolves with the user's actual answer — a boolean for confirm, the chosen option for select, the typed text for input.
35
+
36
+ - **Works from hooks, without the prompt queue.** Answers travel over a dedicated session-daemon channel, so a dialog opened inside an in-flight `tool_call` hook parks safely — the agent loop waits for the hook and the run continues with the answer. Consent-gating a tool from a `tool_call` hook is a supported pattern.
37
+ - **`session_start` dialogs are reachable.** A dialog opened from a `session_start` hook is answerable while the session is still starting, both when creating a session and when opening an existing one; startup completes once the dialog settles.
38
+ - **Survives browser reloads; first answer wins.** Reloading the browser re-renders open dialogs from the session status. With several tabs on the same session, the first answer settles the dialog and the other tabs re-render the settled card.
39
+ - **Settled cards stay until dismissed.** An answered or closed dialog leaves its outcome card in the transcript so the user can see what became of it — answers travel to the extension alone, so the card is the only record of the exchange. The card is browser-local: only a browser that saw the dialog open renders it, and switching sessions or reloading drops it.
40
+ - **Timeouts.** The extension's own `timeout` option applies, and the daemon adds an unattended-dialog safety valve, `extensionDialogsTimeoutMs` (default 5 minutes, `0` waits forever — see [Extension dialogs](https://pi-web.dev/config#extension-dialogs)). The effective deadline is the sooner of the two. A dialog that closes without an answer resolves with its kind's cancel value: `false` for confirm, `undefined` for select and input.
41
+ - **Abort and runtime replacement.** Aborting the current run settles a dialog opened during that run immediately, at abort-request time, with its cancel value. Replacing the session runtime (`/reload`, session disposal) settles any still-open dialog the same way; hooks on the new runtime open fresh dialogs. The extension's own `AbortSignal` is honored: aborting it dismisses the dialog and resolves with the cancel value.
42
+ - **Other UI surfaces are still no-ops.** `ExtensionUIContext` methods beyond the three dialogs (widgets, status, editor, `custom`) remain unimplemented under PI WEB even though `hasUI` is `true`; do not rely on `hasUI` alone to detect them.
43
+
44
+ One browser-local caveat: reloading the browser while a new session is still being created loses the browser-local pending-start row, so the dialog card disappears from view. The daemon-side dialog still settles at its deadline and the session appears in the sidebar once creation completes.
45
+
32
46
  ## Trust model
33
47
 
34
48
  Plugins run as JavaScript in the browser app. Treat them as trusted code:
@@ -90,8 +104,11 @@ Source files:
90
104
  ```text
91
105
  pi-web-plugins/info/package.json
92
106
  pi-web-plugins/info/pi-web-plugin.ts
107
+ pi-web-plugins/info/infoInternals.ts
93
108
  ```
94
109
 
110
+ `pi-web-plugin.ts` is the plugin skeleton: metadata plus contribution definitions. `infoInternals.ts` holds everything the bundled panel and action actually render, so you can ignore or replace it when copying the plugin.
111
+
95
112
  Built module:
96
113
 
97
114
  ```text
@@ -130,6 +147,8 @@ export default {
130
147
 
131
148
  When copying the Info plugin, choose a new plugin id so it does not conflict with the bundled `info` plugin.
132
149
 
150
+ The Info panel doubles as an always-available PI WEB status view: it renders the host-provided `context.state.piWebStatus` (versions, installation, release state, machine, and workspace details) without issuing its own requests, and its action copies a plain-text diagnostics summary suitable for bug reports.
151
+
133
152
  PI WEB also ships an `updates` plugin that demonstrates dynamic `visible` and `badge` callbacks for tabs that only appear when the host has status messages or needs extra install visibility.
134
153
 
135
154
  ## Local plugin usage
@@ -275,6 +294,25 @@ Task fields:
275
294
 
276
295
  Review task configs before running them, especially in shared projects. Workspace Tasks runs trusted shell commands from your repositories.
277
296
 
297
+ ### Relays
298
+
299
+ **Plugin id:** `relays`
300
+ **What it does:** adds a read-only **Relays** workspace tab for browsing the workspace's relays, plus an **Open Workspace Relays** action for the selected workspace that opens the same tab.
301
+
302
+ A relay is a directory of markdown notes under `.pi-web/relays/<name>/` in the workspace root — the convention used by the Relay method for chaining agent sessions. The tab lists each relay's documents with `status.md`, `charter.md`, and `log.md` first (in that order), followed by any other files alphabetically, and opens `status.md` by default. Markdown documents render as sanitized HTML; other files render as preformatted text, and binary files have no preview. Truncated documents show a notice, and **Refresh** re-scans the workspace and reloads the open document.
303
+
304
+ With several relays, a picker pre-selects the most recently modified one; a single relay opens directly. A workspace without `.pi-web/relays/` shows an empty state explaining the convention. The tab never creates, edits, or deletes relay files.
305
+
306
+ Relays is enabled by default. To hide it, disable `relays` in **Settings → PI WEB plugins** or set:
307
+
308
+ ```json
309
+ {
310
+ "plugins": {
311
+ "relays": { "enabled": false }
312
+ }
313
+ }
314
+ ```
315
+
278
316
  ## Discovery and packaging
279
317
 
280
318
  PI WEB builds the gateway `/pi-web-plugins/manifest.json` from these sources:
@@ -433,14 +471,13 @@ Actions appear in the action palette. They can inspect app state and call UI/run
433
471
  ```js
434
472
  actions: [
435
473
  {
436
- id: "workspace.show-path",
437
- title: "Show Current Workspace Path",
438
- description: "Display the selected workspace path",
439
- shortcut: "mod+shift+p",
474
+ id: "copy-diagnostics",
475
+ title: "Copy PI WEB Diagnostics",
476
+ description: "Copy version, installation, and status details for this machine",
440
477
  group: "Info",
441
- enabled: (context) => context.state.selectedWorkspace !== undefined,
442
- run: (context) => {
443
- window.alert(context.state.selectedWorkspace?.path ?? "No workspace selected");
478
+ run: async (context) => {
479
+ const version = context.state.piWebStatus?.components.web.runtimeVersion ?? "unknown";
480
+ await navigator.clipboard.writeText(`PI WEB ${version}`);
444
481
  },
445
482
  },
446
483
  ]
@@ -468,6 +505,7 @@ Stable runtime context fields:
468
505
  ```ts
469
506
  interface PluginRuntimeContext {
470
507
  state: {
508
+ selectedMachine?: PluginMachine;
471
509
  selectedWorkspace?: Workspace;
472
510
  selectedSession?: unknown;
473
511
  piWebStatus?: PiWebStatusResponse;
@@ -492,7 +530,7 @@ interface PluginRuntimeContext {
492
530
  Notes:
493
531
 
494
532
  - `state` is a snapshot of current UI state when actions are built.
495
- - The stable state fields are `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`. `state.piWebStatus` describes the currently selected machine's PI WEB runtime, or the gateway/local runtime when the local machine is selected.
533
+ - The stable state fields are `state.selectedMachine`, `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`. `state.selectedMachine` identifies the currently selected machine. `state.piWebStatus` describes the currently selected machine's PI WEB runtime, or the gateway/local runtime when the local machine is selected.
496
534
  - Other `state` fields may exist at runtime, but they are private PI WEB internals that may graduate into stable helpers, change shape, or disappear.
497
535
  - `enabled` is evaluated when the action palette asks for actions.
498
536
  - `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
@@ -580,6 +618,7 @@ interface WorkspacePanelContext {
580
618
  state?: PluginRuntimeState;
581
619
  files: {
582
620
  readFile(path: string): Promise<FileContentResponse>;
621
+ listFiles(path: string): Promise<FileTreeResponse>;
583
622
  writeFile(path: string, content: string | Uint8Array, options?: WriteWorkspaceFileOptions): Promise<WriteWorkspaceFileResponse>;
584
623
  deleteFile(path: string): Promise<DeleteWorkspaceFileResponse>;
585
624
  moveFile(fromPath: string, toPath: string, options?: MoveWorkspaceFileOptions): Promise<MoveWorkspaceFileResponse>;
@@ -602,7 +641,7 @@ interface WorkspacePanelContext {
602
641
 
603
642
  `icon` is optional and is used in the compact mobile tab bar. Prefer an SVG rendered with the `svg` helper from `PluginActivationContext`; use `currentColor` so PI WEB themes can style it. If `icon` is omitted, mobile tabs fall back to initials from the panel title, or to the full title when initials collide.
604
643
 
605
- `machine`, `workspace`, `files`, `prompt`, `terminal`, and `host` are documented as stable for panel callbacks. The `files` helper supports `readFile`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files) and [Writing workspace files](#writing-workspace-files). The `prompt` helper supports panel interactions that insert workspace context into the current prompt — see [Prompt editor API](#prompt-editor-api). Use `terminal.open()` to switch to the built-in terminal panel; pass `{ terminalId }` to deep-link to a specific terminal. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate panel callbacks such as `badge`, `visible`, or `render`.
644
+ `machine`, `workspace`, `files`, `prompt`, `terminal`, and `host` are documented as stable for panel callbacks. The `files` helper supports `readFile`, `listFiles`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files), [Listing workspace files](#listing-workspace-files), and [Writing workspace files](#writing-workspace-files). The `prompt` helper supports panel interactions that insert workspace context into the current prompt — see [Prompt editor API](#prompt-editor-api). Use `terminal.open()` to switch to the built-in terminal panel; pass `{ terminalId }` to deep-link to a specific terminal. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate panel callbacks such as `badge`, `visible`, or `render`.
606
645
 
607
646
  For compatibility, PI WEB still provides the old `context.openTerminal()` workspace-panel helper at runtime. It is deprecated, intentionally omitted from the public TypeScript declarations, and planned for removal in v2. Existing JavaScript plugins keep working, while typed plugins should migrate to `context.terminal.open()`.
608
647
 
@@ -670,6 +709,7 @@ interface WorkspaceLabelContext {
670
709
  state?: PluginRuntimeState;
671
710
  files: {
672
711
  readFile(path: string): Promise<FileContentResponse>;
712
+ listFiles(path: string): Promise<FileTreeResponse>;
673
713
  writeFile(path: string, content: string | Uint8Array, options?: WriteWorkspaceFileOptions): Promise<WriteWorkspaceFileResponse>;
674
714
  deleteFile(path: string): Promise<DeleteWorkspaceFileResponse>;
675
715
  moveFile(fromPath: string, toPath: string, options?: MoveWorkspaceFileOptions): Promise<MoveWorkspaceFileResponse>;
@@ -680,7 +720,7 @@ interface WorkspaceLabelContext {
680
720
  }
681
721
  ```
682
722
 
683
- `machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. The `files` helper supports `readFile`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files) and [Writing workspace files](#writing-workspace-files). Include `machine.id` in any label caches that depend on workspace data. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate label `visible` or `items` callbacks.
723
+ `machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. The `files` helper supports `readFile`, `listFiles`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files), [Listing workspace files](#listing-workspace-files), and [Writing workspace files](#writing-workspace-files). Include `machine.id` in any label caches that depend on workspace data. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate label `visible` or `items` callbacks.
684
724
 
685
725
  Items are sorted by `order` and then id. Return an empty array to render nothing. Keep callbacks synchronous and lightweight; start async work from the callback, return cached items, then call `host.requestRender()` when the cache changes.
686
726
 
@@ -819,6 +859,32 @@ workspaceLabels: [
819
859
 
820
860
  The file response includes fields such as `path`, `content`, `truncated`, and `binary`. Be careful with sensitive files such as `.env`: plugins are trusted browser code, and file contents are exposed to the plugin.
821
861
 
862
+ ## Listing workspace files
863
+
864
+ `files.listFiles(path)` lists the entries of a workspace directory. Pass `""` for the workspace root. Like `readFile`, PI WEB binds the call to the callback's machine and workspace, so it works the same for local and federated machines.
865
+
866
+ ```js
867
+ const listing = await context.files.listFiles("src");
868
+ for (const entry of listing.entries) {
869
+ // entry: { name, path, type: "file" | "directory" | "symlink", size?, modifiedAt? }
870
+ }
871
+ ```
872
+
873
+ The listing response includes `path`, `entries`, `scannedAt`, and `truncated`. When `truncated` is true, the server cut the listing short, so treat the entries as partial.
874
+
875
+ `listFiles` rejects when the directory does not exist or cannot be read, matching `readFile` error behavior. When a directory is optional, catch the error and treat it as an empty listing:
876
+
877
+ ```js
878
+ async function listSubdirectoryNames(context, path) {
879
+ try {
880
+ const listing = await context.files.listFiles(path);
881
+ return listing.entries.filter((entry) => entry.type === "directory").map((entry) => entry.name);
882
+ } catch {
883
+ return [];
884
+ }
885
+ }
886
+ ```
887
+
822
888
  ## Writing, deleting, and moving workspace files
823
889
 
824
890
  Workspace panels and workspace labels can write, delete, and move files through the documented `files` helper. Like `readFile`, PI WEB binds these helpers to the callback's machine and workspace, so they work the same for local and federated machines.
@@ -957,7 +1023,7 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
957
1023
  9. Add workspace panels for larger workspace UI.
958
1024
  10. Add workspace labels for compact inline metadata.
959
1025
  11. Return arrays from workspace label `items()`; return an empty array to render nothing.
960
- 12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebStatus`, and `prompt`.
1026
+ 12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedMachine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebStatus`, and `prompt`.
961
1027
  13. Do not fetch PI WEB `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers.
962
1028
  14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
963
1029
  15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmfederico/pi-web",
3
- "version": "1.202607.2",
3
+ "version": "1.202607.3",
4
4
  "description": "Web UI for persistent Pi Coding Agent sessions in real workspaces.",
5
5
  "license": "MIT",
6
6
  "author": "Federico Jaramillo Martinez",
@@ -91,6 +91,7 @@
91
91
  "@types/ws": "^8.18.1",
92
92
  "eslint": "^10.6.0",
93
93
  "globals": "^17.7.0",
94
+ "happy-dom": "^20.11.1",
94
95
  "knip": "^6.25.0",
95
96
  "tsx": "^4.23.0",
96
97
  "typescript": "^6.0.3",