Repository navigation
feat(spike): port pi's durable TUI as a remote client (P1) - #427
Conversation
The upstream durable TUI renders a host's gateway through RemoteDurable unchanged, apart from its imports and a host connection notice. pi internals hidden by the package exports map are imported by relative dist path from one module, keeping stateful modules such as the theme single instances. Adds controller tests (steer, abort, conversation switch) and a pty smoke test.
|
|
||
| const remote = await connectRemoteDurable(await hostAddress(process.argv.slice(2))); | ||
| // As in pi's durable TUI, the task panel starts open; /tasks hides it. | ||
| await remote.controller.toggleTasks(); |
There was a problem hiding this comment.
🟡 Host disconnect stalls TUI startup
If the host disconnects before the tasks snapshot arrives, toggleTasks() waits indefinitely. The snapshot waiter survives disconnection, leaving the TUI unable to show reconnection or accept input.
Learn more
The client requests a tasks snapshot when the task panel opens. A disconnect rejects outstanding RPC calls, but the disconnect handler does not settle snapshot waiters. Since startup awaits the task panel before starting the terminal UI, it remains blocked until a restarted host sends another tasks snapshot.
Example: The TUI connects and sends subscribe tasks. The host exits before sending the snapshot. The client enters reconnecting, but the terminal UI never starts; Ctrl+D cannot close it while the host remains down.
Recommended fix: Start the terminal UI without awaiting the optional task panel subscription, or reject and clear outstanding snapshot waiters on disconnect and let reconnect resubscribe. Ensure failed subscriptions do not leave #commands blocked.
Was this helpful? React with 👍 or 👎 to provide feedback.
No linked issue: second stage (P1) of the Pi Durable multi-client spike (
.scratch/durable-multiview/HANDOFF.md), stacked on #426. Spike-only; no Pace app code changes.Summary
tui/tui.tsports pi's durable TUI (packages/coding-agent/src/experimental/durable/tui.tsat earendil-works/pi@7fbbd5f, MIT) to run as a remote client of the host. It rendersRemoteDurable's view through the sameDurableViewSource/DurableControllercontract. Upstream's formatting is kept so that a diff against upstream shows only the port: imports, a localagentOf, and a notice while the host connection is lost or closed.tui/pi-internals.ts: the pi-coding-agent 1.0.0 internals that the TUI needs but the packageexportsmap hides (createAllToolRenderers,WorkingStatusIndicator,getEditorTheme,InteractiveThemeController,formatTokens,theme, and the valueKeybindingsManager, which the package root exports only as a type). They are imported by relative dist path, through the same symlink as the package root, so stateful modules such as the theme stay single instances. If a Pi upgrade moves one of them, typecheck fails here.tui/main.tsopens the task panel at start, as upstream does, and uses this machine'sSettingsManagerfor theme, keybindings and terminal capabilities.cli/host-address.ts:--url/--token/--data-dirparsing shared by the observer and the TUI.@earendil-works/pi-tui1.0.0 (the same instance pi-coding-agent uses), and@lydell/node-ptyas a dev dependency, at the versionapps/desktopalready pins.Test plan
bun run typecheckbun run lintbun run test: 167 files, 1970 tests (spike excluded)bun run buildbun run test: 12 tests. New ones: steer from a second client while busy, abort keeping the partial as an aborted entry, switching conversations per client, an unknown conversation reported as a notice, and a pty smoke test where the TUI prompts the host, renders the answer, shows the reconnect notice and exits 0 on Ctrl+D. The TUI and crash tests passed 4 more consecutive runs./agents, Esc abort, andkill -9mid-stream. In the crash run the partial is shown, thenreconnecting...; after the restart the partial becomesOperation aborted, the samepi.generationtask streams again, and the full answer arrives.Checklist
bun run typecheck,bun run test, andbun run buildare greenCONTEXT.mdupdated if vocabulary changed (none changed)shared/ui/components are registered on/design(none changed)feat/; this is not a push tomain