Add subagent result delivery artifacts
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
---
|
||||
status: resolved
|
||||
claimed-by: "019fc02c-3d48-71aa-b1b4-0ea51138ad4a"
|
||||
claimed-at: "2026-08-01T21:52:36-04:00"
|
||||
resolved-at: "2026-08-01T21:54:08-04:00"
|
||||
parent: "[[084-pi-subagent-result-delivery-product-semantics-grill|Pi subagent result delivery product semantics]]"
|
||||
blocked-by: []
|
||||
tags:
|
||||
- ticket/research
|
||||
---
|
||||
|
||||
# Pi subagent read-only attach feasibility research
|
||||
|
||||
## Question
|
||||
|
||||
Can Pi support a read-only attached child session view that looks like a normal Pi session without the text prompt, using an overlay or temporary replacement rather than split panes, and what implementation boundary should the result-delivery effort assume?
|
||||
|
||||
## Answer
|
||||
|
||||
Pi can support the product shape, but not as a pure current subagent-extension UI tweak.
|
||||
The implementation boundary should assume a Pi-native child-session viewer component owned by the subagent/result-delivery extension, fed by child RPC or session-entry events, and rendered through `ctx.ui.custom()` as either a focused overlay or a temporary editor-area replacement.
|
||||
The boundary should not assume Pi can simply attach an existing live `InteractiveMode` instance, hide its prompt, and embed that normal session view inside the parent TUI.
|
||||
|
||||
## Findings
|
||||
|
||||
Pi's extension TUI surface already supports the two requested placement modes.
|
||||
`ctx.ui.custom()` can show a custom component in place of the editor when `overlay` is absent, and it can render an overlay when `{ overlay: true }` is passed, with width, max-height, anchor, row, column, margin, focus, hiding, and lifecycle handles exposed through `overlayOptions` and `onHandle`.[^tui-overlays]
|
||||
The implementation source confirms the non-overlay path clears `editorContainer`, adds the custom component there, focuses it, and restores the editor when closed, while the overlay path calls `this.ui.showOverlay(component, resolveOptions())` without replacing the editor container.[^interactive-custom]
|
||||
The TUI implementation confirms overlays are composited over the visible viewport and clipped by `maxHeight`, and focused overlays receive input through the normal component `handleInput` path.[^tui-overlay-source]
|
||||
|
||||
Pi's public TUI component contract is sufficient for a read-only attached viewer component.
|
||||
A component renders lines for a width, may handle keyboard input, and invalidates caches on theme changes, so an attach view can implement detach plus scroll keys without accepting text input.[^tui-component]
|
||||
Pi's own docs explicitly tell extension authors to call `tui.requestRender()` after state changes, which is enough for a live view that updates as child events arrive.[^tui-render]
|
||||
Pi's overlay lifecycle documentation says overlays are disposed when closed and should be recreated rather than reused, so attach/detach should be treated as opening and closing a viewer instance rather than keeping a permanently mounted component around.[^tui-lifecycle]
|
||||
|
||||
Pi already exposes the rendering pieces needed to make the viewer look Pi-like, but not a single exported complete transcript widget.
|
||||
The main package exports `AssistantMessageComponent`, `UserMessageComponent`, `ToolExecutionComponent`, `CustomMessageComponent`, `DynamicBorder`, `getMarkdownTheme`, and other interactive components from `@earendil-works/pi-coding-agent`, which means extension code can reuse Pi's message and tool rendering styles rather than reimplement every visual primitive.[^component-exports]
|
||||
The interactive mode's own replay path constructs `UserMessageComponent`, `AssistantMessageComponent`, `ToolExecutionComponent`, and related components from session messages and tool events, showing the composition pattern that a child viewer would need to mirror.[^interactive-replay]
|
||||
However, `InteractiveMode` keeps `chatContainer`, `editorContainer`, `defaultEditor`, `editor`, `footerContainer`, event subscriptions, and render state as private internals, so there is no public API that exposes a ready-made promptless transcript pane for an extension to mount directly.[^interactive-private]
|
||||
|
||||
The current subagent runner is not yet feeding enough structured data to an attach view.
|
||||
It spawns child Pi as `--mode rpc --no-extensions --extension <subagents/index.ts> --name "subagent <id>"`, communicates over JSONL stdin/stdout, and currently records coarse lifecycle strings such as `agent_started`, arbitrary event types, `agent_settled`, and final `get_last_assistant_text`.[^subagent-runner]
|
||||
The subagent `SubagentStatus` type has an optional `childSession` field, but the current runner never populates it from RPC `get_state`, and `RunnerEvents.accepted(childSession?: string)` is called without a child session path.[^subagent-types]
|
||||
Therefore the existing extension can show statuses and final results, but it cannot render a live Pi-like child transcript without extending the runner/supervisor data path to retain child events, retrieve child entries or messages, or learn and load the child's session file.[^subagent-ui]
|
||||
|
||||
RPC mode is a viable feed for a child-session viewer.
|
||||
Pi's RPC docs state that events are streamed as JSON lines while the agent operates, including `agent_start`, `agent_settled`, `turn_start`, `turn_end`, `message_start`, `message_update`, `message_end`, and tool execution lifecycle events.[^rpc-events]
|
||||
RPC also exposes `get_state`, whose response includes `sessionFile` and `sessionId`, and `get_entries`, which returns append-only session entries with stable ids and a `leafId` cursor.[^rpc-state]
|
||||
RPC `message_update` includes both the partial assistant message and the assistant streaming delta, so a viewer can support a live transcript rather than waiting only for terminal results.[^rpc-message-update]
|
||||
The current subagent runner already parses the child stdout stream line by line, so preserving these event payloads and publishing them to an attach viewer is an extension-boundary change rather than a new operating-system or terminal integration.[^subagent-runner]
|
||||
|
||||
A second full `InteractiveMode` inside the parent is not the right boundary for this effort.
|
||||
The SDK documents `InteractiveMode` as a full TUI mode with editor, chat history, built-in commands, session runtime, and an own `run()` loop.[^sdk-interactive]
|
||||
`InteractiveMode.init()` builds a root `FlexSpacerBottomLayout` containing header, loaded resources, chat, pending messages, status, widgets, editor, and footer, then starts a `TUI` and sets focus to the editor.[^interactive-init]
|
||||
Embedding that whole mode inside the parent overlay would mean nested terminal/TUI ownership, duplicate editor/footer/runtime management, and prompt input that must then be suppressed.
|
||||
Primary docs and exports show reusable components and SDK sessions, but they do not provide an attach API that mounts another mode's transcript inside the current TUI.
|
||||
|
||||
RPC child mode has an important limitation for extension UI fidelity.
|
||||
The RPC documentation says `ctx.ui.custom()` returns `undefined` in RPC mode and `setFooter()`, `setHeader()`, `setEditorComponent()`, `setWorkingIndicator()`, and related direct-TUI methods are no-ops.[^rpc-ui-limit]
|
||||
That means an attached view of a child running in RPC mode can faithfully show normal agent messages, thinking, tool calls, and tool outputs from events, but it cannot display arbitrary child-side custom TUI overlays/components as if the child had its own terminal.
|
||||
For the subagent result-delivery effort, this limitation is acceptable if the promised attach view is a normal Pi-like transcript without the text prompt, not a full nested interactive Pi process.
|
||||
|
||||
## Implementation boundary
|
||||
|
||||
Build an attach-view feature inside the subagent/result-delivery extension using Pi's extension UI and reusable rendering components.
|
||||
Extend the child runner/supervisor to retain a per-child event stream, final session metadata, and enough message/tool state to reconstruct a transcript.
|
||||
Prefer an RPC-event-fed live model, optionally backfilled from `get_entries` or the child session file once `get_state.sessionFile` is captured.
|
||||
Render the selected child in a focused `ctx.ui.custom()` overlay for the approved overlay path, with a fallback non-overlay custom component that temporarily replaces the editor area.
|
||||
Implement read-only controls in that component only: detach and scroll up/down.
|
||||
Do not implement split panes.
|
||||
Do not try to embed a nested `InteractiveMode`, duplicate a child terminal, or depend on child-side direct TUI extension UI being visible.
|
||||
If exact visual parity is required later, the Pi core boundary would be a new exported transcript/viewer abstraction extracted from `InteractiveMode`, not a change to Wayfinder or to the status/result tool API alone.
|
||||
|
||||
## Limitations
|
||||
|
||||
No primary source documents a supported promptless child-session attach API.
|
||||
The feasibility conclusion is therefore based on Pi's documented extension UI contract, exported components, RPC event protocol, and current source shape rather than an existing first-class feature.
|
||||
|
||||
The research did not run an end-to-end prototype that renders a live child transcript in an overlay.
|
||||
The source evidence establishes the API boundary and feasibility, but implementation will still need tests for rendering, scroll behavior, event ordering, and terminal resize behavior.
|
||||
|
||||
The current child process is launched with `--no-extensions` plus only the subagents extension explicitly loaded.
|
||||
That is good for avoiding recursive/project extension effects, but it also means the attached child view should not promise to show project/global child extension UI unless that launch policy changes.
|
||||
|
||||
## Citations
|
||||
|
||||
[^tui-overlays]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/tui.md`, "Overlays" and "Overlay Focus".
|
||||
|
||||
[^interactive-custom]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/modes/interactive/interactive-mode.js`, `showExtensionCustom()`.
|
||||
|
||||
[^tui-overlay-source]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/node_modules/@earendil-works/pi-tui/dist/tui.js`, `showOverlay()`, `handleInput()`, and `compositeOverlays()`.
|
||||
|
||||
[^tui-component]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/tui.md`, "Component Interface".
|
||||
|
||||
[^tui-render]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/tui.md`, "Key Rules".
|
||||
|
||||
[^tui-lifecycle]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/tui.md`, "Overlay Lifecycle".
|
||||
|
||||
[^component-exports]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/index.d.ts`, interactive component and theme exports.
|
||||
|
||||
[^interactive-replay]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/modes/interactive/interactive-mode.js`, `addMessageToChat()` and `renderSessionItems()`.
|
||||
|
||||
[^interactive-private]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/modes/interactive/interactive-mode.d.ts`, private fields on `InteractiveMode`.
|
||||
|
||||
[^subagent-runner]: `/home/alexion/wrk/dotfiles/modules/agents/pi/extensions/subagents/runner.ts`, `SubprocessRpcRunner.start()` and `RpcChildHandle.onLine()`.
|
||||
|
||||
[^subagent-types]: `/home/alexion/wrk/dotfiles/modules/agents/pi/extensions/subagents/types.ts`, `SubagentStatus`, `RunnerEvents`, and `ChildRunner`.
|
||||
|
||||
[^subagent-ui]: `/home/alexion/wrk/dotfiles/modules/agents/pi/extensions/subagents/ui.ts` and `/home/alexion/wrk/dotfiles/modules/agents/pi/extensions/subagents/index.ts`, current widget and registered tool/command behavior.
|
||||
|
||||
[^rpc-events]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/rpc.md`, "Events" and "Event Types".
|
||||
|
||||
[^rpc-state]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/rpc.md`, `get_state` and `get_entries`.
|
||||
|
||||
[^rpc-message-update]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/rpc.md`, `message_update (Streaming)`.
|
||||
|
||||
[^sdk-interactive]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/sdk.md`, "Run Modes" and `InteractiveMode`.
|
||||
|
||||
[^interactive-init]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/dist/modes/interactive/interactive-mode.js`, `init()` layout setup.
|
||||
|
||||
[^rpc-ui-limit]: `/nix/store/p2xh13r9f890af4b8jdp60rnld1kbwsl-pi-coding-agent-0.82.1/lib/node_modules/pi-monorepo/docs/rpc.md`, "Extension UI Protocol" limitations.
|
||||
Reference in New Issue
Block a user