Synced from monorepo

Synced from monorepo

Changes:
- Workspace server: report `/ready` as failed with dwell on hub connect failure
- Refresh OIDC token for the Grok agent in the shell
- ACP terminal output recorder
- Cross-platform provider auth commands in the shell
- Default `/resume` to Grok sessions with a hint for hidden external sessions
- Resume sessions by title with `--resume`
- Limit app-builder archive size
- Data-driven tag labels for slash commands
- Doctor fixes for tmux
- Custom provider gateways and subprocess environment policy in the shell
- `/tutorial` — opt-in onboarding tour of Grok Build
- Soft and required CLI version checks in the shell
- Privacy banner env overrides survive live settings updates
- Add remote flag to override the image-edit model
- Return profile fields from auth info even when the access token is expired
- Add edit control on queued prompt rows
- Keep fail-closed policy when clearing orphans with no team
- Setting to disable the Ctrl+Space/F8 voice shortcut
- Pass `--raw` to pw-record so Linux dictation works on older PipeWire
- Validate git URLs when adding marketplace entries
- Stop shipping stale tool-doc parameter and tool names
- Re-point dashboard attach after `/fork` only when the parent was attached
- Surface Grok Computer media-generation results as file-path chunks
- Clear web background-task tray on kill and keep the task description
- Show privacy upsell banner in agent view until acted on
- Add tools-server client callback surface
- Protect persistent global hook sources

Source-Revision: 95d84f443eddcbed6cbfd6eed22e2eafe6b3939d
This commit is contained in:
grokkybara[bot] 2026-07-23 17:12:33 +00:00
commit 69f0ba880a
286 changed files with 22939 additions and 9624 deletions

View file

@ -193,7 +193,12 @@ fn resume_body_rows(agent: &AgentView, width: u16) -> u16 {
state,
Some(current_repo.as_str()),
);
measure_entries(&picker_entries)
// Reserve a row for the pinned hidden-external hint when shown.
let hint_row = u16::from(
!agent.app_chat_mode
&& minimal_api::hidden_external_hint(entries.as_deref(), *source_filter).is_some(),
);
measure_entries(&picker_entries).saturating_add(hint_row)
}
fn render_resume(
@ -203,6 +208,7 @@ fn render_resume(
theme: &Theme,
) -> Option<(u16, u16)> {
let cwd = agent.session.cwd.to_string_lossy().to_string();
let chat_mode = agent.app_chat_mode;
let Some(ActiveModal::SessionPicker {
entries,
state,
@ -212,7 +218,7 @@ fn render_resume(
else {
return None;
};
let (title_row, search_row, divider_row, list_area, footer_row) = chrome_layout(area);
let (title_row, search_row, divider_row, mut list_area, footer_row) = chrome_layout(area);
let entries_data = entries.as_deref().unwrap_or(&[]);
let content_width = area.width.saturating_sub(2);
@ -238,6 +244,9 @@ fn render_resume(
state,
Some(current_repo.as_str()),
);
let hidden_hint = (!chat_mode)
.then(|| minimal_api::hidden_external_hint(entries.as_deref(), *source_filter))
.flatten();
render_title(buf, title_row, theme, "Resume session");
// Focus-aware search bar (cursor only when search is focused).
@ -256,6 +265,21 @@ fn render_resume(
);
render_divider(buf, divider_row, theme);
// Pinned above the list so it stays visible regardless of list scroll.
if let Some(hint) = hidden_hint.as_deref() {
render_dim_line(
buf,
Rect {
height: 1,
..list_area
},
theme,
hint,
);
list_area.y += 1;
list_area.height = list_area.height.saturating_sub(1);
}
let nsc = vec![false; picker_entries.len()];
let hit = picker::render_picker_content(
buf,
@ -493,14 +517,22 @@ fn render_mcps(
// ─────────────────────────────── helpers ────────────────────────────────────
/// Sum the display height of grouped picker entries: a header is one row; a row
/// is its label line plus its collapsed summary lines (what the picker draws
/// when the row is not expanded).
/// Sum the display height of grouped picker entries: a header is one row (plus
/// the blank spacer `render_picker_content` draws before non-first headers); a
/// row is its label line plus its collapsed summary lines (what the picker
/// draws when the row is not expanded).
fn measure_entries(entries: &[PickerEntry<'_>]) -> u16 {
entries
.iter()
.map(|e| match e {
PickerEntry::Header { .. } => 1u16,
.enumerate()
.map(|(idx, e)| match e {
PickerEntry::Header { .. } => {
if idx == 0 {
1u16
} else {
2u16
}
}
PickerEntry::Row(r) => {
if r.expanded {
1u16.saturating_add(r.description_lines.len() as u16)
@ -678,6 +710,37 @@ mod tests {
);
}
#[test]
fn resume_panel_pins_hidden_external_hint_above_scrolling_list() {
// More native rows than the panel fits: the hint must stay pinned
// above the list instead of scrolling away with it.
let mut entries: Vec<_> = (0..20)
.map(|i| session_entry(&format!("native-{i}")))
.collect();
let mut foreign = session_entry("claude-session");
foreign.source = "claude".into();
entries.push(foreign);
let mut a = with_resume(entries);
let theme = Theme::current();
let area = Rect::new(0, 0, 80, 10);
let mut buf = Buffer::empty(area);
render(&mut buf, area, &mut a, ListPanel::Resume, &theme);
let text = buffer_text(&buf);
assert!(
text.contains("1 external session hidden \u{b7} f to show"),
"hidden foreign rows must stay explained while the list scrolls:\n{text}"
);
assert!(
text.find("external session hidden") < text.find("native-"),
"the hint must be pinned above the first list row:\n{text}"
);
assert!(
!text.contains("claude-session"),
"foreign row stays hidden under the default filter:\n{text}"
);
}
#[test]
fn resume_search_uses_picker_grapheme_viewport_at_narrow_width() {
let grapheme = "👩🏽\u{200d}💻";