diff --git a/.changeset/extension-dialogs.md b/.changeset/extension-dialogs.md new file mode 100644 index 0000000..42d3b10 --- /dev/null +++ b/.changeset/extension-dialogs.md @@ -0,0 +1,5 @@ +--- +"@jmfederico/pi-web": patch +--- + +Support Pi extension dialogs in the browser: `ctx.ui.confirm()`, `ctx.ui.select()`, and `ctx.ui.input()` now render as cards inline in the session transcript and resolve with the user's actual answer — including dialogs opened from `session_start` hooks while the session is still starting and from in-flight `tool_call` hooks, which previously resolved `false` immediately despite `hasUI === true`. Answers travel over a dedicated session-daemon channel rather than the prompt queue, so a dialog parked inside a `tool_call` hook cannot deadlock the run. Open dialogs survive browser reloads, the first answer wins across browser tabs, and unanswered dialogs settle safely on run abort, runtime replacement, or timeout. Adds the `extensionDialogsTimeoutMs` config key (default 5 minutes, `0` waits forever) as the unattended-dialog safety valve; dialog support is always on. Other `ExtensionUIContext` surfaces (widgets, status, editor, `custom`) remain unimplemented. diff --git a/docs/config.html b/docs/config.html index 1e3de64..47b2dfd 100644 --- a/docs/config.html +++ b/docs/config.html @@ -102,6 +102,7 @@ Pi extension providers Model catalog refresh Session tools + Extension dialogs Completion tools @@ -173,7 +174,7 @@
host / port: restart the gateway web/API service or process.maxUploadBytes: restart both the web/API process and the session daemon on that machine.agent.command / agent.dir / spawnSessions / subsessions / askUser: restart the session daemon on that machine.agent.command / agent.dir / spawnSessions / subsessions / askUser / extensionDialogsTimeoutMs: restart the session daemon on that machine.pathAccess: applies on the next request; existing file views may need a browser refresh.uploads.defaultFolder: applies to newly opened Files upload dialogs and new direct drag/drop batches after config/workspace refresh.plugins: reload the browser tab after changing PI WEB plugin enablement.extensionDialogsTimeoutMsplugins.<id>.enabled, plugins.<id>.settings
+ Pi extensions can ask the user questions from ctx.ui.confirm(),
+ ctx.ui.select(), and ctx.ui.input() — including from
+ session_start hooks and in-flight tool_call hooks. PI WEB renders these dialogs
+ inline in the session transcript and answers them through a dedicated session-daemon channel, never the
+ prompt queue, so a dialog parked inside a tool_call hook cannot deadlock the run. Dialog
+ support is always on; there is no enable flag. See
+ Pi extension dialogs in PI WEB for behavior details and author
+ guidance.
+
+ extensionDialogsTimeoutMs is the unattended-dialog safety valve: how long the session daemon
+ waits for an answer before settling the dialog with its kind's cancel value (false for
+ confirm, undefined for select and input). It defaults to 300000 (5 minutes);
+ set it to 0 to wait forever. An extension's own timeout option still applies,
+ and the effective deadline is the sooner of the two.
+
extensionDialogsTimeoutMs 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.
+
+ 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(). For these
+ three methods ctx.hasUI is true in fact: the call renders a dialog card inline in the
+ session transcript — including from session_start hooks while the session is still starting
+ and from in-flight tool_call hooks — and resolves with the user's actual answer.
+
+ Answers travel over a dedicated session-daemon channel, never the prompt queue, so a parked
+ tool_call hook cannot deadlock the run. Open dialogs survive browser reloads, the first
+ answer wins across tabs, and unanswered dialogs settle safely: aborting the run or replacing the runtime
+ resolves them immediately with the kind's cancel value (false for confirm,
+ undefined for select and input), and the effective deadline — the sooner of the extension's
+ own timeout and the daemon's extensionDialogsTimeoutMs safety valve (default 5
+ minutes, 0 waits forever) — does the same when no one answers. Other
+ ExtensionUIContext surfaces (widgets, status, editor, custom) remain no-ops
+ despite hasUI === true.
+
+ For the full behavior notes and author guidance, read plugins.md; for the + timeout key, see Extension dialogs in the configuration reference. +
+diff --git a/docs/plugins.md b/docs/plugins.md index 31c4b8e..5362b88 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -29,6 +29,19 @@ Use **Settings → PI WEB plugins** to enable or disable discovered PI WEB brows 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). +## Pi extension dialogs in PI WEB + +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. + +- **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. +- **`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. +- **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. +- **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. +- **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. +- **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. + +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. + ## Trust model Plugins run as JavaScript in the browser app. Treat them as trusted code: