Synced from monorepo
Synced from monorepo Changes: - Shell: accept target response id on rewind execute - Shell: stamp response id on chat user message chunks - Worktree: optional rebuild and stale git registration cleanup in auto-GC - Worktree: kind-aware auto-GC TTLs and config knobs - Worktree: macOS process CWD scan and Unix PID liveness for GC guards - Worktree: automatic throttled GC on startup (Linux age-based; non-Linux dead-only) - Pager: add `[ui].combine_queued_prompts` to batch queued follow-ups - Shell: stop overwriting user skills - Tools: read markdown in `skills/` directories untruncated - `/usage` shows per-session token and dollar usage in the TUI - Security: prompt on environment-dumping `ps` variants - Security: always-safe `kubectl` no longer runs arbitrary kubeconfig credential plugins without permission - Tools: make scheduler deletion durable - Shell: add relocation storage primitives - Shell: give side model calls their own conversation ids - Fix five workflow-runtime bugs (budget, pause, cancel, reconnect) - Security: peel `env -S` / `--split-string` operands in the Bash permission gate (managed deny/ask) - Pager: expose doctor in the TUI - Security: block unauthorized RCE via abused safe commands - Pager idle watcher cue: "1 subagent still running" instead of "watching · 1 subagent" - Security: block `rg --pre` arbitrary code execution in auto-mode - Voice: diagnose silent-mic failures (macOS permission) and add doctor/terminal-setup Voice section - App builder deployer: `allow_forking` and `show_built_with_grok` - Pager: stop stacking duplicate "Worked for" markers on parked turns - Shell: support `max` as a distinct reasoning effort tier - Tools: serialize background `/loop` fires on the whole work unit - Shell: add working-directory relocation state primitives - Proto: `ClientToolResult` and `ChatConfig` client-side tools - Shell: model providers - Chat: select App Builder product on the Build path - Shell: attach author identity to feedback when the deployment opts in - Doctor: fix for SSH wrap setup - Workflow authoring skills: create-workflow and import-claude-workflow docs - Add read-only grok doctor - Sandbox: apply Landlock without a controlling TTY - Pager: recover image paste over grok wrap on headless remotes - Pager: make actions screen-mode aware - Shell: resume sessions when the working directory moves - Pager: centralize terminal diagnostics - Workspace: gate inline shell file access - Pager: centralize terminal probes - Pager: edit minimal prompts in an external editor - Pager: standardize backgrounding on Ctrl+B - Shell: recap rides the parent turn's prompt cache - Tools: add scheduler lifecycle version clock Source-Revision: 0f4d7c91b8b2b408333f6de1e8a76cb8eaa71899
This commit is contained in:
parent
a881e6703f
commit
3af4d5d398
556 changed files with 56609 additions and 21892 deletions
448
crates/codegen/xai-grok-pager/src/diagnostics/probes/mod.rs
Normal file
448
crates/codegen/xai-grok-pager/src/diagnostics/probes/mod.rs
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
//! Shared terminal observations for diagnostics consumers.
|
||||
|
||||
use crate::terminal::TerminalContext;
|
||||
|
||||
mod tmux;
|
||||
|
||||
pub use tmux::{LiveTmuxProbe, TmuxOptionQuery, TmuxProbeResult};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TuiProbeEvidence<'a> {
|
||||
pub fullscreen_active: bool,
|
||||
pub kitty_flags_pushed: bool,
|
||||
pub xtversion: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum RuntimeEvidence<T> {
|
||||
Available(T),
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DiagnosticRuntimeEvidence<'a> {
|
||||
pub fullscreen_active: RuntimeEvidence<bool>,
|
||||
pub kitty_flags_pushed: RuntimeEvidence<bool>,
|
||||
pub xtversion: RuntimeEvidence<Option<&'a str>>,
|
||||
}
|
||||
|
||||
impl<'a> From<TuiProbeEvidence<'a>> for DiagnosticRuntimeEvidence<'a> {
|
||||
fn from(value: TuiProbeEvidence<'a>) -> Self {
|
||||
Self {
|
||||
fullscreen_active: RuntimeEvidence::Available(value.fullscreen_active),
|
||||
kitty_flags_pushed: RuntimeEvidence::Available(value.kitty_flags_pushed),
|
||||
xtversion: RuntimeEvidence::Available(value.xtversion),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProbeSnapshot<'a> {
|
||||
pub terminal: &'a TerminalContext,
|
||||
pub tmux: TmuxProbeFacts,
|
||||
pub wayland: WaylandProbeFacts,
|
||||
pub runtime: TuiProbeEvidence<'a>,
|
||||
}
|
||||
|
||||
pub struct CommonProbeSnapshot<'a> {
|
||||
pub terminal: &'a TerminalContext,
|
||||
pub tmux: TmuxProbeFacts,
|
||||
pub wayland: WaylandProbeFacts,
|
||||
}
|
||||
|
||||
pub struct StandaloneDiagnosticSnapshot<'a> {
|
||||
pub common: CommonProbeSnapshot<'a>,
|
||||
pub clipboard: ClipboardProbeFacts,
|
||||
pub host_os: crate::host::HostOs,
|
||||
pub display_server: crate::host::DisplayServer,
|
||||
pub container_no_display: bool,
|
||||
pub color_level: RuntimeEvidence<crate::theme::color_support::ColorLevel>,
|
||||
}
|
||||
|
||||
pub struct DoctorProbeSnapshot<'a> {
|
||||
pub common: ProbeSnapshot<'a>,
|
||||
pub clipboard: ClipboardProbeFacts,
|
||||
pub host_os: crate::host::HostOs,
|
||||
pub display_server: crate::host::DisplayServer,
|
||||
pub container_no_display: bool,
|
||||
pub color_level: crate::theme::color_support::ColorLevel,
|
||||
}
|
||||
|
||||
pub struct TmuxProbeFacts {
|
||||
pub version: TmuxProbeResult<String>,
|
||||
pub extended_keys: TmuxProbeResult<String>,
|
||||
pub set_clipboard: TmuxProbeResult<String>,
|
||||
pub allow_passthrough_support: TmuxProbeResult<()>,
|
||||
pub allow_passthrough: TmuxProbeResult<String>,
|
||||
pub control_mode: TmuxProbeResult<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClipboardProbeFacts {
|
||||
pub route: crate::clipboard::ClipboardRoute,
|
||||
pub native_tool: &'static str,
|
||||
pub osc52_sink_active: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct WaylandProbeFacts {
|
||||
pub is_wayland: bool,
|
||||
pub data_control: TmuxProbeResult<bool>,
|
||||
pub wl_copy_available: bool,
|
||||
}
|
||||
|
||||
pub fn osc52_sink_active() -> bool {
|
||||
crate::clipboard::osc52_sink_active()
|
||||
}
|
||||
|
||||
pub fn collect_startup_tui<'a>(
|
||||
terminal: &'a TerminalContext,
|
||||
runtime: TuiProbeEvidence<'a>,
|
||||
control_mode: bool,
|
||||
tmux: &dyn TmuxOptionQuery,
|
||||
) -> ProbeSnapshot<'a> {
|
||||
let is_wayland = crate::host::DisplayServer::current() == crate::host::DisplayServer::Wayland;
|
||||
let native_tool = startup_native_tool(is_wayland, || {
|
||||
xai_grok_shell::util::clipboard::native_tool_name()
|
||||
});
|
||||
collect_common(
|
||||
terminal,
|
||||
runtime,
|
||||
Some(control_mode),
|
||||
tmux,
|
||||
is_wayland,
|
||||
native_tool,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn collect_doctor_tui<'a>(
|
||||
terminal: &'a TerminalContext,
|
||||
runtime: TuiProbeEvidence<'a>,
|
||||
tmux: &dyn TmuxOptionQuery,
|
||||
) -> DoctorProbeSnapshot<'a> {
|
||||
let is_wayland = crate::host::DisplayServer::current() == crate::host::DisplayServer::Wayland;
|
||||
let native_tool = xai_grok_shell::util::clipboard::native_tool_name();
|
||||
DoctorProbeSnapshot {
|
||||
common: collect_common(terminal, runtime, None, tmux, is_wayland, Some(native_tool)),
|
||||
clipboard: ClipboardProbeFacts {
|
||||
route: crate::clipboard::clipboard_route().clone(),
|
||||
native_tool,
|
||||
osc52_sink_active: osc52_sink_active(),
|
||||
},
|
||||
host_os: crate::host::HostOs::current(),
|
||||
display_server: crate::host::DisplayServer::current(),
|
||||
container_no_display: xai_grok_shell::util::clipboard::is_containerized_without_display(),
|
||||
color_level: crate::theme::color_support::get(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect standalone evidence without running live tmux subprocesses; skipped
|
||||
/// tmux evidence is reported unavailable so a stuck server cannot block doctor.
|
||||
pub fn collect_standalone<'a>(terminal: &'a TerminalContext) -> StandaloneDiagnosticSnapshot<'a> {
|
||||
let host_os = crate::host::HostOs::current();
|
||||
let display_server = crate::host::DisplayServer::current();
|
||||
let is_wayland = display_server == crate::host::DisplayServer::Wayland;
|
||||
let native_tool = xai_grok_shell::util::clipboard::native_tool_name();
|
||||
let data_control = standalone_data_control(is_wayland);
|
||||
let container_no_display = xai_grok_shell::util::clipboard::is_containerized_without_display();
|
||||
collect_standalone_from(
|
||||
terminal,
|
||||
unavailable_tmux(),
|
||||
WaylandProbeFacts {
|
||||
is_wayland,
|
||||
data_control,
|
||||
wl_copy_available: is_wayland && native_tool == "wl-copy",
|
||||
},
|
||||
native_tool,
|
||||
crate::clipboard::resolve_clipboard_route(terminal),
|
||||
osc52_sink_active(),
|
||||
host_os,
|
||||
display_server,
|
||||
container_no_display,
|
||||
match crate::theme::color_support::standalone(terminal.brand) {
|
||||
crate::theme::color_support::StandaloneColorEvidence::Available(level) => {
|
||||
RuntimeEvidence::Available(level)
|
||||
}
|
||||
crate::theme::color_support::StandaloneColorEvidence::Unavailable => {
|
||||
RuntimeEvidence::Unavailable
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Plumbing constructor: one argument per probe fact feeding the snapshot
|
||||
// (crate precedent for probe/render assemblers, e.g. agent_view/render.rs).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn collect_standalone_from<'a>(
|
||||
terminal: &'a TerminalContext,
|
||||
tmux: TmuxProbeFacts,
|
||||
wayland: WaylandProbeFacts,
|
||||
native_tool: &'static str,
|
||||
route: crate::clipboard::ClipboardRoute,
|
||||
osc52_sink_active: bool,
|
||||
host_os: crate::host::HostOs,
|
||||
display_server: crate::host::DisplayServer,
|
||||
container_no_display: bool,
|
||||
color_level: RuntimeEvidence<crate::theme::color_support::ColorLevel>,
|
||||
) -> StandaloneDiagnosticSnapshot<'a> {
|
||||
StandaloneDiagnosticSnapshot {
|
||||
common: CommonProbeSnapshot {
|
||||
terminal,
|
||||
tmux,
|
||||
wayland,
|
||||
},
|
||||
clipboard: ClipboardProbeFacts {
|
||||
route,
|
||||
native_tool,
|
||||
osc52_sink_active,
|
||||
},
|
||||
host_os,
|
||||
display_server,
|
||||
container_no_display,
|
||||
color_level,
|
||||
}
|
||||
}
|
||||
|
||||
fn unavailable_tmux() -> TmuxProbeFacts {
|
||||
TmuxProbeFacts {
|
||||
version: TmuxProbeResult::Unavailable,
|
||||
extended_keys: TmuxProbeResult::Unavailable,
|
||||
set_clipboard: TmuxProbeResult::Unavailable,
|
||||
allow_passthrough_support: TmuxProbeResult::Unavailable,
|
||||
allow_passthrough: TmuxProbeResult::Unavailable,
|
||||
control_mode: TmuxProbeResult::Unavailable,
|
||||
}
|
||||
}
|
||||
|
||||
fn standalone_data_control(is_wayland: bool) -> TmuxProbeResult<bool> {
|
||||
if !is_wayland {
|
||||
return TmuxProbeResult::Unavailable;
|
||||
}
|
||||
match xai_grok_shell::util::clipboard::probe_wayland_data_control() {
|
||||
xai_grok_shell::util::clipboard::WaylandDataControlProbe::Available(value) => {
|
||||
TmuxProbeResult::Available(value)
|
||||
}
|
||||
xai_grok_shell::util::clipboard::WaylandDataControlProbe::Unavailable => {
|
||||
TmuxProbeResult::Unavailable
|
||||
}
|
||||
xai_grok_shell::util::clipboard::WaylandDataControlProbe::Error(error) => {
|
||||
TmuxProbeResult::Error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_common<'a>(
|
||||
terminal: &'a TerminalContext,
|
||||
runtime: TuiProbeEvidence<'a>,
|
||||
control_mode: Option<bool>,
|
||||
tmux: &dyn TmuxOptionQuery,
|
||||
is_wayland: bool,
|
||||
native_tool: Option<&str>,
|
||||
) -> ProbeSnapshot<'a> {
|
||||
let data_control =
|
||||
is_wayland && xai_grok_shell::util::clipboard::wayland_data_control_supported();
|
||||
ProbeSnapshot {
|
||||
terminal,
|
||||
tmux: collect_tmux(terminal, control_mode, tmux),
|
||||
wayland: WaylandProbeFacts {
|
||||
is_wayland,
|
||||
data_control: TmuxProbeResult::Available(data_control),
|
||||
wl_copy_available: is_wayland && native_tool == Some("wl-copy"),
|
||||
},
|
||||
runtime,
|
||||
}
|
||||
}
|
||||
|
||||
fn startup_native_tool(
|
||||
is_wayland: bool,
|
||||
native_tool: impl FnOnce() -> &'static str,
|
||||
) -> Option<&'static str> {
|
||||
is_wayland.then(native_tool)
|
||||
}
|
||||
|
||||
fn collect_tmux(
|
||||
terminal: &TerminalContext,
|
||||
control_mode: Option<bool>,
|
||||
tmux: &dyn TmuxOptionQuery,
|
||||
) -> TmuxProbeFacts {
|
||||
if !terminal.is_tmux_backed() {
|
||||
return TmuxProbeFacts {
|
||||
version: TmuxProbeResult::Unavailable,
|
||||
extended_keys: TmuxProbeResult::Unavailable,
|
||||
set_clipboard: TmuxProbeResult::Unavailable,
|
||||
allow_passthrough_support: TmuxProbeResult::Unavailable,
|
||||
allow_passthrough: TmuxProbeResult::Unavailable,
|
||||
control_mode: TmuxProbeResult::Unavailable,
|
||||
};
|
||||
}
|
||||
|
||||
let allow_passthrough_support = tmux.option_support("allow-passthrough");
|
||||
let allow_passthrough = match &allow_passthrough_support {
|
||||
TmuxProbeResult::Available(()) => tmux.show_option("allow-passthrough"),
|
||||
TmuxProbeResult::Unsupported => TmuxProbeResult::Unsupported,
|
||||
TmuxProbeResult::Unavailable => TmuxProbeResult::Unavailable,
|
||||
TmuxProbeResult::Error(error) => TmuxProbeResult::Error(error.clone()),
|
||||
};
|
||||
TmuxProbeFacts {
|
||||
version: terminal
|
||||
.tmux_version
|
||||
.clone()
|
||||
.map(TmuxProbeResult::Available)
|
||||
.unwrap_or(TmuxProbeResult::Unavailable),
|
||||
extended_keys: terminal
|
||||
.tmux_extended_keys
|
||||
.clone()
|
||||
.map(TmuxProbeResult::Available)
|
||||
.unwrap_or(TmuxProbeResult::Unavailable),
|
||||
set_clipboard: tmux.show_option("set-clipboard"),
|
||||
allow_passthrough_support,
|
||||
allow_passthrough,
|
||||
control_mode: control_mode
|
||||
.map(TmuxProbeResult::Available)
|
||||
.unwrap_or_else(|| tmux.control_mode()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
|
||||
struct FakeTmuxQuery {
|
||||
values: HashMap<&'static str, TmuxProbeResult<String>>,
|
||||
control_mode: TmuxProbeResult<bool>,
|
||||
calls: RefCell<Vec<String>>,
|
||||
}
|
||||
|
||||
impl TmuxOptionQuery for FakeTmuxQuery {
|
||||
fn show_option(&self, option: &str) -> TmuxProbeResult<String> {
|
||||
self.calls.borrow_mut().push(option.to_owned());
|
||||
self.values
|
||||
.get(option)
|
||||
.cloned()
|
||||
.unwrap_or(TmuxProbeResult::Unavailable)
|
||||
}
|
||||
|
||||
fn option_support(&self, option: &str) -> TmuxProbeResult<()> {
|
||||
self.calls.borrow_mut().push(format!("support:{option}"));
|
||||
match self.values.get(option) {
|
||||
Some(TmuxProbeResult::Unsupported) => TmuxProbeResult::Unsupported,
|
||||
Some(TmuxProbeResult::Unavailable) | None => TmuxProbeResult::Unavailable,
|
||||
Some(TmuxProbeResult::Error(error)) => TmuxProbeResult::Error(error.clone()),
|
||||
Some(TmuxProbeResult::Available(_)) => TmuxProbeResult::Available(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn control_mode(&self) -> TmuxProbeResult<bool> {
|
||||
self.calls.borrow_mut().push("control-mode".to_owned());
|
||||
self.control_mode.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn runtime() -> TuiProbeEvidence<'static> {
|
||||
TuiProbeEvidence {
|
||||
fullscreen_active: true,
|
||||
kitty_flags_pushed: true,
|
||||
xtversion: Some("WezTerm 20240203"),
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_fake() -> FakeTmuxQuery {
|
||||
FakeTmuxQuery {
|
||||
values: HashMap::new(),
|
||||
control_mode: TmuxProbeResult::Unavailable,
|
||||
calls: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supplied_control_mode_is_the_only_snapshot_fact() {
|
||||
let terminal = TerminalContext {
|
||||
multiplexer: crate::terminal::MultiplexerKind::Tmux,
|
||||
..Default::default()
|
||||
};
|
||||
let fake = FakeTmuxQuery {
|
||||
values: HashMap::from([
|
||||
(
|
||||
"set-clipboard",
|
||||
TmuxProbeResult::Available("external".to_owned()),
|
||||
),
|
||||
("allow-passthrough", TmuxProbeResult::Unsupported),
|
||||
]),
|
||||
control_mode: TmuxProbeResult::Available(false),
|
||||
calls: RefCell::new(Vec::new()),
|
||||
};
|
||||
|
||||
let snapshot = collect_common(&terminal, runtime(), Some(true), &fake, false, None);
|
||||
|
||||
assert_eq!(snapshot.tmux.control_mode, TmuxProbeResult::Available(true));
|
||||
assert_eq!(
|
||||
snapshot.tmux.set_clipboard,
|
||||
TmuxProbeResult::Available("external".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.tmux.allow_passthrough_support,
|
||||
TmuxProbeResult::Unsupported
|
||||
);
|
||||
assert_eq!(
|
||||
fake.calls.into_inner(),
|
||||
["support:allow-passthrough", "set-clipboard"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_control_mode_uses_backend() {
|
||||
let terminal = TerminalContext {
|
||||
multiplexer: crate::terminal::MultiplexerKind::Tmux,
|
||||
..Default::default()
|
||||
};
|
||||
let fake = FakeTmuxQuery {
|
||||
control_mode: TmuxProbeResult::Available(true),
|
||||
..empty_fake()
|
||||
};
|
||||
let snapshot = collect_common(&terminal, runtime(), None, &fake, false, None);
|
||||
|
||||
assert_eq!(snapshot.tmux.control_mode, TmuxProbeResult::Available(true));
|
||||
assert_eq!(
|
||||
fake.calls.into_inner(),
|
||||
["support:allow-passthrough", "set-clipboard", "control-mode"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_tmux_snapshot_skips_tmux_backend() {
|
||||
let terminal = TerminalContext::default();
|
||||
let fake = FakeTmuxQuery {
|
||||
control_mode: TmuxProbeResult::Error("must not run".to_owned()),
|
||||
..empty_fake()
|
||||
};
|
||||
let snapshot = collect_common(&terminal, runtime(), None, &fake, false, None);
|
||||
|
||||
assert_eq!(snapshot.tmux.set_clipboard, TmuxProbeResult::Unavailable);
|
||||
assert_eq!(snapshot.tmux.control_mode, TmuxProbeResult::Unavailable);
|
||||
assert!(fake.calls.into_inner().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_wayland_startup_does_not_resolve_native_tool() {
|
||||
let called = Cell::new(false);
|
||||
let native_tool = startup_native_tool(false, || {
|
||||
called.set(true);
|
||||
"fake-tool"
|
||||
});
|
||||
assert!(!called.get());
|
||||
assert_eq!(native_tool, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wayland_startup_resolves_native_tool_for_warning() {
|
||||
let called = Cell::new(false);
|
||||
let native_tool = startup_native_tool(true, || {
|
||||
called.set(true);
|
||||
"fake-tool"
|
||||
});
|
||||
assert!(called.get());
|
||||
assert_eq!(native_tool, Some("fake-tool"));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue