Synced from monorepo

Synced from monorepo

Changes:
- Temporarily disable session share link creation in the TUI
- Do not approve plan on empty Enter from the revise prompt
- Expose chat product Skills via ACP available_commands_update
- Return immediately from a blocking wait on an already-completed ACP task
- Split headless pager module for clearer structure
- Stop git worktree prune from removing user registrations on resume
- Use compaction sampler tokenizer for item token counts
- Opt-in extra root CAs via GROK_EXTRA_CA_BUNDLE
- Cancel all session subagents when the user stops
- Let the session persistence actor exit when its session ends
- Make fullscreen terminal resize much cheaper on long sessions
- Report honestly from kill_task when an ACP task does not exist
- Hide /usage for external-auth deployments
- Forward the history-load trailer’s computer_reason to the client
- Remove ineffective no-op tool reminder
- Declare slash-command screen-mode support in one place
- Keep settings enum picker on the committed value until Enter
- Reap a PTY’s full process tree
- Stream tool calls from headless mode over ACP
- Bridge gateway task lifecycle to ACP for chat session background tasks
- Don’t warn about truncated history on a suppressed replay
- Fit full-replace summarizer input and recover on context-length errors
- Stop dropping agents over an unrecognized frontmatter color
- Add /undo as a slash alias for /rewind
- Harden sleep/wake token-refresh paths against forced re-login
- Add session/list ACP method
- Give each sampling backend its own conversion module
- Treat an unenrolled child process as a lint error
- Suppress the cancelled marker on send-now wake turns
- Stop tearing down Roslyn on every edit, and read C# diagnostics

Source-Revision: 2a28b4a86cfc4a4c133c35b7fc2a6a9964387c39
This commit is contained in:
grokkybara[bot] 2026-07-30 19:07:40 +00:00
commit dd04f397b1
367 changed files with 29489 additions and 10051 deletions

View file

@ -0,0 +1,20 @@
[package]
license = "Apache-2.0"
name = "xai-grok-extra-ca"
version = "0.1.0"
edition.workspace = true
description = "Opt-in extra TLS roots from GROK_EXTRA_CA_BUNDLE (validated DER cache + reqwest 0.12 adapters)"
[dependencies]
reqwest = { workspace = true }
# Explicit pin (not `workspace = true`): the workspace rustls pin enables
# aws-lc-rs. This crate only needs RootCertStore + PEM parse, so stay
# default-features = false with `std` (enables pki-types/std for PEM).
rustls = { version = "0.23", default-features = false, features = ["std"] }
tracing = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
[lints]
workspace = true

View file

