feat(tui): render hierarchical agent tree path (root/agent/sub-agent) instead of raw thread UUIDs (#23588)#356
Merged
Merged
Conversation
…UUIDs Collab transcript rows (Waiting for / Spawned / Sent input to / Resuming / Resumed / Closed / wait-complete) previously printed the full 36-character thread UUID whenever an agent had no nickname, e.g. `Waiting for 019f8894-89dc-70f2-ad8e-d74deba8ed9b`. Fix the single choke point `agent_label_spans` so the no-nickname branch renders a readable hierarchical path (`root/agent/sub_agent`) and, as a last resort, an 8-character short id — never a full UUID. - `AgentMetadata`/`AgentLabel` gain a precomputed `tree_path`; the fallback branch prefers it, then a short id. - `AgentNavigationState` gains `parents` and `paths` maps so ancestry survives thread switches (which rebuild the ChatWidget). - New `App::agent_tree_path` resolves, in order: (a) the server-composed authoritative `AgentPath` (leading `/` stripped, leaf swapped for the thread's nickname); (b) a parent-chain walk to root labeling each node by nickname -> `[role]` -> short id, with a visited-set cycle guard and an ellipsis prefix for unconfirmed chains; (c) `thread_label` (short id). - Depth is capped at 4 segments (`root/…/<parent>/<leaf>`) with per-segment grapheme truncation. Cycles, self-parents, and orphaned parent edges terminate safely with a `…/` prefix. - `parent_thread_id` + `source.get_agent_path()` are captured at the ThreadStarted, thread/read liveness, loaded-subagent backfill, and local / external spawn upsert sites. Addresses upstream openai/codex#23588 (TUI never rendered the AgentPath the fork already composes server-side).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The Codewith TUI never rendered the canonical
AgentPath(/root/agent/sub_agent) the fork already composes server-side. Collab transcript rows fell back to the raw 36-character thread UUID whenever an agent had no nickname, e.g.Waiting for 019f8894-89dc-70f2-ad8e-d74deba8ed9b.Addresses upstream openai/codex#23588.
The single choke point
multi_agents.rs::agent_label_spans()feeds all seven collab rows (Waiting for / Spawned / Sent input to / Resuming / Resumed / Closed / wait-complete). The no-nickname branch printedthread_id.to_string()(full UUID). It now renders a precomputed hierarchicaltree_path, and as a last resort an 8-character short id — never a full UUID.How
AgentMetadata/AgentLabelgain atree_path; the no-nickname branch prefers it, then a short id.AgentNavigationStategainsparentsandpathsmaps so ancestry survives thread switches (which rebuild theChatWidget).App::agent_tree_pathresolves in order:AgentPath(leading/stripped, leaf swapped for the thread's nickname when known);[role]→ 8-char short id, with a visited-set cycle guard and an…/prefix when the chain to root cannot be confirmed;thread_label(short id, never a full UUID).parent_thread_id+source.get_agent_path()are captured at the ThreadStarted, thread/read liveness, loaded-subagent backfill, and local/external spawn upsert sites.Edge cases handled
root/…/<parent>/<leaf>, with per-segment grapheme truncation (truncate_text).…/-prefixed.…/<leaf>./sideforks, primary): noagent_path, no parent →thread_label, unchanged.Tests
agent_tree_pathunit tests (thread_routing.rs): authoritative-path formatting + nickname leaf swap; parent-walk nickname→role→short-id; depth-cap collapse; cycle guard; orphaned parent; primary →thread_label; unknown thread → short id. Each asserts the output never contains a 36-char UUID.multi_agents.rsrendering tests: no-nickname row renders the tree path (and, without one, an 8-char short id) instead of the receiver UUID.collab_agent_transcript/collab_resume_interruptedsnapshots are unchanged (they use nicknames → first branch).Second raw-UUID surface fixed
The live "Waiting for N agents" status indicator (
collab_wait_status.rs) also rendered the raw thread UUID for nickname-less agents. It now uses the same nickname →tree_path→ 8-char short id fallback.Verification (Blacksmith,
codex-tui)New tests, all passing:
agent_tree_path_*(8): authoritative-path + nickname leaf swap, without-nickname, parent-walk nickname→role→short-id, deep-path collapse, cycle guard, orphaned parent, primary→thread_label, unknown→short id (each asserts no 36-char UUID in output).multi_agents::nickname_less_row_renders_tree_path_instead_of_uuidand..._renders_short_id_not_uuid.loaded_threads::captures_authoritative_agent_path_and_parent_for_loaded_subagent.Two snapshots updated — both flip a full UUID to an 8-char short id (the intended fix, reviewed):
app_server_collab_spawn_completed_renders_requested_model_and_effort:Spawned 019cff70-…-b91781b41a8e→Spawned 019cff70.collab_wait_status::multiple_wait_status:• 019cff70-…-b96db334332d — Working→• 019cff70 — Working.TUI-only rendering change; does NOT modify server-side path composition.