Synced from monorepo

Changes:
- Non-blocking coding-data sharing upsell banner
- Consolidate remediation in Doctor
- Auto mode defers fail-closed gate asks to the classifier
- Coalesce marketplace list fetches
- Allow removing a marketplace source by name
- Contain hung git marketplace sources (timeouts, non-blocking refresh, unbrick modal)
- Label failed workspace RPCs with error_kind
- Drop redundant explicit tonic/prost deps from xai-grok-shell
- Report real exit codes for completed background shells
- Narrow the date-rollover reminder to date-bearing templates
- Wire toolOverrides through the session and agent
- Security: Bash(git:*) allowlist matches whole command chain by prefix
- Split prompt-trigger telemetry and record classifier provenance
- Raise connectors-manager timeout to 60s
- Auto classifier honors recorded approvals for repeat actions
- Apply doctor fixes in the TUI
- Auto-mode classifier timeouts prompt instead of silently denying
- Scope subagent completion drains to the owning session
- Add the toolOverrides wire types
- Set client_identifier=grok-agent-sdk
- Accept both spellings of the workspace-teleport kill switch
- Persist one-shot occurrence journal
- Stop turns that poll the exact same tool call 16x in a row
- Copy compaction checkpoint files when forking sessions
- Auto-focus permission prompt from scrollback
- Esc cancels the running turn in non-vim and minimal modes
- List Ctrl+Z undo and redo in keyboard shortcuts
- Out-of-process macOS mic capture
- Show active auth mode on session-info
- Install the npm binary under $GROK_HOME
- Remove hover/click dead zones between dashboard items
- Route startup warnings to doctor
- Document [feedback.user] author identity config
- Extend bang command timeout
- Close combine-queued edit-hold race
- Integrate relocation recovery
- Expose privacy notice rollout flag
- Break harness discovery ref cycle so connections can idle-evict
- Shift/Alt+Enter inserts newline when editing a queued prompt
- Gate project Claude permissions on folder trust
- Echo response.create.event_id on response.created
- Toast when session creation fails from disk full
- Add shared test process lifecycle
- Enable dynamic workflows by default
- Add relocation transaction state machine
- Add shared test sandbox
- Surface auth failures on model-switch compact
- Persist durable scheduler expiry
- Confirm before removing extensions-modal items
- Re-run compact and prompt after login when compact hit expired auth
- Recap sends hosted tools under backend search
This commit is contained in:
grokkybara[bot] 2026-07-22 19:18:53 +01:00
commit a5727c5960
482 changed files with 37627 additions and 13402 deletions

View file

@ -42,11 +42,10 @@ async fn minimal_commits_thinking_body_to_scrollback() {
)
.expect("write config");
let env = content.env_for_pager();
let env_refs: Vec<(&str, &str)> = env.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
let binary = pager_binary().expect("resolve pager binary");
let mut harness = PtyHarness::new(&binary, DEFAULT_ROWS, DEFAULT_COLS, MINIMAL_ARGS, &env_refs)
.expect("spawn minimal pager");
let mut harness =
PtyHarness::spawn_with_content(&binary, DEFAULT_ROWS, DEFAULT_COLS, &content, MINIMAL_ARGS)
.expect("spawn minimal pager");
harness.set_respond_to_queries(true);
wait_minimal_ready(&mut harness);

View file

@ -27,10 +27,12 @@ async fn minimal_ctrl_c_arms_and_quits() {
// Second Ctrl+C within the confirm window exits the process.
harness.inject_keys(b"\x03").expect("inject Ctrl+C again");
let code = harness.wait_exit_code(Duration::from_secs(5));
let exit = harness
.wait_exit_code(Duration::from_secs(5))
.expect("wait for minimal pager exit");
assert!(
code.is_some(),
"second Ctrl+C should quit minimal\nscreen:\n{}",
matches!(exit, PtyExitPoll::Exited(_) | PtyExitPoll::PendingStatus),
"second Ctrl+C should quit minimal, got {exit:?}\nscreen:\n{}",
harness.screen_contents()
);
}

View file

