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

@ -13,10 +13,11 @@
//! Run with:
//! `cargo test -p xai-grok-pager --test pty_auto_mode -- --ignored --nocapture`
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::time::Duration;
use xai_grok_pager_pty_harness::{PtyHarness, pager_binary};
use xai_grok_test_support::TestSandbox;
const ROWS: u16 = 40;
const COLS: u16 = 120;
@ -48,9 +49,13 @@ fn dirs_next_home() -> Option<PathBuf> {
/// Sandbox HOME + optional auth.json seed (no secrets logged), with the
/// auto-permission-mode feature gate pinned explicitly via `gate_on` so each
/// test is self-contained and deterministic regardless of the runner's shell.
fn prepare_sandbox(home: &Path, gate_on: bool) -> Vec<(String, String)> {
let grok = home.join(".grok");
let _ = std::fs::create_dir_all(&grok);
fn prepare_sandbox(sandbox: &mut TestSandbox, gate_on: bool) -> Vec<(String, String)> {
// Remove rather than empty the fake API key so seeded OIDC remains authoritative.
sandbox.remove_env("XAI_API_KEY");
let home = sandbox.home();
let grok = sandbox.grok_home();
let _ = std::fs::create_dir_all(grok);
if let Some(src) = auth_json_source() {
let dest = grok.join("auth.json");
if let Err(e) = std::fs::copy(&src, &dest) {
@ -68,8 +73,6 @@ fn prepare_sandbox(home: &Path, gate_on: bool) -> Vec<(String, String)> {
let home_s = home.display().to_string();
let mut env = vec![
("HOME".into(), home_s.clone()),
("GROK_HOME".into(), grok.display().to_string()),
("XDG_CONFIG_HOME".into(), format!("{home_s}/.config")),
("XDG_DATA_HOME".into(), format!("{home_s}/.local/share")),
("XDG_CACHE_HOME".into(), format!("{home_s}/.cache")),
@ -78,7 +81,6 @@ fn prepare_sandbox(home: &Path, gate_on: bool) -> Vec<(String, String)> {
("NO_COLOR".into(), "0".into()),
("TERM_PROGRAM".into(), "".into()),
("TMUX".into(), "".into()),
// Do not set XAI_API_KEY — prefer OIDC entry in auth.json (pty_e2e pattern).
];
// Pin the feature gate explicitly so the cycle is deterministic regardless
// of the developer's shell. `GROK_AUTO_PERMISSION_MODE` is the highest gate
@ -115,15 +117,16 @@ fn pty_shift_tab_cycles_to_auto_mode_banner() {
Ok(b) => b,
Err(e) => panic!("resolve pager binary via harness env: {e:#}"),
};
let tmp = tempfile::tempdir().expect("temp HOME");
let env_owned = prepare_sandbox(tmp.path(), true);
let mut sandbox = TestSandbox::new();
let env_owned = prepare_sandbox(&mut sandbox, true);
let env_refs: Vec<(&str, &str)> = env_owned
.iter()
.map(|(k, v)| (k.as_str(), v.as_str()))
.collect();
let mut harness = PtyHarness::new(&binary, ROWS, COLS, &[], &env_refs)
.expect("spawn pager in PTY (xai-grok-pager-pty-harness)");
let mut harness =
PtyHarness::new_in_sandbox(&binary, ROWS, COLS, &[], &sandbox, &env_refs, None)
.expect("spawn pager in PTY (xai-grok-pager-pty-harness)");
// Drain startup; welcome or agent chrome.
let _ = harness.wait_for_text(WELCOME_SCREEN_SENTINEL, WELCOME_TIMEOUT);
@ -145,8 +148,9 @@ fn pty_shift_tab_cycles_to_auto_mode_banner() {
see the permission_auto_mode SessionActor wire tests for coverage."
);
// Still prove we exercised PtyHarness spawn (not a no-op).
let running = harness.is_running().expect("poll pager liveness");
assert!(
harness.is_running() || !early.is_empty(),
running || !early.is_empty(),
"pager must have produced output even on login screen"
);
let _ = harness.inject_keys(b"\x11"); // ctrl+q if bound
@ -199,15 +203,16 @@ fn pty_shift_tab_skips_auto_when_gate_off() {
Ok(b) => b,
Err(e) => panic!("resolve pager binary via harness env: {e:#}"),
};
let tmp = tempfile::tempdir().expect("temp HOME");
let env_owned = prepare_sandbox(tmp.path(), false);
let mut sandbox = TestSandbox::new();
let env_owned = prepare_sandbox(&mut sandbox, false);
let env_refs: Vec<(&str, &str)> = env_owned
.iter()
.map(|(k, v)| (k.as_str(), v.as_str()))
.collect();
let mut harness = PtyHarness::new(&binary, ROWS, COLS, &[], &env_refs)
.expect("spawn pager in PTY (xai-grok-pager-pty-harness)");
let mut harness =
PtyHarness::new_in_sandbox(&binary, ROWS, COLS, &[], &sandbox, &env_refs, None)
.expect("spawn pager in PTY (xai-grok-pager-pty-harness)");
let _ = harness.wait_for_text(WELCOME_SCREEN_SENTINEL, WELCOME_TIMEOUT);
let early = harness.screen_contents();
@ -219,8 +224,9 @@ fn pty_shift_tab_skips_auto_when_gate_off() {
eprintln!(
"pty_auto_mode(gate off): login/device-auth screen blocked cycle; env auth limit"
);
let running = harness.is_running().expect("poll pager liveness");
assert!(
harness.is_running() || !early.is_empty(),
running || !early.is_empty(),
"pager must have produced output even on login screen"
);
let _ = harness.inject_keys(b"\x11");