@ -0,0 +1,184 @@
//! Opt-in extra TLS roots via `GROK_EXTRA_CA_BUNDLE` (PEM path).
//!
//! Default-off (unset/empty env → no I/O); parsed once into a process
//! `OnceLock`; additive to webpki roots. Each DER is validated with
//! `rustls::RootCertStore::add` before caching so a bad bundle cannot fail
//! `ClientBuilder::build()`. Unreadable/oversized/empty/unparsable → warn and
//! continue. Size cap: [`MAX_EXTRA_CA_BUNDLE_BYTES`].
//!
//! Source of truth is validated DER ([`extra_root_ders`]) so reqwest 0.12
//! (this crate's adapters) and MCP's 0.13 can each build their own
//! `Certificate`s.
use std::io::Read;
use std::sync::OnceLock;
use rustls::RootCertStore;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::pem::PemObject;
/// Hard cap on `GROK_EXTRA_CA_BUNDLE` (1 MiB) — avoids unbounded startup reads.
pub const MAX_EXTRA_CA_BUNDLE_BYTES: u64 = 1024 * 1024;
/// Env var name for the opt-in extra CA bundle (PEM path).
pub const ENV_GROK_EXTRA_CA_BUNDLE: &str = "GROK_EXTRA_CA_BUNDLE";
/// Process-wide extra roots as validated DER, parsed once.
///
/// Empty when the env var is unset/empty or the file yields no usable certs.
pub fn extra_root_ders() -> &'static [Vec<u8>] {
static DERS: OnceLock<Vec<Vec<u8>>> = OnceLock::new();
DERS.get_or_init(load_extra_root_ders).as_slice()
}
/// Apply [`extra_root_ders`] to a workspace (reqwest 0.12) async `ClientBuilder`.
pub fn with_extra_root_certificates(mut builder: reqwest::ClientBuilder) -> reqwest::ClientBuilder {
for der in extra_root_ders() {
match reqwest::Certificate::from_der(der) {
Ok(cert) => builder = builder.add_root_certificate(cert),
// WHY: rustls already accepted this DER; skip rather than poison build.
Err(e) => tracing::warn!(
error = %e,
"GROK_EXTRA_CA_BUNDLE: validated DER rejected by reqwest; skipping cert"
),
}
}
builder
}
/// Apply [`extra_root_ders`] to a workspace (reqwest 0.12) blocking `ClientBuilder`.
pub fn with_extra_root_certificates_blocking(
mut builder: reqwest::blocking::ClientBuilder,
) -> reqwest::blocking::ClientBuilder {
for der in extra_root_ders() {
match reqwest::Certificate::from_der(der) {
Ok(cert) => builder = builder.add_root_certificate(cert),
// WHY: rustls already accepted this DER; skip rather than poison build.
Err(e) => tracing::warn!(
error = %e,
"GROK_EXTRA_CA_BUNDLE: validated DER rejected by reqwest; skipping cert"
),
}
}
builder
}
fn load_extra_root_ders() -> Vec<Vec<u8>> {
let path = match std::env::var_os(ENV_GROK_EXTRA_CA_BUNDLE) {
Some(p) if !p.is_empty() => std::path::PathBuf::from(p),
_ => return Vec::new(),
};
let bytes = match read_bundle_capped(&path) {
Ok(b) => b,
Err(BundleReadError::Io(e)) => {
// WHY: MITM CA is optional; a missing path must not brick HTTP.
tracing::warn!(
path = %path.display(),
error = %e,
"GROK_EXTRA_CA_BUNDLE unreadable; continuing without extra roots"
);
return Vec::new();
}
Err(BundleReadError::TooLarge) => {
tracing::warn!(
path = %path.display(),
max_bytes = MAX_EXTRA_CA_BUNDLE_BYTES,
"GROK_EXTRA_CA_BUNDLE exceeds size cap; continuing without extra roots"
);
return Vec::new();
}
};
let outcome = parse_and_validate_pem(&bytes);
if outcome.no_pem_blocks {
tracing::warn!(
path = %path.display(),
"GROK_EXTRA_CA_BUNDLE contains no PEM certificate blocks; continuing without extra roots"
);
return outcome.accepted;
}
if outcome.rejected > 0 {
tracing::warn!(
path = %path.display(),
accepted = outcome.accepted.len(),
rejected = outcome.rejected,
"GROK_EXTRA_CA_BUNDLE: dropped unusable certificate block(s)"
);
}
if outcome.accepted.is_empty() {
tracing::warn!(
path = %path.display(),
"GROK_EXTRA_CA_BUNDLE produced zero usable certificates; continuing without extra roots"
);
} else {
tracing::info!(
path = %path.display(),
accepted = outcome.accepted.len(),
"GROK_EXTRA_CA_BUNDLE: loaded extra root certificate(s)"
);
}
outcome.accepted
}
#[derive(Debug)]
enum BundleReadError {
Io(std::io::Error),
TooLarge,
}
fn read_bundle_capped(path: &std::path::Path) -> Result<Vec<u8>, BundleReadError> {
let file = std::fs::File::open(path).map_err(BundleReadError::Io)?;
let mut buf = Vec::new();
let n = file
.take(MAX_EXTRA_CA_BUNDLE_BYTES + 1)
.read_to_end(&mut buf)
.map_err(BundleReadError::Io)?;
if (n as u64) > MAX_EXTRA_CA_BUNDLE_BYTES {
return Err(BundleReadError::TooLarge);
}
Ok(buf)
}
/// Result of parsing a PEM bundle into rustls-validated DER roots.
#[derive(Debug, Default)]
pub(crate) struct ParseOutcome {
pub(crate) accepted: Vec<Vec<u8>>,
/// PEM blocks that failed decode or rustls X.509 validation.
pub(crate) rejected: usize,
/// Input (non-empty) contained no PEM certificate blocks at all.
pub(crate) no_pem_blocks: bool,
}
/// Parse PEM into rustls-validated DER (no env / OnceLock). Input with no PEM
/// certificate blocks (including empty) → empty accepted, zero rejected,
/// `no_pem_blocks` set.
pub(crate) fn parse_and_validate_pem(pem: &[u8]) -> ParseOutcome {
let mut accepted = Vec::new();
let mut rejected = 0usize;
let mut saw_block = false;
// WHY: reject non-X.509 DER before any ClientBuilder sees it; `add`
// validates per certificate, so one store serves the whole bundle.
let mut store = RootCertStore::empty();
for item in CertificateDer::pem_slice_iter(pem) {
saw_block = true;
match item {
Ok(der) => match store.add(der.clone()) {
Ok(()) => accepted.push(der.as_ref().to_vec()),
Err(_) => rejected += 1,
},
Err(_) => rejected += 1,
}
}
ParseOutcome {
accepted,
rejected,
no_pem_blocks: !saw_block,
}
}
#[cfg(test)]
#[path = "lib_tests.rs"]
mod tests;

