Synced from monorepo

Changes:
- Detect the herdr multiplexer
- Mark /gboom as non-production code
- Bound peak memory when loading a large session
- Add a subagent lifecycle soak bounding threads, fds, and heap
- Stream inherited replay to bound fork memory
- Copy full plan from plan approval with y
- Stop armed signature verification from deleting the managed-deny smoke policy
- Add source-tagged terminal version telemetry
- Show the UI instantly and fetch models and settings in the background
- Session test helpers
- computer_reason on the ConversationHistoryDone trailer
This commit is contained in:
grokkybara[bot] 2026-07-26 20:03:03 +01:00
commit b41c75a578
92 changed files with 9410 additions and 3788 deletions

View file

@ -553,6 +553,8 @@ pub struct PlanViewerExtras {
pub comment_hovered: bool,
pub abandon_button_area: Option<Rect>,
pub abandon_hovered: bool,
pub copy_button_area: Option<Rect>,
pub copy_hovered: bool,
pub last_click_at: Option<std::time::Instant>,
pub gutter_drag_start: Option<usize>,
pub gutter_drag_end: Option<usize>,
@ -822,8 +824,7 @@ impl LineViewerState {
}
/// Whether the plan modal should render the action-button footer.
/// True for both modes: plan-approval (q/c/s|a) and casual
/// (c/s — quit via the close-X button instead of a footer button).
/// True for plan-approval and casual plan preview (not plain file preview).
pub fn show_footer(&self) -> bool {
self.plan
.as_ref()
@ -1477,10 +1478,7 @@ pub fn render_line_viewer(
// Buttons use the same `key bold + label dim` treatment as
// `render_modal_shortcuts`, sit in a single row separated by
// ` | `, centered within the modal frame.
//
// - Plan-approval: q quit | c comment | s send / a approve
// - Casual preview: c comment | s send (no `q` —
// the close-X button handles closing in casual mode)
// Casual preview omits `q` (close via the X button).
if viewer.show_footer() && inner.height >= 2 {
let div_y = inner.y + inner.height - 2;
let div_style = Style::default().fg(theme.gray_dim).bg(theme.bg_base);
@ -1492,11 +1490,15 @@ pub fn render_line_viewer(
let abandon_hovered = viewer.plan_ref().is_some_and(|p| p.abandon_hovered);
let comment_hovered = viewer.plan_ref().is_some_and(|p| p.comment_hovered);
let approve_hovered = viewer.plan_ref().is_some_and(|p| p.approve_hovered);
let copy_hovered = viewer.plan_ref().is_some_and(|p| p.copy_hovered);
let is_approval = viewer.feedback_active();
let comment_spans = build_shortcut_button('c', "comment", comment_hovered, theme);
let comment_w: u16 = comment_spans.iter().map(|s| s.width() as u16).sum();
let copy_spans = build_shortcut_button('y', "copy plan", copy_hovered, theme);
let copy_w: u16 = copy_spans.iter().map(|s| s.width() as u16).sum();
// In approval mode, always show `a approve`. When there are
// pending review comments, also show `s revise` (request changes).
// In approval mode, show `a approve` (or `a approve w/ comments`
@ -1555,18 +1557,20 @@ pub fn render_line_viewer(
let sep_w: u16 = 5; // separator is fixed-width ASCII; matches modal_window.rs:565
let sep_style = Style::default().fg(theme.gray_dim).bg(theme.bg_base);
// Total width: [action] + (sep + revise)? + sep + comment[badge?] + (sep + quit)?
let mut total_w: u16 = 0;
let mut base_w: u16 = 0;
if action_w > 0 {
total_w = total_w.saturating_add(action_w).saturating_add(sep_w);
base_w = base_w.saturating_add(action_w).saturating_add(sep_w);
}
if revise_w > 0 {
total_w = total_w.saturating_add(revise_w).saturating_add(sep_w);
base_w = base_w.saturating_add(revise_w).saturating_add(sep_w);
}
total_w = total_w.saturating_add(comment_w).saturating_add(badge_w);
base_w = base_w.saturating_add(comment_w).saturating_add(badge_w);
if let Some((_, w)) = &quit_spans {
total_w = total_w.saturating_add(sep_w).saturating_add(*w);
base_w = base_w.saturating_add(sep_w).saturating_add(*w);
}
let with_copy_w = base_w.saturating_add(sep_w).saturating_add(copy_w);
let show_copy = with_copy_w <= inner.width;
let total_w = if show_copy { with_copy_w } else { base_w };
if total_w <= inner.width {
let mut x = inner.x + (inner.width - total_w) / 2;
@ -1620,6 +1624,20 @@ pub fn render_line_viewer(
x += badge_w;
}
if show_copy {
buf.set_string(x, bottom_y, separator, sep_style);
x += sep_w;
let copy_x = x;
for span in &copy_spans {
let w = span.width() as u16;
buf.set_span(x, bottom_y, span, w);
x += w;
}
viewer.plan_mut().copy_button_area = Some(Rect::new(copy_x, bottom_y, copy_w, 1));
} else {
viewer.plan_mut().copy_button_area = None;
}
// Quit button — approval mode only.
if let Some((spans, w)) = quit_spans {
buf.set_string(x, bottom_y, separator, sep_style);
@ -1640,6 +1658,7 @@ pub fn render_line_viewer(
let plan = viewer.plan_mut();
plan.approve_button_area = None;
plan.comment_button_area = None;
plan.copy_button_area = None;
plan.abandon_button_area = None;
}
}
@ -1683,6 +1702,20 @@ mod tests {
);
}
#[test]
fn plan_preview_exposes_full_raw_markdown_for_copy() {
let body = "# Plan\n\n- Do the thing\n- Then ship";
let mut viewer = LineViewerState::open_markdown_content("plan.md", body.to_owned(), None)
.expect("markdown content should open");
viewer.kind = LineViewerKind::PlanPreview;
viewer.prepare_layout(80, 20);
assert_eq!(
viewer.markdown_content_for_feedback().as_deref(),
Some(body)
);
}
fn line_text(line: &Line<'_>) -> String {
line.spans
.iter()

View file

@ -1826,6 +1826,8 @@ impl PromptWidget {
terminal.multiplexer = %evt.terminal.multiplexer,
terminal.is_ssh = evt.terminal.is_ssh,
terminal.term_var = %evt.terminal.term_var,
terminal.term_version = %evt.terminal.term_version,
terminal.term_version_source = %evt.terminal.term_version_source,
key.code = %evt.key_code,
key.modifiers = %evt.key_modifiers,
key.kind = %evt.key_kind,