137 lines
12 KiB
Markdown
137 lines
12 KiB
Markdown
---
|
|
status: resolved
|
|
claimed-by: "019fbffd-3252-7067-a2e7-8fcab9cc7293"
|
|
claimed-at: "2026-08-01T21:01:28-04:00"
|
|
parent: "[[083-pi-subagent-result-delivery-map|Pi subagent result delivery]]"
|
|
blocked-by: []
|
|
resolved-at: "2026-08-01T21:03:56-04:00"
|
|
tags:
|
|
- ticket/research
|
|
---
|
|
|
|
# Pi subagent result delivery UI research
|
|
|
|
## Question
|
|
|
|
What Pi-native UI affordances can show subagent completion, failure, unread results, or a lightweight result inbox without disrupting an active parent workflow such as a one-question-at-a-time grill session?
|
|
|
|
## Findings
|
|
|
|
The best Pi-native non-disruptive awareness surface is an extension widget plus footer status.
|
|
Pi documents `ctx.ui.setStatus()` as persistent footer or status-bar text, and `ctx.ui.setWidget()` as persistent content above or below the editor.
|
|
The TUI source stores extension widgets in separate above-editor and below-editor maps, limits string widgets to `InteractiveMode.MAX_WIDGET_LINES`, renders them into widget containers, and requests a render without replacing editor focus.
|
|
Sources: Pi `docs/extensions.md`, Custom UI and Widgets, Status, and Footer sections.
|
|
Sources: Pi `docs/tui.md`, Pattern 4 and Pattern 5.
|
|
Sources: Pi `dist/core/extensions/types.d.ts`, `ExtensionUIContext.setStatus()` and `setWidget()`.
|
|
Sources: Pi `dist/modes/interactive/interactive-mode.js`, `setExtensionStatus()`, `setExtensionWidget()`, `renderWidgets()`, and `renderWidgetContainer()`.
|
|
|
|
Footer status is good for the smallest completion and unread-result indicator.
|
|
The built-in footer renders extension statuses from `footerData.getExtensionStatuses()` on an extra sorted status line, and custom footers can consume the same `getExtensionStatuses()` data.
|
|
This supports a compact badge such as `subagents: 2 running · 1 unread · 1 failed` without opening a modal, injecting a message, or disturbing a grill prompt.
|
|
Sources: Pi `dist/modes/interactive/components/footer.js`, extension status rendering.
|
|
Sources: Pi `docs/tui.md`, Pattern 4 and Pattern 6.
|
|
Sources: Pi `examples/extensions/status-line.ts` and `examples/extensions/custom-footer.ts`.
|
|
|
|
A widget is the best Pi-native lightweight inbox surface.
|
|
Pi documents widgets as persistent content above or below the editor, with string-array or component-factory content and explicit clear semantics.
|
|
The current dotfiles subagent extension already uses this pattern by rendering a collapsed summary or expanded inspector through `ctx.ui.setWidget("subagents", ...)`, and its supervisor updates that widget through `onChange` whenever child status changes.
|
|
That proves the runtime state model can drive a non-blocking inbox-like view today.
|
|
Sources: Pi `docs/extensions.md`, `ctx.ui.setWidget()` examples.
|
|
Sources: Pi `docs/tui.md`, Pattern 5.
|
|
Sources: `modules/agents/pi/extensions/subagents/index.ts`, `updateUi()` and `onChange` wiring.
|
|
Sources: `modules/agents/pi/extensions/subagents/ui.ts`, `renderSummary()`, `renderInspector()`, and `widget()`.
|
|
Sources: `modules/agents/pi/extensions/subagents/supervisor.ts`, `emitChange()`, terminal state handling, and result availability updates.
|
|
|
|
A durable unread or milestone trail should use custom entries rather than ordinary messages.
|
|
Pi documents `pi.appendEntry()` as session-persistent extension data that does not participate in LLM context, and `pi.registerEntryRenderer()` as the TUI renderer for those entries.
|
|
The interactive source listens for `entry_appended`, renders custom entries with `addCustomEntryToChat()`, and inserts them before the current streaming component when one exists.
|
|
This is suitable for a compact history of completed, failed, or unread subagent results that is visible in the transcript but does not contaminate the parent model context.
|
|
Sources: Pi `docs/extensions.md`, `pi.appendEntry()` and Message and Entry Rendering sections.
|
|
Sources: Pi `dist/core/extensions/types.d.ts`, `registerEntryRenderer()` and `appendEntry()` definitions.
|
|
Sources: Pi `dist/modes/interactive/interactive-mode.js`, `entry_appended` handling and `addCustomEntryToChat()`.
|
|
Sources: Pi `examples/extensions/entry-renderer.ts`.
|
|
|
|
`ctx.ui.notify()` is Pi-native but should be secondary rather than authoritative.
|
|
The extension docs define `notify()` as non-blocking and typed as `info`, `warning`, or `error`.
|
|
The interactive implementation maps it to `showStatus()`, `showWarning()`, or `showError()`, and `showStatus()` appends or updates a dim chat status line to avoid log spam from back-to-back status messages.
|
|
This can announce `subagent sg-... completed` or `failed`, but it is transient and not enough for unread-result accounting.
|
|
Sources: Pi `docs/extensions.md`, Dialogs section.
|
|
Sources: Pi `dist/core/extensions/types.d.ts`, `ExtensionUIContext.notify()`.
|
|
Sources: Pi `dist/modes/interactive/interactive-mode.js`, `showExtensionNotify()` and `showStatus()`.
|
|
Sources: Pi `examples/extensions/notify.ts`.
|
|
|
|
An overlay or custom component can provide an explicit inbox viewer, but it should be user-invoked.
|
|
Pi documents `ctx.ui.custom()` as temporarily replacing the editor unless `{ overlay: true }` is passed, and overlay mode renders a floating component on top of existing content.
|
|
The interactive source preserves editor text, restores editor focus after non-overlay custom UI, and in overlay mode calls `ui.showOverlay()` instead of clearing the editor container.
|
|
This is appropriate for a `/subagent-inbox` or shortcut that opens an inspect-and-mark-read panel, but automatic popup overlays would still interrupt keyboard flow in a one-question-at-a-time grill.
|
|
Sources: Pi `docs/extensions.md`, Custom Components and Overlay Mode sections.
|
|
Sources: Pi `docs/tui.md`, Overlays and Overlay Focus sections.
|
|
Sources: Pi `dist/modes/interactive/interactive-mode.js`, `showExtensionCustom()`.
|
|
Sources: Pi `examples/extensions/overlay-test.ts`.
|
|
|
|
Custom tool rendering is useful for blocking or in-turn subagent results, but it does not solve background result delivery by itself.
|
|
Pi supports `renderCall()` and `renderResult()` for tool rows, partial results via `onUpdate`, and compact or expanded tool output.
|
|
The bundled Pi subagent example uses custom renderers to show single, chain, and parallel results with success, failure, running, usage, and expanded details.
|
|
That pattern is valuable for `subagent_wait` or old blocking subagent calls, but a spawned background child that completes after the parent turn needs a stateful widget, footer badge, entry renderer, or command because no tool row is actively updating at completion time.
|
|
Sources: Pi `docs/extensions.md`, Custom Rendering section.
|
|
Sources: Pi `examples/extensions/subagent/index.ts`, `renderCall()`, `renderResult()`, and parallel update handling.
|
|
|
|
Message injection APIs are available but are not the default workflow-safe route.
|
|
Pi documents `pi.sendMessage()` as injecting a custom message that participates in LLM context, with delivery modes `steer`, `followUp`, and `nextTurn`.
|
|
It separately says TUI-only durable content should use `pi.appendEntry()` with `pi.registerEntryRenderer()` instead.
|
|
For AFK worker result awareness during a grill, ordinary or custom message injection risks changing the active parent context and should be reserved for explicit user-approved reconciliation or selected result import.
|
|
Sources: Pi `docs/extensions.md`, `pi.sendMessage()` and `pi.appendEntry()` sections.
|
|
Sources: Pi `dist/core/extensions/types.d.ts`, `sendMessage()` and `appendEntry()` definitions.
|
|
|
|
Mode behavior limits which affordances exist outside interactive TUI.
|
|
Pi documents that `ctx.hasUI` is true in TUI and RPC modes, false in print and JSON modes, and that TUI-specific features need `ctx.mode === "tui"`.
|
|
It also states that in RPC mode some TUI-specific methods are no-ops or return defaults.
|
|
The no-op UI context in the extension runner implements notification, status, widget, footer, custom UI, editor, and autocomplete methods as no-ops when no UI is bound.
|
|
Therefore, result delivery should keep the authoritative unread/result state in supervisor/session data and treat TUI surfaces as projections.
|
|
Sources: Pi `docs/extensions.md`, ExtensionContext and Mode Behavior sections.
|
|
Sources: Pi `dist/core/extensions/runner.js`, `noOpUIContext`.
|
|
|
|
## Answer
|
|
|
|
Use a layered Pi-native design.
|
|
Keep authoritative result and unread state in the subagent supervisor and persisted custom entries.
|
|
Project awareness through a footer status badge and the existing subagent widget.
|
|
Add a user-invoked inbox command or shortcut using either an expanded widget state or an overlay custom component.
|
|
Use `notify()` only as a best-effort completion toast or chat status line.
|
|
Use custom entry rendering for durable milestone cards that do not enter LLM context.
|
|
Do not inject ordinary messages into the active parent conversation by default.
|
|
|
|
## Limitations
|
|
|
|
No primary Pi source found a built-in unread badge, inbox widget, notification center, or read/unread model for extensions.
|
|
Those semantics must be implemented by the subagent extension on top of `setStatus()`, `setWidget()`, `appendEntry()`, commands, and optional overlay UI.
|
|
|
|
No primary Pi source found a way for a background process completion to update an already-finalized tool row after the parent turn has moved on.
|
|
Background completion should therefore update extension-owned state surfaces rather than rely on tool result rendering.
|
|
|
|
Automatic overlays are technically possible, but the cited Pi sources do not define a non-disruptive policy for showing one during active typing or an ongoing HITL flow.
|
|
Treat overlays as explicit inbox viewers unless a later design decision chooses interruption rules.
|
|
|
|
Terminal-level desktop notifications are possible as an example extension writes OSC 777, OSC 99, or Windows toast sequences, but that is not a Pi-owned notification API beyond extension code writing to stdout or spawning a command.
|
|
It should not be the primary Pi-native answer.
|
|
|
|
## Citations
|
|
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/extensions.md`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/tui.md`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/core/extensions/types.d.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/core/extensions/runner.js`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/modes/interactive/interactive-mode.js`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/modes/interactive/components/footer.js`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/status-line.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/widget-placement.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/entry-renderer.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/message-renderer.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/overlay-test.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/notify.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/custom-footer.ts`.
|
|
- `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/examples/extensions/subagent/index.ts`.
|
|
- `/home/alexion/wrk/dotfiles/modules/agents/pi/extensions/subagents/index.ts`.
|
|
- `/home/alexion/wrk/dotfiles/modules/agents/pi/extensions/subagents/ui.ts`.
|
|
- `/home/alexion/wrk/dotfiles/modules/agents/pi/extensions/subagents/supervisor.ts`.
|