@jmfederico/pi-web 1.202610.1 → 1.202610.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 (148) hide show
  1. package/README.md +23 -134
  2. package/dist/cli.js +4 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/index-BAl5IbYx.js +4429 -0
  5. package/dist/client/assets/vendor-editor-core-C2PJX3uW.js +10 -0
  6. package/dist/client/assets/{vendor-editor-languages-DOCw3vc9.js → vendor-editor-languages-CS6n1J8n.js} +1 -1
  7. package/dist/client/index.html +4 -48
  8. package/dist/config.js +8 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/pi-packages/captains-log/dist/browser/index.js +30 -32
  11. package/dist/pi-packages/captains-log/dist/companion.js +1 -1
  12. package/dist/pi-packages/captains-log/package.json +4 -4
  13. package/dist/pi-packages/relays/extensions/index.js +60 -0
  14. package/dist/pi-packages/relays/package.json +10 -1
  15. package/dist/pi-packages/relays/pi-web-plugin.js +31 -1
  16. package/dist/pi-packages/relays/prompts/relay-worktree.md +6 -16
  17. package/dist/pi-packages/relays/prompts/relay.md +6 -33
  18. package/dist/pi-packages/relays/relayDispatch.js +67 -0
  19. package/dist/pi-packages/relays/relayIdentity.js +37 -0
  20. package/dist/pi-packages/relays/relaysPanelElement.js +37 -12
  21. package/dist/pi-packages/relays/skills/relay/SKILL.md +24 -68
  22. package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +40 -219
  23. package/dist/pi-packages/workflows/browser/pi-web-plugin.js +33 -0
  24. package/dist/pi-packages/workflows/package.json +22 -0
  25. package/dist/pi-packages/workflows/prompts/worktree.md +12 -0
  26. package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +49 -51
  27. package/dist/pi-web-plugins/git/browser/git-panel.js +0 -1
  28. package/dist/pi-web-plugins/git/server-plugin.js +13 -2
  29. package/dist/pi-web-plugins/git/workspace-discovery-notify.cjs +14 -0
  30. package/dist/pi-web-plugins/git/workspaceDiscoveryHooks.js +186 -0
  31. package/dist/pi-web-plugins/info/infoInternals.js +3 -3
  32. package/dist/pi-web-plugins/info/pi-web-plugin.js +1 -1
  33. package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +1087 -422
  34. package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +30 -30
  35. package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
  36. package/dist/piWebUpdate.js +2 -2
  37. package/dist/piWebUpdate.js.map +1 -1
  38. package/dist/plugin-api.d.ts +229 -8
  39. package/dist/server/activity/workspaceActivityService.js +4 -1
  40. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  41. package/dist/server/app.js +5 -0
  42. package/dist/server/app.js.map +1 -1
  43. package/dist/server/configRoutes.js +6 -0
  44. package/dist/server/configRoutes.js.map +1 -1
  45. package/dist/server/knownAutoInstallPiPackages.js +6 -0
  46. package/dist/server/knownAutoInstallPiPackages.js.map +1 -1
  47. package/dist/server/navigationPreferenceRoutes.js +39 -0
  48. package/dist/server/navigationPreferenceRoutes.js.map +1 -0
  49. package/dist/server/plugins/projectPluginSettings.js +95 -0
  50. package/dist/server/plugins/projectPluginSettings.js.map +1 -0
  51. package/dist/server/plugins/serverPluginRuntime.js +26 -3
  52. package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
  53. package/dist/server/sessiond/autoInstallPiPackages.js +3 -3
  54. package/dist/server/sessiond/workspaceCatalogRoutes.js +3 -1
  55. package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -1
  56. package/dist/server/sessiond/workspaceNotificationRoutes.js +41 -0
  57. package/dist/server/sessiond/workspaceNotificationRoutes.js.map +1 -0
  58. package/dist/server/sessiond.js +40 -7
  59. package/dist/server/sessiond.js.map +1 -1
  60. package/dist/server/sessions/askUserDomain.js +232 -0
  61. package/dist/server/sessions/askUserDomain.js.map +1 -0
  62. package/dist/server/sessions/askUserTool.js +15 -17
  63. package/dist/server/sessions/askUserTool.js.map +1 -1
  64. package/dist/server/sessions/dockerEnvironmentFacts.js +8 -0
  65. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -1
  66. package/dist/server/sessions/extensionSessionDispatch.js +76 -0
  67. package/dist/server/sessions/extensionSessionDispatch.js.map +1 -0
  68. package/dist/server/sessions/globalProviderPolicy.js +71 -16
  69. package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
  70. package/dist/server/sessions/messageHistoryTarget.js +61 -0
  71. package/dist/server/sessions/messageHistoryTarget.js.map +1 -0
  72. package/dist/server/sessions/piSessionManagerGateway.js +23 -24
  73. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  74. package/dist/server/sessions/piSessionService.js +759 -276
  75. package/dist/server/sessions/piSessionService.js.map +1 -1
  76. package/dist/server/sessions/queuedMessageMutation.js +168 -0
  77. package/dist/server/sessions/queuedMessageMutation.js.map +1 -0
  78. package/dist/server/sessions/sessionArchiveStore.js +5 -1
  79. package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
  80. package/dist/server/sessions/sessionArchiveTree.js +2 -2
  81. package/dist/server/sessions/sessionArchiveTree.js.map +1 -1
  82. package/dist/server/sessions/sessionCommandService.js +9 -3
  83. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  84. package/dist/server/sessions/sessionImageDimensions.js +31 -0
  85. package/dist/server/sessions/sessionImageDimensions.js.map +1 -0
  86. package/dist/server/sessions/sessionMediaIndex.js +7 -1
  87. package/dist/server/sessions/sessionMediaIndex.js.map +1 -1
  88. package/dist/server/sessions/sessionMetadata.js +51 -0
  89. package/dist/server/sessions/sessionMetadata.js.map +1 -0
  90. package/dist/server/sessions/sessionNameGenerator.js +8 -28
  91. package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
  92. package/dist/server/sessions/sessionRoutes.js +20 -2
  93. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  94. package/dist/server/sessions/sessionSummaryScanner.js +62 -5
  95. package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
  96. package/dist/server/sessions/spawnSessionTool.js +3 -1
  97. package/dist/server/sessions/spawnSessionTool.js.map +1 -1
  98. package/dist/server/sessions/spawnSubsessionTool.js +25 -17
  99. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  100. package/dist/server/sessions/subsessionCompletionDelivery.js +15 -0
  101. package/dist/server/sessions/subsessionCompletionDelivery.js.map +1 -0
  102. package/dist/server/sessions/subsessionCompletionInbox.js +134 -0
  103. package/dist/server/sessions/subsessionCompletionInbox.js.map +1 -0
  104. package/dist/server/sessions/subsessionProvenance.js +27 -0
  105. package/dist/server/sessions/subsessionProvenance.js.map +1 -0
  106. package/dist/server/status/machineStatusService.js +17 -5
  107. package/dist/server/status/machineStatusService.js.map +1 -1
  108. package/dist/server/storage/navigationPreferenceStore.js +137 -0
  109. package/dist/server/storage/navigationPreferenceStore.js.map +1 -0
  110. package/dist/server/workspaces/projectPiWebConfig.js +88 -6
  111. package/dist/server/workspaces/projectPiWebConfig.js.map +1 -1
  112. package/dist/server/workspaces/workspaceChangeNotifier.js +12 -0
  113. package/dist/server/workspaces/workspaceChangeNotifier.js.map +1 -0
  114. package/dist/server/workspaces/workspaceNotificationCommand.js +64 -0
  115. package/dist/server/workspaces/workspaceNotificationCommand.js.map +1 -0
  116. package/dist/server/workspaces/workspaceProviderRegistry.js +55 -0
  117. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
  118. package/dist/server-plugin-api.d.ts +18 -0
  119. package/dist/server-plugin-api.js.map +1 -1
  120. package/dist/sessiond/sessionDaemonClient.js +3 -3
  121. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  122. package/dist/shared/apiTypes.js +2 -2
  123. package/dist/shared/apiTypes.js.map +1 -1
  124. package/dist/shared/federatedRoutes.js +6 -0
  125. package/dist/shared/federatedRoutes.js.map +1 -1
  126. package/dist/shared/machineStatus.js +11 -2
  127. package/dist/shared/machineStatus.js.map +1 -1
  128. package/dist/shared/navigationPreferences.js +6 -0
  129. package/dist/shared/navigationPreferences.js.map +1 -0
  130. package/dist/shared/pluginApiTypes.d.ts +2 -0
  131. package/dist/shared/sessionMedia.js +6 -0
  132. package/dist/shared/sessionMedia.js.map +1 -1
  133. package/dist/shared/streamingBehavior.js +7 -0
  134. package/dist/shared/streamingBehavior.js.map +1 -0
  135. package/dist/shared/themePreference.js +23 -0
  136. package/dist/shared/themePreference.js.map +1 -0
  137. package/dist/workspaceNotificationCli.js +95 -0
  138. package/dist/workspaceNotificationCli.js.map +1 -0
  139. package/docs/config.md +111 -86
  140. package/docs/plugins.md +105 -30
  141. package/examples/session-bridge-plugin/package.json +2 -2
  142. package/examples/session-bridge-plugin/src/companion.ts +1 -1
  143. package/examples/workspace-provider-plugin/package.json +1 -1
  144. package/package.json +33 -33
  145. package/dist/client/assets/index-CBjDnBIN.js +0 -4178
  146. package/dist/client/assets/vendor-editor-core-YWog6mAR.js +0 -10
  147. package/dist/server/sessions/pendingAskStore.js +0 -275
  148. package/dist/server/sessions/pendingAskStore.js.map +0 -1
package/docs/plugins.md CHANGED
@@ -9,8 +9,10 @@ This guide explains what is possible and what to expect. For implementation, use
9
9
  | Goal | Plugin feature |
10
10
  | --- | --- |
11
11
  | Show project health, service links, or environment information | Workspace labels and panels |
12
+ | Identify a conversation's workflow and link to its saved context | Session-list labels and public session metadata |
12
13
  | Add a dashboard, file viewer, or project-specific tool | Workspace panels, file helpers, and browser UI |
13
14
  | Make common actions easier to find | Action-palette commands and shortcuts |
15
+ | Act on a chat message | Message-action callbacks |
14
16
  | Run builds, tests, or development servers | Workspace terminal commands |
15
17
  | Customize the appearance | Themes and light/dark theme pairs |
16
18
  | Preview diagrams or other text formats in chat and Files | Browser content renderers |
@@ -40,50 +42,84 @@ Use a Pi extension for agent behavior and a PI WEB plugin for web UI. When a fea
40
42
 
41
43
  A plugin package declares a browser entry, a server entry, or both:
42
44
 
43
- - **Browser entries** add actions, panels, labels, themes, and content renderers. They use host helpers for workspace files, terminals, and prompt editing.
45
+ - **Browser entries** add palette and message actions, panels, labels, themes, and content renderers. They use host helpers for workspace files, terminals, and prompt editing.
44
46
  - **Server entries** run in the session daemon. They can serve their browser entry, store plugin data, use host capabilities, or provide workspaces.
