Synced from monorepo

Synced from monorepo

Changes:
- grok-shell: send an expired external-provider credential to the sign-in flow, not a 401 loop
- pager: clickable ▲ jumps to the top of the response being read
- grok-shell: keep a large task log from making the completion message too long
- Plan viewer scrollbar: widen grab zone to the border column; fix striped thumb in Terminal.app
- pager: poll the tmux probe teardown grace instead of sleeping it
- security: vendor-compat MCP kill switch is now actually enforced when reported as on
- grok-shell: restore session eviction when a leader client disconnects
- Bump rust-toolchain to 1.93.0
- workspace: lexical-normalize permission path patterns before glob matching
- pager: reject garbage Enter in the /resume picker
- pager: show Mermaid affordances in plan mode preview
- pager: drop manage-account link from /session-info
- workspace: auto-approve read-only git queries; defer write floor to auto classifier
- Add free-form pattern editor to the "Always allow" command prompt
- grok-shell: fix /btw caching
- pager: Tab walks answers in the ask_user_question card
- External-provider auth refresh: single 7s attempt instead of 3×5s
- pager: don't resurrect finished background tasks as Running when completion arrives first
- pager: report tmux truecolor clamping in Doctor
- Fix plan viewer scrollbar click+drag hijacked by comment gutter
- pager/shell: stop double Recap after the same last turn
- sampler: preserve x-should-retry through stream collection
- pager: clear plan-mode indicator immediately when the user approves a plan
- pager: tmux does not re-read its config on reattach

Source-Revision: 64c4de99cc822b25ce9c54ab5a4f372093d0885d
This commit is contained in:
grokkybara[bot] 2026-08-03 08:17:57 +00:00
commit 780d1388ff
323 changed files with 12258 additions and 7226 deletions

View file

@ -0,0 +1,107 @@
//! HTTPS (TLS) gRPC transport coverage for the external OTEL stream —
//! regression test for GB-4580, where `https://` collector endpoints were
//! rejected at exporter build time and the stream silently disabled itself.
//!
//! The collector presents a certificate signed by a freshly generated CA and
//! the client trusts it via the standard `OTEL_EXPORTER_OTLP_CERTIFICATE`
//! variable, so the full TLS handshake + OTLP export path is exercised.
//! Lives in its own integration-test binary because the external telemetry
//! registry is a process-global `OnceLock`.
mod otlp_collector;
use otlp_collector as col;
#[test]
fn external_stream_grpc_over_tls_end_to_end() {
let tls = col::generate_tls_material();
let ca_file = tempfile::NamedTempFile::new().expect("CA temp file");
std::fs::write(ca_file.path(), &tls.ca_cert_pem).expect("write CA pem");
let ca_path = ca_file.path().to_str().expect("utf-8 CA path").to_string();
let collected = col::Collected::default();
let endpoint = col::start_grpc_tls_collector(
collected.clone(),
tls.server_cert_pem.clone(),
tls.server_key_pem.clone(),
);
assert!(endpoint.starts_with("https://"), "{endpoint}");
let mut cfg = xai_grok_telemetry::external::ExternalOtelConfig::resolve_with(
|name| match name {
"GROK_EXTERNAL_OTEL" => Some("1".into()),
"OTEL_LOGS_EXPORTER" | "OTEL_METRICS_EXPORTER" => Some("otlp".into()),
"OTEL_EXPORTER_OTLP_ENDPOINT" => Some(endpoint.clone()),
"OTEL_EXPORTER_OTLP_PROTOCOL" => Some("grpc".into()),
"OTEL_EXPORTER_OTLP_CERTIFICATE" => Some(ca_path.clone()),
"OTEL_METRIC_EXPORT_INTERVAL" => Some("200".into()),
"OTEL_BLRP_SCHEDULE_DELAY" => Some("100".into()),
_ => None,
},
None,
)
.expect("double opt-in must resolve");
assert_eq!(cfg.logs_ca_certificate.as_deref(), Some(ca_path.as_str()));
cfg.client = xai_grok_telemetry::external::config::ExternalClientInfo {
service_version: "0.0.0-test".into(),
client_version: "0.0.0-test".into(),
app_entrypoint: "cli".into(),
};
xai_grok_telemetry::external::init(Some(cfg));
assert!(
xai_grok_telemetry::external::is_active(),
"https gRPC exporters must build and activate the stream (GB-4580)"
);
// `SessionNew` maps to the `session.count` metric; `SessionHarness` maps
// to the `session_start` log record — emit both so each signal's TLS
// export path is exercised.
xai_grok_telemetry::log_event(xai_grok_telemetry::events::SessionNew {
session_id: "sess-grpc-tls-1".into(),
client_identifier: None,
client_version: None,
is_git_repo: true,
permission_mode: xai_grok_telemetry::enums::PermissionMode::Ask,
});
xai_grok_telemetry::log_event(xai_grok_telemetry::events::SessionHarness {
session_id: "sess-grpc-tls-1".into(),
client_identifier: Some("grok-pager".into()),
model_id: "grok-4".into(),
agent_name: "grok-build-plan".into(),
permission_mode: xai_grok_telemetry::enums::PermissionMode::Ask,
mcp_server_names: vec![],
plugin_names: vec![],
skill_names: vec![],
lsp_server_names: vec![],
hook_names: vec![],
agents_md_dir_names: vec![],
memory_enabled: false,
is_git_repo: true,
auto_update: None,
});
xai_grok_telemetry::external::flush();
assert!(
col::wait_until(std::time::Duration::from_secs(10), || {
collected.logs_len() > 0
}),
"log records must arrive over TLS"
);
let names = col::event_names(&collected);
assert!(
names.iter().any(|n| n == "grok_code.session_start"),
"expected grok_code.session_start in {names:?}"
);
// Metrics ride the same TLS channel config; make sure at least one
// periodic export lands too.
assert!(
col::wait_until(std::time::Duration::from_secs(10), || {
collected.metrics_len() > 0
}),
"metric exports must arrive over TLS"
);
xai_grok_telemetry::external::shutdown();
}

View file

@ -174,6 +174,87 @@ async fn start_grpc_collector(collected: Collected, addr_tx: std::sync::mpsc::Se
.expect("collector serve");
}
/// A freshly generated CA plus a `localhost` server certificate signed by it,
/// all PEM-encoded — for the TLS collector variants.
pub struct TestTlsMaterial {
pub ca_cert_pem: String,
pub server_cert_pem: String,
pub server_key_pem: String,
}
/// Generate a self-signed CA and a `localhost`/`127.0.0.1` server certificate
/// signed by it.
pub fn generate_tls_material() -> TestTlsMaterial {
use rcgen::{BasicConstraints, CertificateParams, IsCa, KeyPair};
let ca_key = KeyPair::generate().expect("generate CA key");
let mut ca_params = CertificateParams::new(Vec::new()).expect("CA params");
ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
let ca_cert = ca_params.self_signed(&ca_key).expect("self-sign CA");
let server_key = KeyPair::generate().expect("generate server key");
let server_params =
CertificateParams::new(vec!["localhost".to_string(), "127.0.0.1".to_string()])
.expect("server params");
let server_cert = server_params
.signed_by(&server_key, &ca_cert, &ca_key)
.expect("sign server cert");
TestTlsMaterial {
ca_cert_pem: ca_cert.pem(),
server_cert_pem: server_cert.pem(),
server_key_pem: server_key.serialize_pem(),
}
}
/// Start a **TLS** gRPC collector presenting `server_cert_pem`; returns its
/// base URL (`https://localhost:PORT`).
pub fn start_grpc_tls_collector(
collected: Collected,
server_cert_pem: String,
server_key_pem: String,
) -> String {
use opentelemetry_proto::tonic::collector::logs::v1::logs_service_server::LogsServiceServer;
use opentelemetry_proto::tonic::collector::metrics::v1::metrics_service_server::MetricsServiceServer;
// The test binary links both ring and aws-lc-rs, so rustls cannot pick a
// process default on its own; the server-side acceptor needs one pinned.
// (The production client is unaffected: tonic passes a provider
// explicitly.)
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let (addr_tx, addr_rx) = std::sync::mpsc::channel::<SocketAddr>();
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("collector runtime");
rt.block_on(async move {
let incoming = tonic::transport::server::TcpIncoming::bind(
"127.0.0.1:0".parse().expect("collector bind addr"),
)
.expect("bind gRPC TLS collector");
addr_tx
.send(incoming.local_addr().expect("collector addr"))
.expect("send addr");
let identity = tonic::transport::Identity::from_pem(server_cert_pem, server_key_pem);
let service = GrpcCollector { collected };
tonic::transport::Server::builder()
.tls_config(tonic::transport::ServerTlsConfig::new().identity(identity))
.expect("collector TLS config")
.add_service(LogsServiceServer::new(service.clone()))
.add_service(MetricsServiceServer::new(service))
.serve_with_incoming(incoming)
.await
.expect("collector serve");
});
});
let addr = addr_rx
.recv_timeout(std::time::Duration::from_secs(10))
.expect("collector must start");
format!("https://localhost:{}", addr.port())
}
pub fn decode_logs(
collected: &Collected,
) -> Vec<opentelemetry_proto::tonic::collector::logs::v1::ExportLogsServiceRequest> {