Add local child agents to TUI#13776
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
665b765 to
5511f60
Compare
a725a0d to
7fde057
Compare
589ec22 to
5055c52
Compare
7fde057 to
289a5d1
Compare
289a5d1 to
cbbd8d0
Compare
c4eb571 to
e1b93b0
Compare
6bb0618 to
6a04f77
Compare
94f69eb to
626fa04
Compare
6a04f77 to
1e9b80f
Compare
626fa04 to
90c65aa
Compare
1e9b80f to
063d148
Compare
90c65aa to
a548617
Compare
97603f3 to
b030f4f
Compare
a548617 to
e38c4a2
Compare
b030f4f to
6a48d86
Compare
e38c4a2 to
1519f99
Compare
| @@ -0,0 +1,93 @@ | |||
| //! Frontend-neutral preparation and settings propagation for local Oz children. | |||
There was a problem hiding this comment.
this is mostly just code moved from GUI-specific places into shared helpers
| | TodoOperation::MarkAsCompleted { .. } => {} | ||
| }, | ||
|
|
||
| // TODO: add full status rendering for sub-agents. |
There was a problem hiding this comment.
this is covered in a PR above this one in the stack, so don't index on the gross message UI in this PR
| } | ||
|
|
||
| /// Creates and registers a full local terminal session. | ||
| pub(crate) fn create_local_terminal_session( |
There was a problem hiding this comment.
this is just a re-org so session creation can be done elsewhere using the same code
There was a problem hiding this comment.
I think session creation should be driven by the TuiSessionModel itself
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
a039404 to
aa12618
Compare
There was a problem hiding this comment.
Overview
This PR adds shared local Oz child-agent launch preparation and wires the TUI to materialize accepted local child agents as retained background sessions, with model override handling, event-consumer ownership, transcript rendering updates, and tests. The provided spec context contains no approved repository spec to validate against; the PR description includes a Loom demo for the user-facing TUI behavior.
Concerns
- Background child session creation still focuses the newly created session view input, which can steal keyboard focus from the parent even though the session registry remains focused on the parent.
- The TUI materializer listens to every
StartAgentExecutorcreate event, so direct model-emittedStartAgenttool calls can launch local children without the acceptedrun_agentsapproval/config path described by the PR. - Removing a parent conversation still leaves its
children_by_parententry behind, so the topology index can return stale child IDs for a deleted parent. - Security/privacy: one new warning log includes the agent-controlled child name; avoid putting user/agent-controlled text into Info-and-above breadcrumbs.
Verdict
Found: 0 critical, 3 important, 1 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
| ) { | ||
| let sessions = TuiSessions::handle(ctx); | ||
| let (session_id, session_view) = | ||
| create_local_terminal_session(&sessions, false, working_directory, ctx); |
There was a problem hiding this comment.
focus=false only affects the session registry; constructing the child TuiTerminalSessionView still focuses its input view, so launching a background child can steal keyboard focus from the parent. Plumb the focus intent into view construction or restore parent focus after creating unfocused child sessions.
There was a problem hiding this comment.
Can you double check if this is true?
| }); | ||
| let start_agent_executor = action_model.as_ref(ctx).start_agent_executor(ctx); | ||
| ctx.subscribe_to_model(&start_agent_executor, |view, _, event, ctx| match event { | ||
| StartAgentExecutorEvent::CreateAgent(request) => { |
There was a problem hiding this comment.
StartAgentExecutor create event into the TUI materializer; direct model-emitted StartAgent tool calls autoexecute outside the accepted run_agents approval/config path, so carry provenance or otherwise gate this path to approved run_agents dispatches.
| self.children_by_parent.retain(|_, child_ids| { | ||
| child_ids.retain(|child_id| *child_id != conversation_id); | ||
| !child_ids.is_empty() | ||
| }); |
There was a problem hiding this comment.
child_conversation_ids_of(&removed_parent) cannot return orphaned children.
| self.children_by_parent.retain(|_, child_ids| { | |
| child_ids.retain(|child_id| *child_id != conversation_id); | |
| !child_ids.is_empty() | |
| }); | |
| self.children_by_parent.remove(&conversation_id); | |
| self.children_by_parent.retain(|_, child_ids| { | |
| child_ids.retain(|child_id| *child_id != conversation_id); | |
| !child_ids.is_empty() | |
| }); |
| ) { | ||
| log::warn!( | ||
| "Failing TUI child agent request '{}': {message}", | ||
| request.name |
There was a problem hiding this comment.
💡 [SUGGESTION] [SECURITY] request.name is model/user-controlled, and warn-level logs can become Sentry breadcrumbs in crash-reporting builds; omit it, use a non-sensitive request identifier, or use a safe logging macro with redacted release output.
6a48d86 to
d97af3b
Compare
aa12618 to
34e7925
Compare
| self.children_by_parent.retain(|_, child_ids| { | ||
| child_ids.retain(|child_id| *child_id != conversation_id); | ||
| !child_ids.is_empty() | ||
| }); |
| /// execution profile. Orchestrated child runs use this because a requested | ||
| /// model equal to the profile default must still override the TUI's | ||
| /// file-backed model setting. | ||
| pub(crate) fn set_agent_mode_llm_override( |
There was a problem hiding this comment.
Seems like emitting UpdatedActiveAgentModeLLM triggers a bunch of handlers downstream. ooc why do we need this specifically for TUI?
| }); | ||
| let model_for_sessions = model.clone(); | ||
| ctx.subscribe_to_model(&sessions, move |sessions, event, ctx| match event { | ||
| TuiSessionsEvent::SessionAdded(session_id) => { |
There was a problem hiding this comment.
Is SessionAdded the right trigger for registering entry in the orchestration model? A new session doesn't mean it is being orchestrated / should have child sessions yeah?
| ) { | ||
| let sessions = TuiSessions::handle(ctx); | ||
| let (session_id, session_view) = | ||
| create_local_terminal_session(&sessions, false, working_directory, ctx); |
There was a problem hiding this comment.
Can you double check if this is true?
| history.delete_conversation(*conversation_id, terminal_surface_id, ctx); | ||
| }); | ||
| if let Some(session_id) = self.child_session_by_conversation.remove(conversation_id) { | ||
| TuiSessions::handle(ctx).update(ctx, |sessions, ctx| { |
There was a problem hiding this comment.
I think the modeling dependency between TuiSessions and the orchestration model is a bit reversed imo
Right now the call graph is:
- Orchestration model subscribes to TuiSessions for updates
- On update handler result, the orchestration model calls directly into TuiSessions to create / clean up sessions
I think the correct flow should be:
- TuiSessions subscribe to Orchestration model for spawning / clean up sessions
- On orchestration attempt, TuiSessions call into the orchestration model to register parent / child orchestrations
| } | ||
|
|
||
| /// Creates and registers a full local terminal session. | ||
| pub(crate) fn create_local_terminal_session( |
There was a problem hiding this comment.
I think session creation should be driven by the TuiSessionModel itself
…essions The orchestration model subscribes to every session's StartAgentExecutor via TuiSessions registration, materializes native (Oz) local children into background sessions (create_agent_task -> shared create_local_terminal_session helper -> child conversation linkage -> prompt dispatch), and resolves CLI-harness/remote requests as clean per-child failures instead of spawn timeouts. Exports the StartAgent executor surface through tui_export and adds minimal transcript lines for inter-agent messages/lifecycle events (TODO(code-1822) for richer rendering). Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
d97af3b to
eb8c75e
Compare
34e7925 to
8179fca
Compare

Description
Adds
TuiOrchestrationModel, the TUI coordinator that turns accepted localrun_agentsrequests into native Oz child agents hosted by retained background terminal sessions. The parent session remains focused while each child gets a completeTuiTerminalSessionViewand terminal manager, preserving the multi-session ownership model established in the PR below.Each session view owns its
StartAgentExecutorsubscription and emits semantic launch/cleanup events with the parent working-directory snapshot. The coordinator subscribes to every session registered inTuiSessions, including background children, so descendants use the same path without exposing action/controller model handles through the view. Native launch creates the server task first, then creates the background session, inherits the parent execution profile/base model/current directory, applies any explicit run-wide model override, establishes conversation lineage, and sends the first prompt.Introduces a shared local-Oz launch layer used by both GUI hidden panes and TUI background sessions. It owns task creation, agent-name normalization, setting inheritance, and explicit model overrides, while each frontend retains only its surface materialization. TUI model resolution now honors explicit per-surface child overrides before the file-backed
agents.modeldefault.The coordinator also owns orchestration-event consumer lifetimes. Parent and child streams are registered only after successful materialization and are removed with their session.
The parent transcript now renders received child-message sender/subject lines and lifecycle-event counts, and suppresses the otherwise-empty
WaitForEventstool row. Rich child-message UI, remote/CLI-harness materialization, and child-session navigation remain in the PRs above.Ownership and data flow
Linked Issue
CODE-1822 — Orchestration
Demo
Watch the local child-agent orchestration demo on Loom
Review guide
app/src/ai/blocklist/child_agent_launch.rsandapp/src/pane_group/pane/terminal_pane.rs— shared native task preparation/settings and GUI reuse.crates/warp_tui/src/orchestration_model.rs— session event routing, native launch phases, conversation/session ownership, streamer consumers, and failure cleanup.crates/warp_tui/src/terminal_session_view.rs,session.rs, andsession_registry.rs— executor-to-session events, semantic child startup, parent-cwd session creation, and retained background sessions.app/src/ai/llms.rsandapp/src/ai/blocklist/history_model.rs— explicit TUI child model overrides and topology cleanup on conversation removal.crates/warp_tui/src/agent_block.rs— minimal received-message/lifecycle rendering and hiddenWaitForEventsrows.specs/code-1822-tui-local-children/TECH.md— as-built architecture, ownership boundaries, non-goals, and validation.Testing
./script/runAgent Mode
CHANGELOG-NONE
Co-Authored-By: Oz oz-agent@warp.dev