@ -20,14 +20,24 @@ async fn minimal_ctrl_o_send_now_queued_apple_terminal() {
);
let binary = pager_binary().expect("resolve pager binary");
let mut env = content.env_for_pager();
env.push(("TERM_PROGRAM".into(), "Apple_Terminal".into()));
let mut overrides: Vec<(String, String)> =
vec![("TERM_PROGRAM".into(), "Apple_Terminal".into())];
// Non-interactive $PAGER so a mistaken transcript open fails fast rather
// than hanging in `less` if the predicate regresses.
env.push(("PAGER".into(), "cat".into()));
let env_refs: Vec<(&str, &str)> = env.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
let mut harness = PtyHarness::new(&binary, DEFAULT_ROWS, DEFAULT_COLS, MINIMAL_ARGS, &env_refs)
.expect("spawn minimal + Apple_Terminal");
overrides.push(("PAGER".into(), "cat".into()));
let env_refs: Vec<(&str, &str)> = overrides
.iter()
.map(|(key, value)| (key.as_str(), value.as_str()))
.collect();
let mut harness = PtyHarness::spawn_with_content_env(
&binary,
DEFAULT_ROWS,
DEFAULT_COLS,
&content,
MINIMAL_ARGS,
&env_refs,
)
.expect("spawn minimal + Apple_Terminal");
harness.set_respond_to_queries(true);
wait_minimal_ready(&mut harness);

View file

