Repository navigation
feat(tui): show delegated child sessions clearly in the parent - #654
jiro-by-nori[bot] wants to merge 10 commits into
Conversation
🤖 Generated with [Nori](https://noriagentic.com) Co-Authored-By: Nori <contact@tilework.tech>
Terminal proof: readable delegation in the TUIThese are captures of the real Delegation from request to result (GIF, 120×40)1. Cross-agent approval: who asks, which agent runs, the task (120×40)2. Parent while the child waits: one-line card, readable notice, and a request line naming parent › child plus the Alt+Down hint (120×40)3. Session list: child under its parent, amber "waiting on you" (120×40)4. Inside the child: breadcrumb back to the parent with the Alt+Up hint; the child's own permission prompt (120×40)5. Result returns to the parent on its own: result block, then a one-line delivery marker (120×40)6. Session list after completion (120×40)7.
|
🤖 Generated with [Nori](https://noriagentic.com) Co-Authored-By: Nori <contact@tilework.tech>
Verification of 1a9d376: readable delegation in the TUITested end to end with the real Verdict: no blockers. The core flow works (request → approval → waiting → jump → answer → result → tree), but there are 8 should-fix items. Most affect alignment or readable names, which this PR exists to deliver. Test suites (PR head,
|
| Suite | Result |
|---|---|
| nori-harness | 454 passed, 1 ignored |
| nori-tui | 1,514 passed, 4 ignored |
| nori-cli | 71 passed |
| nori-config | 174 passed |
| nori-tui-components | 104 passed, 8 ignored |
| nori-protocol | 0 tests in the crate (see nit 15) |
| tui-pty-e2e | 199 passed, 1 failed, 9 ignored. The failure is the known flaky test_cloud_mode_surfaces_auth_error_from_child_stderr, which passed 3 of 3 re-runs. Not a regression. |
Bugs
Should-fix
nori host ls: a long agent name shifts STATUS, AGE and TITLE. AGENT is padded to 16 cells but never truncated (cli/src/host/table.rs,{agent:<16}). The attach list truncates the same row correctly (reviewer-long…).
Repro: add a[[agents]]entry with slugreviewer-long-agent-name-x, runnori hostandnori attach, type/new, then/delegate reviewer-long-agent-name-x Review the parser changes, approve, and runnori host lsin a 120-column TTY.

nori host lsat 80 columns drops TITLE, so the tree disappears. The fixed prefix is 82 cells, so every row takes the earlytruncate(&prefix)path. TITLE and its├─ └─guides are gone, AGE reads1…, and the status colour is lost.
Repro: in an 80-column terminal, runnori host lswith a parent, child and grandchild.

- A parent gets titled with the protocol header. When
/delegateis the first input in a session, its first prompt is the delegation result. The session is then titled[Nori delegation result] from …in the local session list, the attach header and list, andhost ls.
Repro: start a fresh session, run/delegate mock-model inspect the parser, then press Alt+S.

- A failed child's card shows a Rust
Debugdump. It reads✕ … failed · RequestFailure { request_id: Some(Str("…")), message: "Agent process exited…\nMock agent: …", kind: Fatal }, with literal\nescapes. The parent agent reads the same text. The cause isformat!("{failure:?}")atharness/src/session_manager/delegation_state.rs:136(from 4befa43 in feat(delegation): let a session hand tasks to child sessions #653), which this PR's new card now puts on screen. It should read likefailed · agent exited (code 17).
Repro:/delegate mock-model mock:exit-during-prompt.

- Alt+Down does nothing while the parent has a draft. The request line still says
alt + down to jump, butnext_waiting_sessiononly fires when the composer is empty (tui/src/app/session_keys.rs, a gate added in 2b2c2ec). Nothing tells the user why. The draft itself is safe: it survives the jump through Alt+S and the result delivery, and it stays in the composer.
Repro: while a child is waiting on you, typemy draftin the parent and press Alt+Down. Nothing happens. Clear the composer and it jumps. Either jump with the draft parked, or hide or change the hint while a draft exists. - Names are cut to a fixed length, not to the width. The
REQUEST_LINE_PARENT_CHARS = 12andREQUEST_LINE_CHILD_CHARS = 16limits intui/src/app/session_overview.rscut names to that many characters at any width. At 120 columns the request line uses about 88 cells yet showsparent task… › mock:request-pe…. Sessions that share a title prefix become indistinguishable: the breadcrumb readmock:delegate mock-mode… › mock:delegate mock-mode…(parent and child).
Repro: delegate from a session whose title shares its first 24 characters with the child's task, then open the child.

- The local breadcrumb is not width-aware. At 80 columns it wraps to two rows (
… › mock-model-alt ·/mock:request-permission… · alt + up to parent). The attached breadcrumb fits one row after ff21dab.
Repro: in an 80×30 terminal, delegate tomock-model-altwith a permission marker, then press Alt+Down.

- The attached root gives no sign that a grandchild is waiting. With root → child → grandchild waiting, the root view in
nori attachshowsready, no request line and no notice. In the attach list andhost lsthe root also readsready, while the local list shows the family statuswaiting on you. Alt+Down still finds the grandchild.
Repro: innori attach, type/new, thenmock:delegate mock-model mock:delegate mock-model mock:request-permission:x.

Nits
- A grandchild's breadcrumb shows only
parent › current, not the full path from the root as in mock-up E-5c.
- AGENT names differ by surface. The local list shows display names (
Mock ACP Alt), while cards,host lsand the attach list show slugs (mock-model-alt). - One end state has three words: the result block says
ready, the collapsed line sayscompleted, and the local list saysclosed. The list also repeatsclosedas the row's description. - A declined delegation still sends
Child session: <uuid>to the parent agent for a session that never started. - ID fragments remain for untitled sessions: the attach header shows
mock-model · 24444c0e · readybefore the first prompt, and the fallbacksession 1a2b3c4dis still insession_overview.rs. - In a root session, Alt+Up (the new
parent_sessionkey) falls through to the composer and recalls the previous prompt. The stale⌥↑ edithint is already listed under Known gaps. nori-protocolhas no tests, so the newdelegation_result_promptanddelegation_child_nameare covered only through harness and TUI tests, and "protocol: all pass" means nothing. Thejust test protocolscope still points atcodex-protocol, which no longer exists.
Checks that passed
| # | Check | Result |
|---|---|---|
| 2 | Flag off | Prompt, /agent, /resume, the / menu and ? help screens match main (ae0642c) word for word, apart from the rotating placeholder hint. /delegate is unrecognised, and the hotkey picker hides parent_session. |
| 3 | Delegation card and brief | ↳ you delegated to … and ↳ mock-model delegated to … both show. Ctrl+T shows the brief and child ID. |
| 4 | Approval | Both wordings (user and agent) work. Enter starts the child, Esc declines (✕ … declined by you), and a same-agent child starts without asking. |
| 5 | Waiting child | The request line names parent › child for a child and a grandchild, with no pop-up. Alt+Down jumps, the answer reaches the right session, and rebound alt+j / alt+k work and appear in the hints. |
| 6 | Result | The result block names the child and points to its history (Alt+S opens it). The collapsed one-line prompt appears, and a typed draft survives delivery. |
| 7 | Session tree | The local and attach lists stay aligned at 120 and 80 columns with 2 levels and long titles. Status colours are right in the lists (amber waiting, green ready, dim closed) and on cards (cyan working); no list row was in the working state during capture. Exceptions: should-fix 1, 2 and 8. |
| 8 | Breadcrumb | Alt+Up returns to the parent, both locally and attached. The attached breadcrumb fits 80 columns. |
| 10 | False error from #653 (e93deef) | No ACP agent command channel closed in any parent scrollback after normal child completion (3 sessions). A real child crash still shows as failed (wording: should-fix 4). |
| 11 | Regressions | A crashed parent plus Ctrl+R keeps the same session ID and tree. /close on a parent with a live child and grandchild cancels both and all 3 agent processes exit. No non-zombie nori or mock_acp_agent processes remain after teardown. |
Captures
Request → result, 120×40 (local; the first delegation was declined with Esc, the second approved):
1a9d376 to
847c02d
Compare
Delegations read as a one-line card naming the child by its task, agent and status, with the brief in the transcript view. Results arrive as a short block that names the child and points to its history; the delivered prompt collapses to one line. The cross-agent prompt names who asks, which agent runs, and the task. A waiting child's request line names parent and child, children get a breadcrumb and an Alt+Up parent_session key, and host ls plus the attach list show children under their parent with tree glyphs inside TITLE and colored status words. Delegations now record whether the user or the agent asked (origin), and the harness names sessions readably in notices and result prompts.
…sion 🤖 Generated with [Nori](https://noriagentic.com) Co-Authored-By: Nori <contact@tilework.tech>
Separate result paragraphs at tool calls and permission requests, collapse the delivered result prompt in nori attach too, match it only by its full header, keep the result rail on wrapped lines, limit the breadcrumb and parent key to delegated children, and keep the parent ID for orphaned children in piped host ls output.
🤖 Generated with [Nori](https://noriagentic.com) Co-Authored-By: Nori <contact@tilework.tech>
On a terminal, ID, AGENT, STATUS and AGE shrink to a compact layout before TITLE loses its space, so the tree glyphs, whole ages and status colors stay at 80 columns. A long agent name is cut with an ellipsis instead of pushing the later columns right. A top-level row shows its family's status, so a waiting grandchild marks it as waiting on you. Piped output is unchanged.
- A child that finished its task ends as done; closed is left for sessions the user closed. - A declined delegation tells the parent agent so without naming a session that never started. - A session whose first input is /delegate is titled by that task, never by the result prompt it receives, and a result prompt never starts the first-prompt summary. - The host session list carries each family's status for top-level rows. - Untitled sessions are called "new session" instead of an ID fragment. - just test protocol runs the nori-protocol crate, which now tests the result prompt it builds.
- The breadcrumb inside a delegated child shows the whole path from its top-level session, on one row at any width; sessions between the root and the current one collapse to … when names would get too short. - The waiting-on-you line cuts names to the room the terminal has rather than to a fixed length, and nori attach shows it too, so a top-level session says when a grandchild waits. - Alt+Down reaches a waiting session even with a draft, which stays in its session's prompt; Alt+Up outside a delegated child does nothing. - Every surface names agents by the slug /delegate takes, a finished child reads done everywhere, and its list row keeps its last answer instead of repeating closed.
…s layouts 🤖 Generated with [Nori](https://noriagentic.com) Co-Authored-By: Nori <contact@tilework.tech>
… across frontends - Session path and waiting lines never exceed the terminal width, down to zero columns. - A /delegate sent before any prompt titles the session from the session events, so the local list, nori attach and nori host ls agree. - nori host ls clips plainly below its tightest layout. - The local breadcrumb stops if a parent chain ever loops.
847c02d to
40837dc
Compare
🤖 Generated with [Nori](https://noriagentic.com) Co-Authored-By: Nori <contact@tilework.tech>
Review fixes for the verifier's findings (#6901036688)All 8 should-fix items and nits 9–15 are fixed. New heads:
Linux checks and cargo-deny pass on both heads.
Tests at
Behaviour changes worth a look:
Before / afterCaptures are full colour (Ghostty Web 0.4.0), with the mock agent and the same inputs on both binaries. Text versions sit next to each PNG: proof folder. The 1 · host ls with a long agent name, 120×40
1 · host ls with a long agent name, 80×30
2/8 · host ls, root → child → grandchild waiting, 120×40
2/8 · host ls, same family, 80×30
4 · failed child card, 120×40
4 · failed child card, 80×30
6 · parent's waiting line, 120×40
6 · parent's waiting line, 80×30
7 · local child breadcrumb, 120×40
7 · local child breadcrumb, 80×30 (before wraps to two rows)
8 · nori attach, root view while the grandchild waits, 120×40
8 · nori attach, root view, 80×30
8 · nori attach list, 120×40
8 · nori attach list, 80×30
9 · grandchild breadcrumb in nori attach, 120×40 (three 40-character names: the middle collapses)
9 · grandchild breadcrumb, 80×30
11 · session list after a finished delegation, 120×40
11 · session list, 80×30
11 · result block and delivered line, 120×40
11 · result block and delivered line, 80×30
|
🤖 Generated with [Nori](https://noriagentic.com) Co-Authored-By: Nori <contact@tilework.tech>
Re-verification of review fixes at 40837dc (base #653 at e61d10e)I tested with the real Verdict: no blockers. 14 of the 15 fixes hold. Fix 5 holds in the local TUI but not in Test suites (40837dc,
|
| Suite | Result |
|---|---|
| nori-harness | 454 passed, 3 failed, 1 ignored. A full rerun gave 456 passed, 1 failed. Every failure is in tests/remote_host.rs (see "Not from this PR"). |
| nori-tui | 1,526 passed, 4 ignored |
| tui-pty-e2e | 204 passed, 9 ignored, 0 failed. The known flaky cloud-mode test passed this time. |
nori-protocol (just test protocol) |
4 passed |
Findings
Should-fix
A. nori attach: Alt+Down still does nothing while the composer has a draft. The local TUI now jumps and keeps the draft, but attach still requires an empty composer (tui/src/attach/input.rs:26). The root's line still says alt+down to jump, and nothing tells the user why the key did nothing.
Repro: nori host, then nori attach, then /new, then mock:delegate mock-model mock:delegate mock-model mock:request-permission:x. When the root shows ● … is waiting on you, type my draft and press Alt+Down. Nothing happens. Clear the composer and Alt+Down jumps to the grandchild.

Nits
B. A child reloaded after a host restart never reads closed anywhere. harness/docs.md (around line 688) and the fix report say it reads closed, and no test covers this. What actually happens: after nori host stop and a new nori host, host ls and the attach list are empty, because the host has no snapshot. Reopening the finished child with /resume makes it a live, top-level ready session with its parent link gone. Either correct the doc or add a test for the path you meant.
Repro: finish a delegation under nori host, run nori host stop then nori host, then nori host ls. Then in nori run /resume, pick the child, and press Alt+S.

C. Sessions that share a long title prefix still look the same at 120 columns (fix 6). Names now use the width, but they are cut only at the tail. A parent and child that share 37 characters read shared prefix for the parser r… › shared prefix for the parser…. A 24-character shared prefix, as in the original repro, now fits. Cutting in the middle, or keeping the end of each name, would separate longer prefixes too.
Repro: in nori, run /new, then send shared prefix for the parser review: parent side, then /delegate mock-model-alt shared prefix for the parser review: child side mock:request-permission:z and approve it.

D. At 80 columns the attached grandchild breadcrumb shrinks its own name before the key hint. It reads mock-model · mock:d… › … › mock-model · mock:… · waiting on you · alt+up parent. The current session's name should be the last thing to shrink. Drop alt+up parent first.

E. At 80 columns the collapsed › delegation result from … line still wraps to two rows. The child name in it is cut at a fixed length (review-the-lexe…), and the protocol header the parent agent receives carries the same cut name.
Repro: at 80×30, approve /delegate mock-model-alt mock:request-permission:review-the-lexer-tokens, answer the request, then press Alt+Up.

F. The ↳ … delegated to … · working card still says working after the child is done, locally and attached. Each state change is a new cell by design, so this is a wording problem. started would not go stale.
G. Small done inconsistencies.
- The attached root header reads
· readywhile the attach list andhost lsshow the family statuswaiting on you. doneandclosedare both dim inhost lsand the attach list.- A finished child's view ends with
Session ended. Use /new to start another session., which reads like a user close.
Fixes 1–15
| # | Result | Evidence |
|---|---|---|
| 1 | ✅ reviewer-long-a… at 120 and reviewer-long… at 80; columns stay put |
fix1-hostls-long-120/80 |
| 2 | ✅ 80 columns keeps TITLE with └─ guides, the whole AGE and the colours |
fix2-hostls-tree-80 |
| 3 | ✅ A parent whose first input is /delegate is titled by its task in the local list, the attach header, the attach list and host ls |
fix3-fix11-local-list-120, fix1-hostls-long-120 |
| 4 | ✅ ✕ … failed · Agent process exited unexpectedly (exit code 17). The parent agent gets the same line |
fix4-local-failed-80 |
| 5 | fix5-fix7-local-child-120, fix5-attach-draft-altdown-120 | |
| 6 | ✅ with nit C | fix6-* |
| 7 | ✅ The local child breadcrumb fits one row at 80 | fix7-local-child-80 |
| 8 | ✅ The attached root shows ● … › … is waiting on you · alt+down to jump; the attach list and host ls show the root as waiting on you at 120 and 80 |
fix8-* |
| 9 | ✅ The path runs from the root, and the middle collapses to … (nit D) |
fix9-* |
| 10 | ✅ Slugs (mock-model, mock-model-alt) in the local list, cards, attach and host ls |
fix3-fix11-local-list-120 |
| 11 | ✅ done everywhere for a finished child, with no repeated closed description |
fix3-fix11-local-list-120, done-* |
| 12 | ✅ A declined delegation sends The user declined this delegation. with no Child session: line |
fix12-local-declined-120 |
| 13 | ✅ The attach header reads mock-model · new session · ready before the first prompt |
(text check) |
| 14 | ✅ Alt+Up in a top-level session does nothing, and plain Up still recalls history | (text check) |
| 15 | ✅ just test protocol runs nori-protocol: 4 passed |
test log |
Other checks
| Check | Result |
|---|---|
done in host ls and the attach list |
✅ The child and grandchild read done; the root reads ready once the family finishes |
Flag off (multi_session = false) |
✅ I ran the same inputs (start, prompt, / menu, ? help, /agent, /delegate, Alt+Up) through main (ae0642c) and this head. The text diff is only the rotating placeholder hint. /delegate is unrecognised, and Alt+Up changes nothing. Texts: flagoff-main/, flagoff-pr/ |
| Leftover processes | ✅ No live nori or mock_acp_agent after local TUI exit, two nori host stops, or the suites. 201 zombie entries remain, all reparented to Modal's non-reaping PID 1 |
Not from this PR
Three cancel-timing tests in harness/tests/remote_host.rs fail most of the time on this machine even when run alone, with Elapsed(()) at lines 855, 924 and 991:
remote_cancel_still_cancels_a_remote_owned_turnimmediate_remote_cancel_waits_for_remote_turn_registrationdropped_prompt_registration_does_not_wedge_a_reconnected_controller
At this head, the test file failed 2 of 3 runs. At E1 e61d10ec it failed 4 of 5, so the problem predates this PR. That is far more often than the "failed once under load" noted earlier. It should get its own fix in the stack, not in this PR.

















































































Summary
🤖 Generated with Nori
Makes delegated child sessions easy to read in the TUI, behind
[features] multi_session. Stacked on #653. Single-session use and flag-off behaviour are unchanged.↳ you delegated to mock-model-alt · Review the parser · working. One line names the child by its task, plus its agent and status. The full brief opens with the transcript key (Ctrl+T).asked by/runs on/taskrows, andYes, start it/No, decline. Esc declines.● parent task… › mock:request-pe… is waiting on you · approve: … · alt + down to jumpwith an amber ●. The parent's notice names the child and the parent by name. No pop-up.○ result from <agent> · <child> · ready, up to six result lines under a▏rail, thenreturns to <agent> on its own · its history: alt + s. The delivered result prompt collapses to one line; the transcript view keeps the full text the agent read.nori host lsand thenori attachlist put children under their parent, with├─ └─ │inside TITLE. ID, AGENT, STATUS and AGE stay fixed. Status words are coloured: waiting on you amber, working cyan, ready green, failed red.mock-model · Parent › mock-model-alt · Child · alt + up to parent. Newparent_sessionhotkey (Alt+Up by default) returns to the parent. Forks are excluded.(parent 1a2b3c4d)are gone from rows, request lines, notices and approval prompts.Design
nori attach, so both frontends say the same thing.Delegationgains an additiveoriginfield (agent|user, serde defaultagent), so a card can say "you delegated" or " delegated". Records from older hosts read as agent requests.[Nori delegation result] from <agent> · <child> · <outcome>, thenChild session: <id>, then the text. The parent's own ID is dropped. Paragraph breaks are kept where tool calls or permission requests split the child's output. The header prefix is unchanged, so existing matching still works.tree_column, places tree guides and the(current)badge in TITLE rather than the first column.tree_orderorders(id, parent)rows depth-first; missing parents become roots, and cycles are safe.Validation
host lstree order with fixed columns, narrow-width truncation, orphan parent ID, and ANSI colours.--no-fail-fast, mock agent): the full suite passed on the first commit, and the known flaky cloud auth-error test also passed. After the review fixes I re-ran the delegation, session-list, host-attach and multi-session targets. The new scenariodelegation_reads_clearly_from_request_to_resultdrives request → approval → waiting child → Alt+Down → breadcrumb → Alt+Up → result → tree, and passed 8 repeated runs in a row.1a9d3761.Known gaps
⌥↑ editis a stale hint that predates this branch: nothing handles that key. In a child session Alt+Up now goes to the parent. I left the hint alone because removing it changes flag-off UI; it needs a separate decision.Alt+Downliterally. The TUI request line shows the configured binding.Draft. Do not merge.
Originating chat: https://demo.norisessions.com/chat/01a1e081-859a-7375-a670-d0f8fd38155e
Share Nori with your team: https://www.npmjs.com/package/nori-skillsets