Synced from monorepo

Changes:
- Gate session-lifecycle heap steady state with a dhat soak
- Unbreak merge lifecycle e2e after default model → grok-4.5
- Scan home-scope rules dirs at <root>/rules
- Complete text-input paste and terminal parity
- Gate project roles and personas
- Use canonical editing in dialogs
- Use canonical editing in search bars
- Reject ambiguous MCP tool IDs
- Harden Git operands for plugins
- Simplify queue drain API
- Pass RFC 9207 iss through MCP OAuth token exchange
- Show leader roster when local agents map is empty
- Use canonical editing in Persona views
- Remove marketplace default-skills auto-install and purge old installs
- Use canonical editing in extension forms
- Add canonical dashboard text editing
- Use canonical editing in settings
- Add /summarize as a /recap alias
- Restore previous agent when exiting dashboard
- Use tool_choice auto for compaction
- Settings toggle for snap-prompt-to-top on send
- Update default models to grok-4.5
- Source login shell once for local bash (env + alias/function snapshot)
- Template hardcoded param names in server-native tool descriptions
- Fix System-Reminder XML tag injection in CLAUDE.md via agents_md
- Fix remote workspace-server hardcoding LSP trust (repo code execution risk)
- Clear orphaned tool-call updates at turn end
- Suppress task wake after cancel
- Send x-grok-client-identifier on direct API tool calls
- Harden dashboard peek lease transitions
- Host /btw side panel in live region (minimal mode)
- Bound scroll presentation latency
- Highlight multi-line constructs correctly in diffs and the file viewer
- Block web_fetch non-public IPs; local opt-in is explicit-host only
- Seed coding_data_retention_opt_out=false for OAuth e2es in pty-harness
- Follow up clipboard delivery feedback
- Use canonical editing in pickers
- Route TextArea through canonical editor
- Persistent "watching" status row; quieter turn markers
- Gate sensitive edit targets
- Expose agent registry counts and gate session churn on them
- Default coding data sharing to opt-out until server preference applies
- Wire chat attachment ids through gateway prompts
- On auth refresh failure, issue retry
- Forward preview provenance and computer lifecycle state
- Document independent privacy controls and scope /privacy output
- Strip SamplingError Display prefix on rate-limit UI copy
- Stop dumping Cloudflare HTML into Retry failed
- Disable in-place prompt edit (scroll jank on enter)
- Strip forced ANSI color from gh pr view JSON
- Plumb bash tool description onto ToolUsageCard wire
This commit is contained in:
grokkybara[bot] 2026-07-18 19:48:28 +01:00
commit 7cfcb20d2b
292 changed files with 23315 additions and 9209 deletions

View file

@ -389,12 +389,25 @@ pub(crate) fn build_virtual_list(
items
}
/// Build a position-indexed entry map for the session picker.
///
/// Each element is `Some(item)` for selectable rows or `None` for
/// non-selectable headers. When `grouped` is true, repo-group headers
/// are interleaved so indices match what the renderer stores in hit areas.
/// `current_repo` pins the matching repo group to the top of the list.
/// Rebuild expansion keys in the backing-data index space used by session rendering.
pub(crate) fn expand_all_mapped_session_items(
state: &mut PickerState,
entry_map: &[Option<PickerItem>],
) {
state.expanded.clear();
if state.query().is_empty() {
return;
}
for item in entry_map.iter().flatten() {
let key = match item {
PickerItem::Fuzzy { original_index } => *original_index,
PickerItem::Content { hit_index } => CONTENT_EXPAND_OFFSET + hit_index,
};
state.expanded.insert(key);
}
}
/// Build the position-indexed session map, including non-selectable headers.
pub(crate) fn build_entry_map(
entries: Option<&[SessionPickerEntry]>,
content_results: Option<&[xai_grok_shell::extensions::session_search::SearchSessionHit]>,
@ -489,6 +502,77 @@ pub(crate) fn build_entry_map(
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum SessionPickerWorktreeSelection {
Fuzzy(usize),
Content { session_id: String, cwd: String },
Unavailable,
}
/// Resolve Ctrl+W before generic editing because the line editor binds it to delete-word.
pub(crate) fn session_picker_worktree_selection(
key: &crossterm::event::KeyEvent,
state: &mut PickerState,
entry_map: &[Option<PickerItem>],
non_selectable: &[bool],
entries: Option<&[SessionPickerEntry]>,
content_results: Option<&[xai_grok_shell::extensions::session_search::SearchSessionHit]>,
) -> Option<SessionPickerWorktreeSelection> {
if key.kind != crossterm::event::KeyEventKind::Press || !crate::key!('w', CONTROL).matches(key)
{
return None;
}
if entry_map.is_empty() {
return Some(SessionPickerWorktreeSelection::Unavailable);
}
crate::views::picker::clamp_picker_selection(state, entry_map.len(), non_selectable);
Some(
match entry_map
.get(state.selected)
.and_then(|entry| entry.as_ref())
{
Some(PickerItem::Fuzzy { original_index }) => entries
.and_then(|entries| entries.get(*original_index))
.filter(|entry| !crate::app::is_foreign_picker_source(&entry.source))
.map_or(SessionPickerWorktreeSelection::Unavailable, |_| {
SessionPickerWorktreeSelection::Fuzzy(*original_index)
}),
Some(PickerItem::Content { hit_index }) => content_results
.and_then(|results| results.get(*hit_index))
.map_or(SessionPickerWorktreeSelection::Unavailable, |hit| {
SessionPickerWorktreeSelection::Content {
session_id: hit.session_id.clone(),
cwd: hit.cwd.clone(),
}
}),
None => SessionPickerWorktreeSelection::Unavailable,
},
)
}
/// Rebuild backing-index expansion after a session query changes.
pub(crate) fn sync_session_picker_query_expansion(
entries: Option<&[SessionPickerEntry]>,
content_results: Option<&[xai_grok_shell::extensions::session_search::SearchSessionHit]>,
entries_query: Option<&str>,
state: &mut PickerState,
grouped: bool,
content_loading: bool,
source_filter: SourceFilter,
current_repo: Option<&str>,
) {
let entry_map = build_entry_map(
entries,
content_results,
effective_filter_query(state.query(), entries_query),
grouped,
content_loading,
source_filter,
current_repo,
);
expand_all_mapped_session_items(state, &entry_map);
}
// ---------------------------------------------------------------------------
// Session entry data building
// ---------------------------------------------------------------------------
@ -1096,6 +1180,28 @@ mod tests {
assert!(matches!(map[3], Some(PickerItem::Content { hit_index: 0 })));
}
#[test]
fn expand_all_mapped_session_items_uses_backing_indices() {
let entries = vec![make_entry("zero", "repo-a"), make_entry("needle", "repo-b")];
let hits = vec![make_content_hit("content")];
let map = build_entry_map(
Some(&entries),
Some(&hits),
"needle",
true,
false,
SourceFilter::All,
None,
);
let mut state = PickerState::default();
state.set_query("needle");
expand_all_mapped_session_items(&mut state, &map);
assert_eq!(state.expanded, HashSet::from([1, CONTENT_EXPAND_OFFSET]),);
assert!(!state.expanded.contains(&0), "group header is not an item");
}
#[test]
fn foreign_id_does_not_suppress_native_content_result() {
let mut foreign = make_entry("shared", "repo");