@ -5,10 +5,10 @@ use crate::common::*;
/// Minimal mode guards the documented `in_flight_committed` dogfood
/// double-show: a promoted queued prompt's " " block commits (prints) into
/// native scrollback immediately, so cancelling its turn pre-first-token
/// (minimal's cancel gesture is Ctrl+C; Esc is swallowed) must SKIP the
/// composer rewind — a rewind would leave the printed block on screen AND
/// refill the composer, showing the prompt twice. Standard cancel instead:
/// the block renders exactly once and the cancel marker is visible.
/// (via Ctrl+C here) must SKIP the composer rewind — a rewind would leave the
/// printed block on screen AND refill the composer, showing the prompt twice.
/// Standard cancel instead: the block renders exactly once and the cancel
/// marker is visible.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[ignore]
async fn minimal_double_esc_committed_queued_prompt_single_render() {

View file

@ -2,11 +2,13 @@
#[allow(unused_imports)]
use crate::common::*;
/// Mid-turn Esc in minimal mode is a swallowed no-op (the prompt is always
/// focused). Esc must NOT cancel; cancel remains on Ctrl+C.
/// Esc cancels a running turn in minimal mode (the prompt is always focused, so
/// the turn-running Esc branch wins; minimal enables the Esc-cancel gate
/// regardless of vim mode). The cancellation marker is finalized and committed
/// to native scrollback like any other block.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[ignore]
async fn minimal_esc_mid_turn_is_swallowed() {
async fn minimal_esc_cancels_running_turn() {
let content = ContentController::start().await.expect("start content");
// Paced, long stream so the turn is provably still running when Esc lands.
let long = format!(
@ -26,27 +28,13 @@ async fn minimal_esc_mid_turn_is_swallowed() {
.wait_for_text(MOCK_RESPONSE_SENTINEL, Duration::from_secs(30))
.expect("turn streaming in the live tail");
harness.inject_keys(keys::ESC).expect("press esc");
harness.update(Duration::from_millis(1000));
harness.inject_keys(keys::ESC).expect("press esc to cancel");
// Full-text: minimal commits the cancel marker to native scrollback, so it
// may sit above the pinned viewport — check scrollback + screen.
assert!(
!harness.contains_full_text("Turn cancelled by user"),
"mid-turn Esc must NOT cancel in minimal mode\nfull contents:\n{}",
harness.full_text()
);
// Positive tail: prove the turn was still alive at Esc-time (the negative
// check above would false-pass on an already-finished turn) and that
// Ctrl+C — the replacement cancel gesture — works in minimal mode. The
// prompt is empty and the turn is running, so Ctrl+C cancels (the minimal
// quit arm applies only to an idle empty prompt).
harness.inject_keys(keys::CTRL_C).expect("press ctrl+c");
harness
.wait_for_full_text("Turn cancelled by user", Duration::from_secs(15))
.expect("Ctrl+C must cancel the still-running turn in minimal mode");
.expect("cancellation marker committed to scrollback");
assert!(
!harness.contains_text("panicked"),
"pager panicked\nscreen:\n{}",

View file

@ -36,12 +36,16 @@ async fn minimal_external_editor_round_trip() {
format!("'{}'", script.display())
};
let mut env = content.env_for_pager();
env.push(("VISUAL".to_owned(), editor));
let env_refs: Vec<(&str, &str)> = env.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
let binary = pager_binary().expect("resolve pager binary");
let mut harness = PtyHarness::new(&binary, DEFAULT_ROWS, DEFAULT_COLS, MINIMAL_ARGS, &env_refs)
.expect("spawn minimal pager");
let mut harness = PtyHarness::spawn_with_content_env_ops(
&binary,
DEFAULT_ROWS,
DEFAULT_COLS,
&content,
MINIMAL_ARGS,
&[EnvOp::set("VISUAL", &editor)],
)
.expect("spawn minimal pager");
harness.set_respond_to_queries(true);
wait_minimal_ready(&mut harness);

View file

@ -41,7 +41,7 @@ async fn minimal_resize_preserves_committed_scrollback() {
harness.update(Duration::from_millis(800));
assert!(
harness.is_running(),
harness.is_running().expect("poll pager liveness"),
"pager exited during resize\nscreen:\n{}",
harness.screen_contents()
);

View file

@ -17,12 +17,21 @@ async fn minimal_transcript_opens_in_pager() {
// Minimal env + PAGER=cat (non-interactive). Response forwarding on so the
// inline-viewport cursor probe completes (see spawn_minimal).
let mut env = content.env_for_pager();
env.push(("PAGER".to_string(), "cat".to_string()));
let env_refs: Vec<(&str, &str)> = env.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
let overrides: Vec<(String, String)> = vec![("PAGER".to_string(), "cat".to_string())];
let env_refs: Vec<(&str, &str)> = overrides
.iter()
.map(|(key, value)| (key.as_str(), value.as_str()))
.collect();
let binary = pager_binary().expect("resolve pager binary");
let mut harness = PtyHarness::new(&binary, DEFAULT_ROWS, DEFAULT_COLS, MINIMAL_ARGS, &env_refs)
.expect("spawn minimal pager");
let mut harness = PtyHarness::spawn_with_content_env(
&binary,
DEFAULT_ROWS,
DEFAULT_COLS,
&content,
MINIMAL_ARGS,
&env_refs,
)
.expect("spawn minimal pager");
harness.set_respond_to_queries(true);
wait_minimal_ready(&mut harness);

View file

@ -43,16 +43,25 @@ async fn minimal_transcript_pager_restore_no_artifacts() {
let content = ContentController::start().await.expect("start content");
content.set_response(format!("{MOCK_RESPONSE_SENTINEL} transcript body."));
let mut env = content.env_for_pager();
env.push(("PAGER".to_string(), "less".to_string()));
env.push((
let mut overrides: Vec<(String, String)> = vec![("PAGER".to_string(), "less".to_string())];
overrides.push((
"GROK_TEST_FRAME_WRITE_DELAY_MS".to_string(),
FRAME_DELAY_MS.to_string(),
));
let env_refs: Vec<(&str, &str)> = env.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
let env_refs: Vec<(&str, &str)> = overrides
.iter()
.map(|(key, value)| (key.as_str(), value.as_str()))
.collect();
let binary = pager_binary().expect("resolve pager binary");
let mut harness = PtyHarness::new(&binary, DEFAULT_ROWS, DEFAULT_COLS, MINIMAL_ARGS, &env_refs)
.expect("spawn minimal pager");
let mut harness = PtyHarness::spawn_with_content_env(
&binary,
DEFAULT_ROWS,
DEFAULT_COLS,
&content,
MINIMAL_ARGS,
&env_refs,
)
.expect("spawn minimal pager");
harness.set_respond_to_queries(true);
wait_minimal_ready(&mut harness);

View file

@ -14,7 +14,7 @@ mod minimal_committed_content_survives_overlay_grow;
mod minimal_continue_reprints_transcript;
mod minimal_ctrl_c_arms_and_quits;
mod minimal_double_esc_committed_queued_prompt_single_render;
mod minimal_esc_mid_turn_is_swallowed;
mod minimal_esc_cancels_running_turn;
mod minimal_external_editor_round_trip;
mod minimal_flush_left_no_hpad;
mod minimal_help_opens_command_palette;