45
47
  - **Package peers** connect a plugin's browser and server entries. The host handles the selected machine and workspace; a plugin does not need to own that workspace to serve it.
46
48
  - **Capabilities** let a plugin declare the host or plugin functionality it requires. Dependencies must be available at the requested version before the plugin starts.
47
49
 
48
- Plugins declare contributions in `activate()`, initialize dependency-backed work in `start()`, and release resources in `dispose()`. Long-lived work follows the plugin's `lifetimeSignal`. Simple browser plugins only need to return their contributions.
50
+ For entry declarations and lifecycle details, use the [public contracts and examples](#agent-development).
51
+
52
+ ### Tabs without a workspace
53
+
54
+ Application panels can show machine dashboards before a project, workspace, or session is selected. Workspace panels require a selected workspace. Both appear in the tool tabs and follow selection changes; terminal-backed actions also require an available Terminal provider.
55
+
56
+ Older hosts may omit application tabs; update PI WEB to use them.
57
+
58
+ ### Follow selection while a tab is closed
59
+
60
+ Plugins can follow the selected machine, project, workspace, and session even while their tab is closed. This provides basic selection information, not the transcript or private session-file path. Older hosts may lack selection observation; update PI WEB when a plugin needs it.
61
+
62
+ ### Read public session metadata
63
+
64
+ Plugins can publish namespaced session metadata to identify workflows and link to saved context. It is browser-public data saved with the conversation, available after reopening without a live Pi connection. Missing metadata is not inferred from titles or conversation text.
65
+
66
+ Only deliberately published session metadata is exposed, not arbitrary Pi custom entries. Keep secrets and private extension state out of it. Namespaces organize data rather than enforce ownership or authorization, and plugins must validate their own schema and render values as untrusted input. Browser API v4 remains unchanged; older hosts omit the field.
67
+
68
+ ### Dispatch a session from a Pi extension
69
+
70
+ Trusted hosted Pi extensions can start an independent conversation in a workspace of the same registered project, with a prompt, name, and public metadata. Model and thinking settings inherit from the initiating conversation unless explicitly overridden. Dispatch respects the same delegation restrictions as `spawn_session`.
71
+
72
+ Older hosts may lack this capability. After upgrading, restart the session daemon when safe to load it; restarting interrupts active sessions and terminals.
73
+
74
+ ### Add indicators to session-list rows
75
+
76
+ Plugins can add text, links, or custom indicators to conversations, including unselected and archived rows. Indicators can use public session metadata to open saved workflow context on the row's machine and workspace without a live Pi connection.
77
+
78
+ They appear only in the session list, not the top Session pill or transcript bubbles. Older hosts ignore session labels; update PI WEB to use them.
79
+
80
+ ### Discover projects on the context's machine
81
+
82
+ Plugins can list registered projects and suggest directories using the same search as **Add Project**, even before a project is selected. Discovery is read-only and stays on the initiating machine; remote failures do not substitute gateway projects. Older hosts may lack project discovery; update PI WEB when a plugin needs it.
83
+
84
+ ### Attach text context to a message
85
+
86
+ Plugins can stage labeled, removable text chips in a ready, non-archived conversation's composer. Chips stay with that conversation when you switch tabs or selections. **Send** appends their text verbatim to your message; chips can also be sent alone. Messages with chips are ordinary prompts, even if typed text starts with `/` or `!`.
87
+
88
+ Chips clear when the server accepts the prompt, not when the agent finishes. Failed sends retain them for retry. Staging is browser-memory-only and clears on reload unless the plugin restores its own saved data. Older hosts may lack chip support; update PI WEB to use it.
49
89
 
50
90
  ### Opening workspace files from chat
51
91
 
52
92
  Chat Markdown links to relative files (including `./file`) or absolute paths inside the session workspace open in the bundled Files panel. The link retains a download URL for modifier/new-tab clicks and when no panel accepts it. File access still uses server-side workspace containment checks.
53
93
 
54
- A workspace panel can opt in with `fileOpenQuery(context, path)`. This synchronous hook receives its contribution-scoped context and a decoded workspace-relative path; return a navigation query such as `{ file: path }`, or `undefined` to decline. Keep the hook free of side effects: the host opens the first accepting visible, enabled panel for the selected machine, ordered by panel `order` then title, and applies its namespaced query through normal panel navigation. The panel reads the selection from `context.navigation.query`; no Files-plugin dependency is required.
94
+ Other workspace panels can handle these links too, letting a plugin open a file in its own viewer.
55
95
 
56
96
  ### Panel and tab navigation
57
97
 
58
- The URL's `view` selects a responsive panel: `navigation`, `chat`, or `workspace`. The independent `tool` parameter selects a workspace tab by contribution ID. Opening a workspace tool sets `view=workspace` and `tool` to its ID; switching to chat keeps the selected tool. Contribution IDs are not accepted in `view`. Browser plugins use `selectMainView("workspace")` to show the workspace panel without changing its selected tab, or `selectWorkspaceTool(panelId)` to select and show a particular tool.
98
+ **Panel ID migration.** `routeAliases` and `navigationAliases` have been removed from application and workspace panel contributions. Remove these options from plugins; declarations that still supply them fail registration. Update saved links to the current qualified contribution ID and its query namespace. Short tool names such as `files` and historical IDs such as `core:workspace.files` or `core:workspace.terminal` no longer select the bundled panels; use `pi-web.files:workspace.files` and `pi-web.terminal:workspace.terminal`. Remote plugins still use the host's source-to-runtime ID mapping; this is not an author-configurable alias mechanism. Action `shortcutAliases` are unchanged.
59
99
 
60
- Invalid values remain in the URL rather than triggering a redirect. An invalid `view` shows a warning and displays navigation on mobile; on two-column layouts, navigation remains alongside a valid requested tool or, otherwise, chat. Desktop keeps its normal columns. A valid workspace view with an invalid tool shows an unavailable-tab message inside the workspace panel, without selecting another tab or adding a duplicate warning. Omitted parameters use defaults and are not errors.
100
+ Links use `view` for `navigation`, `chat`, or `workspace`, and `tool` for a qualified application or workspace tab ID. Contribution IDs do not belong in `view`. Invalid destinations show a warning or unavailable-tab message rather than silently selecting another tool.
61
101
 
62
- Action and workspace-panel contexts expose `navigate(destination): Promise<void>` for complete destinations:
102
+ Plugin navigation can open a complete destination or patch the current URL, select a saved item within a tab, and push or replace browser history. Complete destinations use host restoration defaults for omitted fields; patches preserve omitted URL fields. When opening a known session elsewhere, supply its machine, project, and workspace explicitly.
63
103
 
64
- ```ts
65
- await context.navigate({
66
- machineId: context.machine.id,
67
- projectId: context.workspace.projectId,
68
- workspaceId: context.workspace.id,
69
- sessionId: sourceSessionId,
70
- view: "chat",
71
- });
72
- ```
104
+ **Session IDs.** Browser URLs, plugin navigation, and PI WEB session APIs use exact, case-sensitive IDs. Pass the full `session.id` returned by the session catalog or creation response. Abbreviated IDs produce a missing-session destination instead of matching or expanding a prefix; update old shortened links and integrations to use full IDs. Short IDs shown in labels are display-only.
73
105
 
74
- All destination fields are optional: `machineId`, `projectId`, `workspaceId`, `sessionId`, `view` (`navigation`, `chat`, or `workspace`), and `tool` (a qualified contribution ID). Omitted `machineId` means the machine selected when called. This is not a route patch: omitted fields use normal host restoration defaults rather than copying the current route's session, tool, or contribution query. Those defaults can select a remembered session. Supply the project/workspace scope when opening a known session; the host does not search for IDs or create missing destinations.
106
+ Older hosts ignore the options argument and still treat the destination as a complete replacement; upgrade PI WEB before relying on patch behavior or history replacement. Hosts without destination-query support also ignore `query`; upgrade before relying on exact item selection.
75
107
 
76
- The promise settles after host restoration, or normally when newer navigation supersedes it. Missing or unavailable destinations use the normal host UI and do not also reject the promise. Malformed argument types and invalid `view` values reject with `TypeError` before changing the URL or UI. Captain's Log uses this API for **Open source session** on translations that record a source session.
108
+ ### Message actions
77
109
 
78
- ### Content previews in chat and Files
110
+ Plugins can add buttons beside transcript messages, for example to quote text into the next prompt or start a review. Actions can target a saved entry or displayed text, including a streaming message. They receive the clicked message's ordinary text, not thinking, image data, tool payloads, or a whole-transcript subscription.
79
111
 
80
- Browser plugins can contribute `contentRenderers` with an `id`, `languages` (Markdown fence labels), `fileExtensions` (without a dot), and a synchronous `render(input)` returning a Lit template. Selectors are case-insensitive and match by language OR file extension. `renderMode?: 'manual' | 'automatic'` defaults to `manual`: raw source and a **Render** button appear without calling the renderer. This is the initial policy, not a restriction on explicit user intent; unrelated prose updates retain activation. Authors may explicitly opt into `automatic` and are responsible for efficient activation and asynchronous work. The same renderer serves chat fences, Files Markdown preview fences, and standalone text files. Selection follows the effective machine's plugin availability and portable/machine-specific precedence. When several renderers match, a chooser lets you compare alternatives for each diagram or file, with only the selected renderer mounted. Choices default to alphabetical source plugin ID order (not remote runtime prefixes), then local contribution ID, using locale-independent, case-sensitive code-unit comparison. The chooser labels identify the plugin and contribution. Chat remembers explicit renderer and Raw/Preview choices per code block in this browser tab for 15 minutes from the last explicit choice. Viewing or remounting never extends that deadline; expiry applies on revisit, without removing a visible preview. Entries are bounded to the 128 most recently chosen blocks and scoped by machine, session, message/entry, part, block and exact source. Changed source or an unavailable selected renderer invalidates the choice and restores the deterministic default and its policy. Automatic rendering alone creates no remembered override. DOM is not cached; previews render again on remount. Reloading or closing the tab clears this memory. Files Markdown previews do not use chat intent memory. For standalone files, the plugin policy supplies the initial default only when no browser-local Raw/Preview preference exists. Saved Preview authorizes all file rendering, including manual renderers inside Markdown fences; saved Raw suppresses previews. Per-block Raw and renderer choices remain available within Markdown Preview. URL mode still takes precedence. Defaults are not automatically saved as explicit choices. Built-in file previews retain their existing defaults. There is no plugin order field or sorting UI.
112
+ **Plugin actions do not automatically ask for confirmation.** Plugins must provide any needed safeguards. Built-in **Fork** and **Go back** retain their confirmations. On a non-user message, they retain its checkpoint and intervening history before the next checkpoint; on a user message, they act before that entry and restore its text to the editor. Neither action reruns commands or reverses filesystem changes.
81
113
 
82
- Chat keeps **Raw**, **Preview**, and **Copy source** available in a toolbar on each supported diagram block. Standalone Files previews use the file header's **Raw**/**Preview** controls beside **Download** and follow the saved file-view mode; diagrams within Markdown files retain per-block controls. Incomplete fences stay raw and copyable; closed fences preview before the message finishes. Appending prose does not restart an unchanged completed block. Preview failure shows the source instead. Unknown formats retain normal code rendering.
114
+ Older hosts may ignore message actions or lack newer display-action features; update PI WEB before relying on them.
115
+
116
+ ### Content previews in chat and Files
83
117
 
84
- `input` contains `text`, an abort `signal`, and `fail(error)`. Render synchronously, own async work in a component, clean up on disconnect or abort, and report asynchronous failures with `fail`. Obsolete failures are ignored. Renderers receive untrusted text: escape or sanitize output, avoid executing source, and do not relax the surrounding Markdown policy. The host skips plugin rendering for truncated input and blocks over 100,000 UTF-16 code units; Files also retains its inline byte-size limit. Ordinary chat Markdown and Files Markdown retain their distinct sanitizers.
118
+ Plugins can preview diagrams and other text formats in chat code blocks, Markdown files, and standalone Files previews. Manual rendering is the default: choose **Render** to view a preview. Plugins may opt into automatic rendering. If several renderers match, use the chooser to compare them.
85
119
 
86
- A browser consumer such as Files declares the host capability `{ pluginId: "pi-web", id: "content-rendering", version: 1, parse }` in `requires`, then resolves it in `start`. The exported `ContentRenderingCapability` type describes `listRenderers(request)`, `renderText(request)`, and `renderMarkdown({ machineId, text, truncated?, toSafeHtml, allowManualPreview? })`. `listRenderers` returns sorted eligible `{ id, label, renderMode }` choices without invoking renderers. `renderText` accepts an optional `rendererId` only with `controls: "external"`; absent or unavailable IDs select the first match. Embedded controls own their selection and ignore externally supplied IDs. Use `controls: "external"` when the consumer owns the renderer chooser, mode switching, and raw-source access (as Files does in its header beside Download); otherwise controls remain embedded. Consumers own their user-intent policy and may pass `allowManualPreview: true` to `renderText` or `renderMarkdown` to authorize manual previews (for example, an explicit Render click or Files’ saved or URL Preview preference). For Markdown this applies to eligible fences without overriding per-block Raw choices. Omitting it or passing false retains renderer defaults, including automatic previews. Chat never passes this override. External Raw controls must remove the preview. The public capability stores no remembered intent. Chat’s bounded per-block memory is private host policy, not a plugin option. Markdown requests have their own shape without file-path or language selectors; languages come from fences. Creating a `renderText` template does not invoke a renderer. Markdown diagrams each own an independent chooser. Switching renderers cancels the previous preview and clears its failure state. `renderMarkdown` requires the consumer's trusted sanitizer; it is not permission to insert untrusted HTML. See the [Files consumer](https://github.com/jmfederico/pi-web/blob/main/pi-web-plugins/files/pi-web-plugin.ts) and [Mermaid contribution](https://github.com/jmfederico/pi-web/tree/main/pi-web-plugins/mermaid) for complete implementations.
120
+ **Raw** shows source; **Copy** copies source even while a preview is visible. Files uses **Raw**/**Preview** beside **Download** and remembers your file-view preference; Preview also permits manual renderers in Markdown files. Chat choices are temporary and clear on reload.
121
+
122
+ Incomplete, truncated, or oversized input is not rendered. Files also limits the size of inline previews. Failed previews fall back to source. Renderers must treat text as untrusted, sanitize output, and avoid executing source. For implementation, see the [Files consumer](https://github.com/jmfederico/pi-web/blob/main/pi-web-plugins/files/pi-web-plugin.ts) and [Mermaid contribution](https://github.com/jmfederico/pi-web/tree/main/pi-web-plugins/mermaid).
87
123
 
88
124
  ### Conversations and companion extensions
89
125
 
@@ -105,7 +141,13 @@ A provider decides which workspaces belong to a project. A primary provider can
105
141
 
106
142
  Conflicting claims produce a visible error. A provider that claims a project and then fails does not silently hand ownership to another provider. Providers can also offer workspace removal, which runs as a visible terminal operation.
107
143
 
108
- Workspace discovery must be available before host session services start. If a package needs both a provider and session-backed features, use two plugin entries in the same package. There is no need to split the distribution into separate packages.
144
+ External integrations can send a project-scoped discovery hint through the core CLI on the project's machine:
145
+
146
+ ```sh
147
+ pi-web workspaces notify --project ID --plugin git
148
+ ```
149
+
150
+ `ID` is the registered project ID, not a filesystem path. `--plugin` identifies the provider (`git` here), which must own the project and accept notifications. Connected browsers re-read the workspace list; the command does not register projects or workspaces.
109
151
 
110
152
  ## Install and manage
111
153
 
@@ -126,7 +168,7 @@ systemctl --user restart pi-web-sessiond
126
168
 
127
169
  A browser reload, web/API restart, or Pi's `/reload` does not activate server-plugin changes. Until the daemon restarts, Settings can show different desired and active states. A paired browser entry is withheld when it no longer matches the active server entry, rather than running incompatible code.
128
170
 
129
- Most plugins are enabled by default. Packages can opt out, and Settings can override the default. Plugin settings live in the normal [PI WEB configuration](https://pi-web.dev/config), not inside the package's installed files.
171
+ Most plugins are enabled by default. Packages can opt out, and Settings can override the default. Plugin settings live in the normal [PI WEB configuration](https://pi-web.dev/config), not inside the package's installed files. Enablement stays global; server plugins supporting project settings, including Git, can use `plugins.<id>.settings` overrides in the registered project's `.pi-web/config.json`. See [Project plugin settings](https://pi-web.dev/config#project-plugin-settings) for override and restart rules.
130
172
 
131
173
  ### Local development
132
174
 
@@ -151,12 +193,22 @@ Keep gateways and targets compatible. During this plugin API transition, upgrade
151
193
 
152
194
  - **Terminal** supplies terminals and command runs. It is required in normal operation; disable it only through emergency safe start.
153
195
  - **Files** supplies file browsing, previews, and uploads. Disabling its panel does not remove other plugins' file helpers.
154
- - **Git** discovers Git workspaces and provides status/diff. Disabling it leaves the project-folder workspace available unless another provider takes over.
196
+ - **Git** discovers Git workspaces and provides status/diff, including [automatic discovery of new worktrees](#automatic-git-worktree-discovery) on supported Git builds. Disabling it leaves the project-folder workspace available unless another provider takes over.
155
197
  - **Mermaid** uses the default manual mode: choose **Render** to preview `mermaid` fences and `.mmd`/`.mermaid` text files. Its bundled engine runs locally in an opaque-origin sandbox with network access blocked; no diagram service receives your source. Interactive links and external resources are intentionally unavailable. Disable Mermaid in plugin Settings to keep plain code rendering. Only a browser reload is needed after changing this browser-only plugin.
156
- - **Info** displays PI WEB status and copyable diagnostics.
157
- - **Updates** shows update/restart guidance when relevant and offers a manual update check.
198
+ - **Info** displays machine and PI WEB status without requiring a workspace, adds workspace details when selected, and provides copyable diagnostics.
199
+ - **Updates** shows update/restart guidance when relevant and offers a manual update check. Guidance and Copy work without a workspace; Run requires a selected workspace with an available Terminal provider on that machine.
158
200
  - **Workspace Tasks** turns project commands into runnable buttons.
159
201
 
202
+ ### Automatic Git worktree discovery
203
+
204
+ New worktrees can appear without a browser reload or loss of the current conversation or draft. On Git builds supporting native config-based named hooks (introduced in Git 2.54), the bundled Git provider registers a local `post-checkout` hook. Existing hook scripts and `core.hooksPath` are preserved, and notification failures do not fail Git operations.
205
+
206
+ Running dev and production on the same repositories? Docker dev handles this automatically. For other setups, see [running multiple instances](https://pi-web.dev/config#git-instance-namespace).
207
+
208
+ `workspaceDiscoveryHooks` defaults to `true`. See [Git hook opt-out configuration](https://pi-web.dev/config#git-worktree-discovery-hooks) to disable it globally or per project. Setting changes require a manual session-daemon restart, which can interrupt active sessions and terminals; a browser reload is insufficient.
209
+
210
+ Unsupported Git builds are left untouched, and `git worktree add --no-checkout` skips the notification. When hooks are skipped, disabled, or unavailable, discovery catches up on browser focus, navigation, manual refresh, or reconnect.
211
+
160
212
  ### Workspace Tasks
161
213
 
162
214
  Create `.pi-web/tasks.json` in a project:
@@ -173,11 +225,26 @@ Create `.pi-web/tasks.json` in a project:
173
225
 
174
226
  Open the **Tasks** tab to run a command in a workspace terminal. Tasks can also have a `description`. Review commands before running them, especially in shared repositories. Disabling the plugin hides the tab without changing the project file.
175
227
 
228
+ ### Workflows
229
+
230
+ Workflows is a shipped Pi package for PI WEB's opinionated prompts and shortcuts. It currently provides `/worktree` with the free-form hint **Purpose and/or base branch**, plus a **Create Worktree** palette action. The action prepares `/worktree` in an empty Chat prompt for an active conversation; add your description and send it when ready.
231
+
232
+ ```text
233
+ /worktree fix login redirects
234
+ /worktree add search based on origin/main
235
+ ```
236
+
237
+ The prompt asks the agent to create a worktree named for your purpose, using the requested base or the current worktree's HEAD. If you give no purpose or description, the agent asks for one.
238
+
239
+ PI WEB installs Workflows automatically for the active agent profile at session-daemon startup. Its web plugin is enabled by default. Disable **Workflows** in **Settings → PI WEB plugins**, then reload the browser, to hide the shortcut while keeping `/worktree`. To remove its prompts as well, remove the package in **Settings → Pi packages** and run `/reload` in each idle conversation. Removal is remembered across daemon restarts; reinstall from **Available packages** if you change your mind, then reload idle conversations and the browser.
240
+
176
241
  ### Relays
177
242
 
178
- The shipped Relay package adds agent prompts and skills for carrying work across sessions, plus a read-only **Relays** tab for inspecting plans and progress under `.pi-web/relays/`. The tab does not start or edit a relay.
243
+ The shipped Relay package adds `/relay` for the current checkout and `/relay-worktree` for a fresh worktree. It clarifies a goal and boundaries with you, then starts only after explicit approval. Fresh sessions record progress and hand off until the goal is reached or human help is needed. Project instructions and skills govern the work, and completion requires a fresh-context review.
179
244
 
180
- PI WEB installs Relay automatically for the active agent profile if it is not configured. Removing it through **Settings → Pi packages** is remembered; it will not be silently reinstalled. Reinstall from **Available packages** if you change your mind. Disabling just the Relays plugin hides its tab but leaves its agent resources available.
245
+ The read-only **Relays** tab lists saved packets under `.pi-web/relays/` and shows their goal, current baton, history, and any recorded decisions. It does not start or edit a relay. A session-list indicator opens the associated packet on that conversation's machine and workspace, including custom packet locations inside the workspace. Older sessions without public Relay metadata have no indicator.
246
+
247
+ PI WEB installs Relay automatically for the active agent profile if it is not configured. Removing it through **Settings → Pi packages** is remembered; it will not be silently reinstalled. Reinstall from **Available packages** if you change your mind. Disabling just the Relays plugin hides its tab and row indicators but leaves its agent resources available.
181
248
 
182
249
  ### Try Captain's Log
183
250
 
@@ -196,10 +263,18 @@ See the [Captain's Log usage guide](https://github.com/jmfederico/pi-web/blob/ma
196
263
 
197
264
  Pi extensions can ask for confirmation, a selection, or text input. PI WEB shows these questions inline in the conversation, including during session startup or while a tool is waiting. They remain answerable after a browser reload, and the first answer wins across tabs.
198
265
 
199
- Dialogs use the extension's timeout and the host's configured [dialog timeout](https://pi-web.dev/config#extension-dialogs). Aborting work or replacing its runtime closes the relevant outstanding questions. Answered cards are browser-local and need not survive a reload. Reloading while a new session is still being created can temporarily lose its question card; the pending question still has its deadline.
266
+ Dialogs use the extension's timeout and the host's configured [dialog timeout](https://pi-web.dev/config#extension-dialogs). Aborting work, replacing a runtime, or reloading extensions closes the relevant outstanding questions. Answered cards are browser-local and need not survive a reload. If you reload during startup, find the live conversation in Sessions and open it to answer its pending questions.
200
267
 
201
268
  These three dialog methods are supported; other Pi extension UI surfaces, such as custom editors and widgets, are not. An extension should not assume every UI feature works just because `hasUI` is true.
202
269
 
270
+ ### Extension tree commands
271
+
272
+ Pi extension commands can rewind with `ctx.navigateTree()` and fork with `ctx.fork()`, including follow-up work using the fresh `withSession` context. These operations run independently of browser connections. Rewinds refresh the viewed conversation; forks appear in Sessions for you to open. Other viewers keep their own selection and unsent drafts.
273
+
274
+ Prepared text appears as suggested input. Choose **Use suggested input** to replace this browser's current draft; it is never applied automatically. The latest suggestion survives browser reloads while the daemon hosts the conversation. A later tree action, submitted user message, runtime close, or daemon restart can clear it. Explicit web rewind/fork buttons keep their response-driven navigation and editor behavior.
275
+
276
+ Extension idle/reload actions are supported. `ctx.newSession()` and `ctx.switchSession()` report that they are unsupported; use the web UI for those actions. Other terminal editor and widget APIs remain unsupported.
277
+
203
278
  ### Pi Markdown transformers
204
279
 
205
280
  PI WEB applies a session's `pi.registerMarkdownTransformer()` chain to completed and historical user text, assistant text, and assistant thinking. Transformations run in the session daemon before browser Markdown sanitization and code-fence previews; file previews are unaffected. Original session content and **Copy message** stay unchanged. Code-fence Raw/copy controls use the transformed fence source.
@@ -8,8 +8,8 @@
8
8
  "scripts": { "build": "tsc -p tsconfig.json" },
9
9
  "devDependencies": {
10
10
  "@jmfederico/pi-web": "^1.202609.1",
11
- "@earendil-works/pi-coding-agent": "^1.0.0",
12
- "@types/node": "^24.13.3",
11
+ "@earendil-works/pi-coding-agent": "^1.1.0",
12
+ "@types/node": "^24.19.2",
13
13
  "typescript": "^6.0.3"
14
14
  },
15
15
  "peerDependencies": { "@earendil-works/pi-coding-agent": "^1.0.0" },
@@ -13,7 +13,7 @@ export default function companion(pi: ExtensionAPI): void {
13
13
  });
14
14
  pi.on("before_agent_start", (event) => {
15
15
  if (!active) return;
16
- if (!active.started && event.prompt === active.prompt) active.started = true;
16
+ if (!active.started && "prompt" in event && event.prompt === active.prompt) active.started = true;
17
17
  else active.error = "Another prompt interfered with this review; findings were not saved";
18
18
  });
19
19
  pi.on("message_start", (event) => {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "devDependencies": {
10
10
  "@jmfederico/pi-web": "^1.202609.1",
11
- "@types/node": "^24.13.3",
11
+ "@types/node": "^24.19.2",
12
12
  "typescript": "^6.0.3"
13
13
  },
14
14
  "piWeb": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmfederico/pi-web",
3
- "version": "1.202610.1",
3
+ "version": "1.202610.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",
@@ -64,7 +64,6 @@
64
64
  "test": "vitest run --config vitest.config.ts",
65
65
  "check:artifacts": "vitest run --config vitest.artifacts.config.ts",
66
66
  "verify": "npm run typecheck && npm run lint && npm run knip && npm test",
67
- "verify:staged": "node scripts/verify-staged.mjs",
68
67
  "start": "tsx src/server/index.ts",
69
68
  "start:sessiond": "node scripts/dev-sessiond.mjs",
70
69
  "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
@@ -79,52 +78,53 @@
79
78
  "changelog:status": "changeset status"
80
79
  },
81
80
  "dependencies": {
82
- "@codemirror/commands": "^6.10.4",
81
+ "@codemirror/commands": "^6.11.1",
83
82
  "@codemirror/lang-css": "^6.3.1",
84
83
  "@codemirror/lang-go": "^6.0.1",
85
- "@codemirror/lang-html": "^6.4.11",
84
+ "@codemirror/lang-html": "^6.4.12",
86
85
  "@codemirror/lang-javascript": "^6.2.5",
87
86
  "@codemirror/lang-json": "^6.0.2",
88
- "@codemirror/lang-markdown": "^6.5.0",
87
+ "@codemirror/lang-markdown": "^6.5.2",
89
88
  "@codemirror/lang-python": "^6.2.1",
90
89
  "@codemirror/lang-rust": "^6.0.2",
91
- "@codemirror/language": "^6.12.4",
92
- "@codemirror/legacy-modes": "^6.5.3",
93
- "@codemirror/state": "^6.7.1",
94
- "@codemirror/view": "^6.43.6",
95
- "@fastify/compress": "^9.0.0",
96
- "@fastify/static": "^10.1.2",
97
- "@fastify/websocket": "^11.3.0",
90
+ "@codemirror/language": "^6.13.1",
91
+ "@codemirror/legacy-modes": "^6.5.5",
92
+ "@codemirror/state": "^6.7.6",
93
+ "@codemirror/view": "6.43.13",
94
+ "@fastify/compress": "^9.2.2",
95
+ "@fastify/static": "^10.1.6",
96
+ "@fastify/websocket": "^11.3.4",
98
97
  "@xterm/addon-fit": "^0.11.0",
99
98
  "@xterm/xterm": "^6.0.0",
100
99
  "diff": "^9.0.0",
101
- "fastify": "^5.10.0",
100
+ "fastify": "^5.12.7",
101
+ "image-size": "^2.0.4",
102
102
  "lit": "^3.3.3",
103
- "marked": "^18.0.6",
103
+ "marked": "^18.1.1",
104
104
  "node-pty": "^1.1.0",
105
- "typebox": "1.3.7",
106
- "undici": "^8.5.0",
107
- "ws": "^8.21.0"
105
+ "pi-web-typebox": "npm:typebox@1.3.37",
106
+ "undici": "^8.11.2",
107
+ "ws": "^8.22.0"
108
108
  },
109
109
  "devDependencies": {
110
- "@changesets/cli": "^2.31.0",
111
- "@earendil-works/pi-agent-core": "^1.0.0",
112
- "@earendil-works/pi-ai": "^1.0.0",
113
- "@earendil-works/pi-coding-agent": "^1.0.0",
110
+ "@changesets/cli": "^3.0.3",
111
+ "@earendil-works/pi-agent-core": "^1.2.0",
112
+ "@earendil-works/pi-ai": "^1.2.0",
113
+ "@earendil-works/pi-coding-agent": "^1.2.0",
114
114
  "@eslint/js": "^10.0.1",
115
- "@types/node": "^24.13.3",
116
- "@types/ws": "^8.18.1",
117
- "dompurify": "^3.4.15",
118
- "eslint": "^10.6.0",
119
- "globals": "^17.7.0",
120
- "happy-dom": "^20.11.1",
121
- "knip": "^6.25.0",
122
- "mermaid": "^11.17.2",
123
- "tsx": "^4.23.0",
115
+ "@types/node": "^26.6.5",
116
+ "@types/ws": "^8.18.2",
117
+ "dompurify": "^3.4.17",
118
+ "eslint": "^10.12.0",
119
+ "globals": "^17.13.0",
120
+ "happy-dom": "^20.14.6",
121
+ "knip": "^6.41.0",
122
+ "mermaid": "^12.2.0",
123
+ "tsx": "^4.23.15",
124
124
  "typescript": "^6.0.3",
125
- "typescript-eslint": "^8.63.0",
126
- "vite": "^8.1.4",
127
- "vitest": "^4.1.10"
125
+ "typescript-eslint": "^8.71.2",
126
+ "vite": "^8.3.4",
127
+ "vitest": "^5.0.4"
128
128
  },
129
129
  "publishConfig": {
130
130
  "access": "public"