View file

@ -0,0 +1,130 @@
use super::*;
// Self-signed PEMs for unit tests only (CN=test-extra-ca-1 / -2).
const VALID_CERT_1: &str = "-----BEGIN CERTIFICATE-----\n\
MIIDFTCCAf2gAwIBAgIUT2czXTuxSAjDjEh92UMB1OVahZYwDQYJKoZIhvcNAQEL\n\
BQAwGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMB4XDTI2MDcyOTE4MzUwNFoX\n\
DTM2MDcyNjE4MzUwNFowGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMIIBIjAN\n\
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1gNk2BQwUy+n5cCaTFtGpSzVQv//\n\
d7QD+3QWeE411wIGJzp3nrd7np55X8JHxeg/pRhspQvLQAF7bt55LSkL/+sSth3S\n\
QTbBqhftic9CXik3llAwbdQkAM9srz5zXWW9KVjZ57dxjjxrS15SCXu/UmvGZy98\n\
faJcS++TRkczsNFzwQEqeDYARVc/no0C0I++NhGLPaNMfFAevvnu6Kt3CYMI5ls4\n\
KCFgnlau4CjgRCMSfRDCRcwEwUAp+DyX9IU+tvDAQY1ncVoa/05tvaEvw7pQ+UgW\n\
0wRG0lk7PLlcWmUkLcFpO+sL5GRkC8RoWM4cFbIOiXoVxUFks/z2y0GCEQIDAQAB\n\
o1MwUTAdBgNVHQ4EFgQU+lyC70W5aR6BIf4VNtjfiWMNzzkwHwYDVR0jBBgwFoAU\n\
+lyC70W5aR6BIf4VNtjfiWMNzzkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B\n\
AQsFAAOCAQEA02972nA7LshRgubz6BwXbh1gA5pLzTd5KEae+94Hq6mP2zJ1T0gk\n\
x+me0NtSgG4BJLdBIylUzo2UmsfB/sz+ght6WX1uB38Vc2UQsp0sRPeeiMovSd6n\n\
I7xZyuZEF3noYJVBBlKQ8XsCUIBNIROlyKlNjNcWY8tGqPh9cepvtZYkBgRZr1vW\n\
hJAE3EOL2ZddrMPF64QeU9UhvCm0Ch+Ceqa1ZWE0MygccggX5s2yQwtXO2ovJdjH\n\
6vW0I02r8sE+NX0d1u8rIPJEKlp89UwCwniD7SxHTNw8bbsTCWz+AMod7vC7De3X\n\
4Daxme+vD8adOfCeOIu5vNrlXLNST2yaTw==\n\
-----END CERTIFICATE-----\n";
const VALID_CERT_2: &str = "-----BEGIN CERTIFICATE-----\n\
MIIDFTCCAf2gAwIBAgIUKckMakNVssdBbRUlVtyWZZPx7EcwDQYJKoZIhvcNAQEL\n\
BQAwGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0yMB4XDTI2MDcyOTE4MzUwNFoX\n\
DTM2MDcyNjE4MzUwNFowGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0yMIIBIjAN\n\
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3pVKr4xNdWm+RIYVRuOv+8Pg3I3/\n\
wsmC7m84I4bw6EofraYY1vTT8XYcWAspo++Tj1hYNAyfdtdrgdZT8dgsTqsVPzYz\n\
rluGu03mu0aE9Ix2IieLvR9C0s+mYpsfCQYRjsL2wDD6fOAWN4wjj1R4XGgUZKCF\n\
q8JirftcRBLGjAa8XXD496dUGXzURQ7C9jAxFmPWGbyz3f1ymOLBvp8RdzrJNCsA\n\
zdEjqJODMMf0czJH5gtt06hIQG9JkPHNqZXVxEIBIDlkmkr9Nk/asqZGhbHILkHX\n\
/jqfdOMb4Xu95iglbwbACgAtfysNQdjUU7hbjKxx4S4FCjf+gyb4whQo/QIDAQAB\n\
o1MwUTAdBgNVHQ4EFgQUVrqEwVrKpoc/GinOYZR13TkjdwgwHwYDVR0jBBgwFoAU\n\
VrqEwVrKpoc/GinOYZR13TkjdwgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B\n\
AQsFAAOCAQEAtK9ylmMIEQsuYm5Qo1pi4xp5rFywO0g5zkWEl/fIMBevP9Thhnco\n\
gHiOFBhQcuo+Go65p3Fbbt3Vrx30Oi0hQUlYLlY44BO3/TgfZ0VbIheeDfyYaq97\n\
S3I1cLHJ1qmKq99zKcqvCcD+NmifbuMi03Zo35Kp+jm8GXpONumnPlu17WZLw5N7\n\
KFHbC1eO3iat27z4WRhPHG4vmPfMHIIvrbA+aEwc1b88NO5UdRmSHvkt4MDEOsIe\n\
IgKmdcW5+BG5ffCRJ9wNsCCy165AFUmuNWz0aqDWybjK4eiEb88sHKbVv7fyXpwi\n\
YwiFroodmakt1behpPy1p9Ih94MTqy9pQw==\n\
-----END CERTIFICATE-----\n";
/// Valid PEM framing / base64, but DER is not an X.509 certificate.
const INVALID_DER_PEM: &str = "-----BEGIN CERTIFICATE-----\n\
MAMBAf8=\n\
-----END CERTIFICATE-----\n";
#[test]
fn parse_empty_bytes_returns_empty() {
let o = parse_and_validate_pem(b"");
assert!(o.accepted.is_empty());
assert_eq!(o.rejected, 0);
assert!(o.no_pem_blocks);
}
#[test]
fn parse_garbage_non_pem_flags_no_blocks_without_panic() {
let o = parse_and_validate_pem(b"this is not a certificate");
assert!(o.accepted.is_empty());
assert_eq!(o.rejected, 0);
assert!(o.no_pem_blocks);
}
#[test]
fn parse_valid_single_cert_pem() {
let o = parse_and_validate_pem(VALID_CERT_1.as_bytes());
assert_eq!(o.accepted.len(), 1);
assert_eq!(o.rejected, 0);
assert!(!o.no_pem_blocks);
}
#[test]
fn parse_multi_cert_bundle() {
let o = parse_and_validate_pem(format!("{VALID_CERT_1}\n{VALID_CERT_2}").as_bytes());
assert_eq!(o.accepted.len(), 2);
assert_eq!(o.rejected, 0);
}
#[test]
fn parse_invalid_der_pem_rejected() {
let o = parse_and_validate_pem(INVALID_DER_PEM.as_bytes());
assert!(o.accepted.is_empty());
assert!(o.rejected >= 1);
assert!(!o.no_pem_blocks);
}
#[test]
fn parse_mixed_bundle_keeps_valid_drops_invalid() {
let o = parse_and_validate_pem(
format!("{VALID_CERT_1}\n{INVALID_DER_PEM}\n{VALID_CERT_2}").as_bytes(),
);
assert_eq!(o.accepted.len(), 2);
assert!(o.rejected >= 1);
}
#[test]
fn validated_ders_build_reqwest_client() {
let o = parse_and_validate_pem(VALID_CERT_1.as_bytes());
assert_eq!(o.accepted.len(), 1);
let mut builder = reqwest::Client::builder();
for der in &o.accepted {
builder = builder.add_root_certificate(
reqwest::Certificate::from_der(der).expect("from_der after rustls validation"),
);
}
builder
.build()
.expect("client with validated roots must construct");
}
#[test]
fn read_bundle_capped_rejects_oversized() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("huge.pem");
std::fs::write(&path, vec![b'A'; (MAX_EXTRA_CA_BUNDLE_BYTES as usize) + 1]).unwrap();
match read_bundle_capped(&path) {
Err(BundleReadError::TooLarge) => {}
other => panic!("expected TooLarge, got {other:?}"),
}
}
#[test]
fn read_bundle_capped_accepts_at_limit() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("ok.pem");
std::fs::write(&path, vec![b'B'; MAX_EXTRA_CA_BUNDLE_BYTES as usize]).unwrap();
let got = read_bundle_capped(&path).expect("at-limit read");
assert_eq!(got.len(), MAX_EXTRA_CA_BUNDLE_BYTES as usize);
}

View file

@ -0,0 +1,22 @@
//! Process-isolated: missing GROK_EXTRA_CA_BUNDLE path → fail-open client build.
#[test]
fn missing_bundle_path_builds_clients_without_panic() {
// Safety: sole test in this binary; set before any OnceLock resolve.
unsafe {
std::env::set_var(
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
"/nonexistent/grok-extra-ca-bundle-invalid-file.pem",
);
}
assert!(xai_grok_extra_ca::extra_root_ders().is_empty());
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
.build()
.expect("async client builds when bundle is unreadable");
xai_grok_extra_ca::with_extra_root_certificates_blocking(reqwest::blocking::Client::builder())
.build()
.expect("blocking client builds when bundle is unreadable");
}

View file

@ -0,0 +1,34 @@
//! Process-isolated: oversize GROK_EXTRA_CA_BUNDLE → ignored; client still builds.
use std::io::Write;
#[test]
fn oversized_bundle_ignored_clients_build() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("oversized.pem");
{
let mut f = std::fs::File::create(&path).expect("create");
let chunk = vec![b'X'; 64 * 1024];
let mut written = 0u64;
let target = xai_grok_extra_ca::MAX_EXTRA_CA_BUNDLE_BYTES + 1;
while written < target {
let n = ((target - written) as usize).min(chunk.len());
f.write_all(&chunk[..n]).expect("write");
written += n as u64;
}
}
// Safety: sole test in this binary; set before any OnceLock resolve.
unsafe {
std::env::set_var(
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
path.as_os_str(),
);
}
assert!(xai_grok_extra_ca::extra_root_ders().is_empty());
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
.build()
.expect("client builds after oversized reject");
}

