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

@ -1,6 +1,6 @@
[package]
name = "xai-grok-pager-bin"
version = "0.2.109"
version = "0.2.110"
edition.workspace = true
license = "Apache-2.0"
authors = ["xAI"]

View file

@ -338,13 +338,15 @@ fn print_leader_descriptor(d: &LeaderDescriptor) {
eprintln!(" PID {pid} ({state}) -- {sock}");
}
fn leader_descriptor_json(d: &LeaderDescriptor) -> serde_json::Value {
serde_json::json!(
{ "pid" : leader_pid(d), "pidFromLock" : d.pid_from_lock, "pidLive" : d.live_info
.as_ref().map(| li | li.pid), "classification" : format!("{:?}", d
.classification), "socketPath" : d.socket_path.as_deref().map(| p | p.display()
.to_string()), "lockPath" : d.lock_path.as_deref().map(| p | p.display()
.to_string()), "wsUrlSuffix" : d.ws_url_suffix, }
)
serde_json::json!({
"pid": leader_pid(d),
"pidFromLock": d.pid_from_lock,
"pidLive": d.live_info.as_ref().map(|li| li.pid),
"classification": format!("{:?}", d.classification),
"socketPath": d.socket_path.as_deref().map(|p| p.display().to_string()),
"lockPath": d.lock_path.as_deref().map(|p| p.display().to_string()),
"wsUrlSuffix": d.ws_url_suffix,
})
}
fn leader_info_json(
d: &LeaderDescriptor,
@ -588,10 +590,15 @@ fn render_workspace_payload(payload: &ControlPayload, json: bool) {
return;
};
if json {
let value = serde_json::json!(
{ "state" : state, "hubUrl" : hub_url, "cwd" : cwd, "uptimeMs" : uptime_ms,
"activeToolCalls" : active_tool_calls, "sessions" : sessions, "pid" : pid, }
);
let value = serde_json::json!({
"state": state,
"hubUrl": hub_url,
"cwd": cwd,
"uptimeMs": uptime_ms,
"activeToolCalls": active_tool_calls,
"sessions": sessions,
"pid": pid,
});
println!("{}", serde_json::to_string(&value).unwrap_or_default());
return;
}
@ -858,17 +865,22 @@ fn replay_load_json(sid: &str, cached: &CachedSession) -> Option<String> {
return Some(verbatim.clone());
}
let cwd = cached.cwd.as_deref()?;
let mut params = serde_json::json!({ "sessionId" : sid, "cwd" : cwd, });
let mut params = serde_json::json!({
"sessionId": sid,
"cwd": cwd,
});
if let Some(ref mcp_raw) = cached.mcp_servers_json
&& let Ok(mcp_val) = serde_json::from_str::<serde_json::Value>(mcp_raw)
{
params["mcpServers"] = mcp_val;
}
Some(
serde_json::json!(
{ "jsonrpc" : "2.0", "id" : REPLAY_LOAD_REQUEST_ID, "method" :
"session/load", "params" : params, }
)
serde_json::json!({
"jsonrpc": "2.0",
"id": REPLAY_LOAD_REQUEST_ID,
"method": "session/load",
"params": params,
})
.to_string(),
)
}
@ -908,24 +920,23 @@ async fn replay_acp_state_after_reconnect(
let mut restored: Vec<String> = Vec::new();
for (sid, cached) in &state.sessions {
let Some(load_json) = replay_load_json(sid, cached) else {
tracing::warn!(
session_id = % sid, "replay: no way to rebuild session/load; skipping"
);
tracing::warn!(session_id = %sid, "replay: no way to rebuild session/load; skipping");
continue;
};
match replay_request_until_response(tx, rx, stdout, &load_json, "session/load").await {
ReplayOutcome::ResponseOk => {
tracing::info!(session_id = % sid, "replay: session restored");
tracing::info!(session_id = %sid, "replay: session restored");
restored.push(sid.clone());
}
ReplayOutcome::ResponseErr => {
tracing::warn!(
session_id = % sid, "replay: session/load was rejected by new leader"
session_id = %sid,
"replay: session/load was rejected by new leader"
);
}
ReplayOutcome::Failed => {
tracing::warn!(
session_id = % sid,
session_id = %sid,
"replay: transport failure during session/load; aborting remaining replays"
);
break;
@ -1022,9 +1033,7 @@ async fn run_agent_command(
match std::env::current_dir() {
Ok(cwd) => xai_grok_shell::agent::folder_trust::grant_folder_trust(&cwd),
Err(e) => {
tracing::warn!(
error = % e, "--trust: failed to resolve cwd; folder not trusted"
)
tracing::warn!(error = %e, "--trust: failed to resolve cwd; folder not trusted")
}
}
}
@ -1191,11 +1200,18 @@ async fn run_agent_command(
let mut stdin_lines = xai_acp_lib::spawn_stdin_line_reader();
loop {
tokio::select! {
biased; _ = cancel_stdin.cancelled() => break, maybe_line =
stdin_lines.recv() => { let Some(line) = maybe_line else {
break }; forward_stdio_line_to_leader(line, &
leader_tx_stdin, & replay_state_stdin, & cancel_stdin,).
await; }
biased;
_ = cancel_stdin.cancelled() => break,
maybe_line = stdin_lines.recv() => {
let Some(line) = maybe_line else { break };
forward_stdio_line_to_leader(
line,
&leader_tx_stdin,
&replay_state_stdin,
&cancel_stdin,
)
.await;
}
}
}
});
@ -1245,7 +1261,7 @@ async fn run_agent_command(
reconnector.notify_connected();
let params = match replayed_session_id {
Some(ref sid) => {
serde_json::json!({ "sessionId" : sid }).to_string()
serde_json::json!({ "sessionId": sid }).to_string()
}
None => "{}".to_string(),
};
@ -1258,7 +1274,7 @@ async fn run_agent_command(
continue;
}
Err(e) => {
tracing::error!(error = % e, "Failed to reconnect (stdio)");
tracing::error!(error = %e, "Failed to reconnect (stdio)");
cancel_stdout.cancel();
break;
}
@ -1268,7 +1284,8 @@ async fn run_agent_command(
}
});
tokio::select! {
_ = stdin_task => {} _ = stdout_task => {}
_ = stdin_task => {}
_ = stdout_task => {}
}
return Ok(());
}
@ -1293,9 +1310,7 @@ async fn run_agent_command(
continue;
}
Err(e) => {
tracing::error!(
error = % e, "Failed to reconnect (headless)"
);
tracing::error!(error = %e, "Failed to reconnect (headless)");
break;
}
}
@ -1640,6 +1655,9 @@ fn main() {
if let Some(code) = xai_grok_pager::app::mermaid_worker::maybe_run_render_subprocess() {
std::process::exit(code);
}
if let Some(code) = xai_grok_pager::voice::maybe_run_capture_subprocess() {
std::process::exit(code);
}
let args = PagerArgs::parse_cli();
if dispatch_version_if_requested(&args) || dispatch_doctor_if_requested(&args) {
return;
@ -1785,10 +1803,10 @@ async fn async_main(args: PagerArgs) -> Result<()> {
match command {
Command::Version { json } => {
if json {
let payload = serde_json::json!(
{ "currentVersion" : env!("VERSION_WITH_COMMIT"), "channel" :
xai_grok_update::channel_name().unwrap_or("unknown"), }
);
let payload = serde_json::json!({
"currentVersion": env!("VERSION_WITH_COMMIT"),
"channel": xai_grok_update::channel_name().unwrap_or("unknown"),
});
println!("{}", serde_json::to_string(&payload)?);
} else {
write_version(
@ -2263,9 +2281,7 @@ async fn signal_leaders_to_relaunch(installed_version: &str) {
{
Ok(c) => c,
Err(e) => {
tracing::debug!(
error = % e, "Could not connect to leader to signal relaunch"
);
tracing::debug!(error = %e, "Could not connect to leader to signal relaunch");
continue;
}
};
@ -2287,17 +2303,14 @@ async fn signal_leaders_to_relaunch(installed_version: &str) {
eprintln!(" ↻ Relaunching shared session (leader {from_version}{to_version})…");
}
Ok(Ok(xai_grok_shell::leader::ControlPayload::RelaunchDeclined { reason })) => {
tracing::debug!(% reason, "Leader declined relaunch");
tracing::debug!(%reason, "Leader declined relaunch");
}
Ok(Ok(_)) => {}
Ok(Err(e)) => {
tracing::debug!(error = % e.message, "Leader relaunch control error");
tracing::debug!(error = %e.message, "Leader relaunch control error");
}
Err(e) => {
tracing::debug!(
error = % e,
"Leader relaunch ack not received (leader may be exiting)"
);
tracing::debug!(error = %e, "Leader relaunch ack not received (leader may be exiting)");
}
}
client.cancel();
@ -2818,10 +2831,11 @@ mod tests {
assert_eq!(load2_json["id"].as_str(), Some(REPLAY_LOAD_REQUEST_ID));
response_tx
.send(
serde_json::json!(
{ "jsonrpc" : "2.0", "id" : REPLAY_LOAD_REQUEST_ID, "result" : {}
}
)
serde_json::json!({
"jsonrpc": "2.0",
"id": REPLAY_LOAD_REQUEST_ID,
"result": {}
})
.to_string(),
)
.unwrap();
@ -2967,8 +2981,8 @@ mod tests {
response_tx
.send(
format!(
r#"{{"jsonrpc":"2.0","method":"session/update","params":{{"sessionId":"s9","n":{i}}}}}"#
),
r#"{{"jsonrpc":"2.0","method":"session/update","params":{{"sessionId":"s9","n":{i}}}}}"#
),
)
.unwrap();
}
@ -3071,10 +3085,11 @@ mod tests {
);
response_tx
.send(
serde_json::json!(
{ "jsonrpc" : "2.0", "id" : REPLAY_LOAD_REQUEST_ID, "result" : {}
}
)
serde_json::json!({
"jsonrpc": "2.0",
"id": REPLAY_LOAD_REQUEST_ID,
"result": {}
})
.to_string(),
)
.unwrap();