Files
codex/codex-rs/tui/src/chatwidget/side.rs
T
Felipe Coury 3f0669dbd6 Make parent-owned sub-agent threads read-only in the TUI (#33841)
## Why

Multi-agent V2 spawned threads are controlled by their parent and reject direct
app-server input. The TUI should expose that ownership instead of presenting an
input flow that the server will reject.

## What changed

- Add the experimental `Thread.canAcceptDirectInput` capability for loaded
  threads and populate it from the same policy used to validate `turn/start`
  and `turn/steer`. Stored, unloaded threads report `null`.
- Carry the capability through TUI thread discovery, selection, startup, and
  resume flows, with a compatibility fallback for older app servers.
- Present parent-owned threads as view-only, preserve drafts and queued input,
  block agent-directed commands and settings shortcuts, and keep local
  navigation and inspection commands available.

## Testing

- Cover V1 writable and V2 view-only spawned threads through selection and
  resume.
- Cover composer submission paths, queued and initial input preservation,
  allowed navigation commands, settings shortcuts, and the view-only UI.

GitOrigin-RevId: 495ad051249e5577fb0b0c1cc8c40a0998d04918
2026-07-17 15:14:47 +00:00

39 lines
1.3 KiB
Rust

//! Chat widget hooks for side-conversation mode.
//!
//! App-level side-thread lifecycle lives in `app::side`; this module owns the
//! chat-surface pieces that side mode toggles, such as the composer placeholder,
//! footer label, and inline `/side` message submission behavior.
use super::*;
impl ChatWidget {
pub(crate) fn submit_user_message_as_plain_user_turn(
&mut self,
user_message: UserMessage,
) -> Option<AppCommand> {
self.submit_user_message_with_shell_escape_policy(user_message, ShellEscapePolicy::Disallow)
}
pub(crate) fn set_side_conversation_active(&mut self, active: bool) {
self.active_side_conversation = active;
let placeholder = if active {
self.side_placeholder_text.clone()
} else {
self.normal_placeholder_text.clone()
};
self.bottom_pane.set_placeholder_text(placeholder);
self.bottom_pane.set_side_conversation_active(active);
if self.blocks_direct_input && !active {
self.bottom_pane.set_parent_owned_thread();
}
}
pub(crate) fn side_conversation_active(&self) -> bool {
self.active_side_conversation
}
pub(crate) fn set_side_conversation_context_label(&mut self, label: Option<String>) {
self.bottom_pane.set_side_conversation_context_label(label);
}
}