View file

@ -0,0 +1,41 @@
//! Process-isolated: valid GROK_EXTRA_CA_BUNDLE loads one root via OnceLock.
#[test]
fn valid_bundle_loads_one_root() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("ca.pem");
const CERT: &str = "-----BEGIN CERTIFICATE-----\n\
MIIDFTCCAf2gAwIBAgIUT2czXTuxSAjDjEh92UMB1OVahZYwDQYJKoZIhvcNAQEL\n\
BQAwGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMB4XDTI2MDcyOTE4MzUwNFoX\n\
DTM2MDcyNjE4MzUwNFowGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMIIBIjAN\n\
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1gNk2BQwUy+n5cCaTFtGpSzVQv//\n\
d7QD+3QWeE411wIGJzp3nrd7np55X8JHxeg/pRhspQvLQAF7bt55LSkL/+sSth3S\n\
QTbBqhftic9CXik3llAwbdQkAM9srz5zXWW9KVjZ57dxjjxrS15SCXu/UmvGZy98\n\
faJcS++TRkczsNFzwQEqeDYARVc/no0C0I++NhGLPaNMfFAevvnu6Kt3CYMI5ls4\n\
KCFgnlau4CjgRCMSfRDCRcwEwUAp+DyX9IU+tvDAQY1ncVoa/05tvaEvw7pQ+UgW\n\
0wRG0lk7PLlcWmUkLcFpO+sL5GRkC8RoWM4cFbIOiXoVxUFks/z2y0GCEQIDAQAB\n\
o1MwUTAdBgNVHQ4EFgQU+lyC70W5aR6BIf4VNtjfiWMNzzkwHwYDVR0jBBgwFoAU\n\
+lyC70W5aR6BIf4VNtjfiWMNzzkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B\n\
AQsFAAOCAQEA02972nA7LshRgubz6BwXbh1gA5pLzTd5KEae+94Hq6mP2zJ1T0gk\n\
x+me0NtSgG4BJLdBIylUzo2UmsfB/sz+ght6WX1uB38Vc2UQsp0sRPeeiMovSd6n\n\
I7xZyuZEF3noYJVBBlKQ8XsCUIBNIROlyKlNjNcWY8tGqPh9cepvtZYkBgRZr1vW\n\
hJAE3EOL2ZddrMPF64QeU9UhvCm0Ch+Ceqa1ZWE0MygccggX5s2yQwtXO2ovJdjH\n\
6vW0I02r8sE+NX0d1u8rIPJEKlp89UwCwniD7SxHTNw8bbsTCWz+AMod7vC7De3X\n\
4Daxme+vD8adOfCeOIu5vNrlXLNST2yaTw==\n\
-----END CERTIFICATE-----\n";
std::fs::write(&path, CERT).expect("write cert");
// Safety: sole test in this binary; set before any OnceLock resolve.
unsafe {
std::env::set_var(
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
path.as_os_str(),
);
}
assert_eq!(xai_grok_extra_ca::extra_root_ders().len(), 1);
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
.build()
.expect("client with env-loaded root builds");
}

View file

@ -0,0 +1,22 @@
//! Process-isolated: configured garbage file → zero roots; client still builds.
#[test]
fn configured_garbage_file_yields_zero_roots_and_builds() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("garbage.pem");
std::fs::write(&path, b"not a pem at all").expect("write");
// Safety: sole test in this binary; set before any OnceLock resolve.
unsafe {
std::env::set_var(
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
path.as_os_str(),
);
}
assert!(xai_grok_extra_ca::extra_root_ders().is_empty());
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
.build()
.expect("client builds after zero-cert configured file");
}