Skip to main content

Module window_capture

Module window_capture 

Source
Expand description

Window capture and discovery, plus the audit log both write.

Split out of main.rs (audit step 17, docs/reports/REPORT_DRY_AND_MODULARITY_AUDIT_2026_09_06.md) — it was the largest single cluster in that file and is one concern: find AgentMux windows, decide what this agent is allowed to capture (CaptureTier), capture it, and record what was captured.

Bodies are unchanged from main.rs; the only edits were pub(crate) on the items call_tool and the tests name. main.rs glob-imports this module, so those call sites — and mod tests’s use super::* — resolve exactly as they did before.

Structs§

AgentMuxWindowInfo 🔒
One top-level window visible to this process, with the metadata both DiscoverWindows and CaptureWindow need. Shared via enumerate_agentmux_windows() so the two tools can’t drift on what counts as “AgentMux’s own window”, “the calling agent’s own instance”, or which tier a target falls in.
CaptureOutcome 🔒
A completed capture. Richer than the bare message string it replaces so audit_log_capture_window can record WHAT was captured and at which tier, rather than only the caller’s query and a success/failure flag.

Enums§

CaptureTier 🔒
Capture tier for one target window — see docs/specs/SPEC_AGENT_UNRESTRICTED_CAPTURE_WITH_ACCOUNTABILITY_2026_08_30.md §3. Keyed on WHAT is being captured, never on who is asking: identity is already proven upstream (sign_ui_automation_auth), so the open question is what a proven identity may reach.

Constants§

CAPTURE_RETENTION 🔒
OWN_INSTANCE_ANCESTOR_HOPS 🔒
Max ancestor hops to walk from this MCP process before giving up — bounds the fallback search (see own_instance_pids’s doc comment for why this is a fallback, not the primary signal), while never walking so far up the tree (e.g. to explorer.exe) that “same instance” degrades into “everything on the desktop.”

Functions§

append_window_audit_log_entry 🔒
Shared append-only NDJSON writer for both window-tool audit trails above — best-effort (a logging failure must never break the tool itself), inside capture_window_dir() itself since prune_old_captures’s PNG-only extension filter already leaves a .log file in that same directory untouched, so this doesn’t need (or want) its own directory.
audit_log_capture_window 🔒
Audit trail for CaptureWindow — reagent P1 (PR #2709 round 4): the residual risk after rounds 2-3’s scoping (a different AgentMux instance’s window, which can belong to a different OS user on a shared machine) is a disclosure-across-a-human-boundary question, not a technical one a capability flag alone would fully answer — a real per-agent opt-in gate is tracked as a separate follow-up (no existing settings/enforcement mechanism to build it on today). Logging every call — who, what was requested, what happened — is the honest, shippable Phase-1 answer: best-effort (a logging failure must never break the tool itself), append-only NDJSON inside capture_window_dir() itself — prune_old_captures’s PNG-only extension filter already leaves a .log file in that same directory untouched, so this doesn’t need (or want) its own separate directory alongside it.
audit_log_discover_windows 🔒
Audit trail for DiscoverWindows — reagent P1 on PR #2810: this tool discloses exe_path (a full filesystem path, typically embedding the OS username) for windows belonging to OTHER AgentMux instances/users on a shared machine — the exact same disclosure-across-a-human-boundary risk audit_log_capture_window above already exists to log, but this tool shipped with none. Same shape, same file (a single window-related audit trail is easier to review than two): best-effort, never blocks the tool’s own result.
audit_target_label 🔒
Label for a window in the AUDIT TRAIL.
candidate_label 🔒
Label for a window in a CALLER-FACING disambiguation/candidate list.
capture_window_dir 🔒
Where CaptureWindow writes its PNGs: <AgentMux root>/tmp/capture-window, not the shared OS temp dir — reagent P2 on this tool’s own PR (#2709 round 1): std::env::temp_dir() is world-readable on a multi-user host, and CaptureWindow can capture arbitrary OS windows (not just AgentMux’s own pane), so a captured image could leak to other local users.
capture_window_impl 🔒
The actual CaptureWindow logic — window enumeration, own-instance and third-party-app exclusion, targeting (by pid or by title), capture, and save. Extracted to its own function (rather than living inline in the "CaptureWindow" => match arm) so its Result can be captured once and unconditionally audit-logged by the caller before propagating — see audit_log_capture_window.
current_user_id 🔒
The OS user id owning the calling process, used to place a target window on the near or far side of the T3 boundary.
enumerate_agentmux_windows 🔒
Enumerate every top-level OS window, AgentMux-owned or not, each tagged with the CaptureTier that decides whether it may be captured.
looks_unrendered 🔒
Cheap heuristic for “this capture is probably a blank/unpainted frame, not a real render” — sample ~200 evenly-spaced pixels and check they’re all within a small tolerance of the first one. Not real image analysis (a legitimately solid-color themed window would also trip this) — deliberately cheap and approximate, used only to decide whether a short bounded retry is worth attempting, and to set likely_unrendered as a hint, never as a hard failure.
own_instance_pids 🔒
PIDs CaptureWindow must treat as “this agent’s own AgentMux instance” and always exclude — reagent P0 (PR #2709 round 3).
prune_old_captures 🔒
Same bug class, same fix, as agentmux-srv’s prune_old_screenshots (ui_handlers.rs, reagent P2, PR #2662) — reapplied here per reagent’s review of this tool’s own PR (#2709 round 1), which found it wasn’t reused. Best-effort, on the write path, PNG-only.
sha256_file 🔒
Hex SHA-256 of a captured PNG, recorded in the audit trail so a leaked screenshot can be traced back to the call that produced it (spec §6). Best-effort — a hashing failure yields None and must never fail the capture, matching audit_log_capture_window’s own “never break the tool” discipline.
window_listing_entry 🔒
One DiscoverWindows entry.