Skip to content

feat(console): attach a live agent terminal#209

Open
Lanzelot1 wants to merge 2 commits into
feat/console-membershipfrom
feat/console-attach
Open

feat(console): attach a live agent terminal#209
Lanzelot1 wants to merge 2 commits into
feat/console-membershipfrom
feat/console-attach

Conversation

@Lanzelot1

Copy link
Copy Markdown
Collaborator

What

Closes the #1 control gap — the console couldn't open or drive a running agent's terminal (the classic lazygit/k9s "look inside the process" move). Now a on a roster agent (or :attach <agent>) suspends the console and hands the terminal to the agent's live PTY over the manager's WS attach endpoint; Ctrl-] (or COTAL_DETACH_KEY) returns and repaints.

  • Reuses everything that already exists — the manager's attach op (returns a loopback ws after own-child/admin authz, rejects non-pty runtimes) and the CLI's attach-client. The console resolves the ws via a per-action control-caller-admin mint (the exact path kill/pause use) and streams it.
  • Suspends Ink in place, no rebuild: on attach the App renders null and gates its top-level useInput, so Ink releases stdin (ref-counted raw mode) before attachClient grabs it; the observer endpoint + MeshView keep running in the background (App stays mounted), so the console repaints current state on return. On detach the effect re-asserts alt-screen/alt-scroll/cursor (attach-client leaves the main buffer after a full-screen child).
  • canControl-gated, pty-runtime only (tmux/cmux are watched natively and flash their own hint), same-host only (the ws is loopback). Keyed by name, so it works for a managed agent even if it isn't a mesh roster peer (like cotal attach --name).

Stacked on #208 (feat/console-membership) — retarget to main as the chain merges.

Verified

  • pnpm typecheck clean; existing smoke:attach (pty-vs-tmux gating) still green.
  • New smoke:attach-control:live (4 checks): against a real broker + Manager + pty agent, the console's exact control-op path (control-caller-admin mint → requestControl(ADMIN, {op:"attach"})) returns a valid ws://127.0.0.1:…/attach/<name>, and a missing agent errors (not crashes). The attachClient data path + terminal hygiene is already covered by attach-repaint.smoke.
  • Live tmux end-to-end: broker + Manager + a pty echo-agent → console :attach echo suspends the UI (Ink → null), the agent terminal takes over, typing HELLO-FROM-CONSOLE reaches the agent and echoes back (bidirectional I/O), and the console process/MeshView stay alive in the background. (Detach byte delivery through tmux send-keys is a harness quirk; the detach+repaint path is smoke-covered.)

Notes

No core change — reuses + . No SPEC change.

Lanzelot1 added 2 commits July 8, 2026 02:41
Adds the lazygit 'look inside the process' move the console lacked: 'a' on a
roster agent (or ':attach <agent>') suspends the console and hands the
terminal to the agent's live PTY over the manager's loopback WS attach
endpoint; Ctrl-] (COTAL_DETACH_KEY) returns and repaints. Reuses the CLI's
attach-client and the manager's attach op — the console resolves the ws via a
per-action control-caller-admin mint (like kill/pause) and suspends Ink in
place (App renders null so stdin is released to the PTY; the observer +
MeshView keep running in the background, no rebuild). canControl-gated,
pty-runtime only, same-host (loopback ws).
…f8 stdin

An invalid COTAL_DETACH_KEY no longer process.exit(1)s from inside the console —
attachClient throws, the standalone command exits loudly, the console flashes a
notice (validated before suspending, and the error notice survives the resume).
Normalize stdin data in attachClient: Ink sets stdin.setEncoding("utf8"), which
persists after it releases raw mode, so the 1-byte detach compare never matched
strings and Ctrl-] could not detach from a console attach. New live smoke drives
the real TUI under node-pty through attach -> type -> detach -> repaint and the
bad-key path.
@Lanzelot1 Lanzelot1 force-pushed the feat/console-membership branch from 2e2a867 to 9fa3f87 Compare July 8, 2026 09:42
@Lanzelot1 Lanzelot1 force-pushed the feat/console-attach branch from 388a40a to 47dfed1 Compare July 8, 2026 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant