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§
- Agent
MuxWindow 🔒Info - One top-level window visible to this process, with the metadata both
DiscoverWindowsandCaptureWindowneed. Shared viaenumerate_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. - Capture
Outcome 🔒 - A completed capture. Richer than the bare message string it replaces so
audit_log_capture_windowcan record WHAT was captured and at which tier, rather than only the caller’s query and a success/failure flag.
Enums§
- Capture
Tier 🔒 - 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. toexplorer.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 sinceprune_old_captures’s PNG-only extension filter already leaves a.logfile 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 insidecapture_window_dir()itself —prune_old_captures’s PNG-only extension filter already leaves a.logfile 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 disclosesexe_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 riskaudit_log_capture_windowabove 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
CaptureWindowwrites 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
CaptureWindowlogic — 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 itsResultcan be captured once and unconditionally audit-logged by the caller before propagating — seeaudit_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
CaptureTierthat 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_unrenderedas a hint, never as a hard failure. - own_
instance_ 🔒pids - PIDs
CaptureWindowmust 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’sprune_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
Noneand must never fail the capture, matchingaudit_log_capture_window’s own “never break the tool” discipline. - window_
listing_ 🔒entry - One
DiscoverWindowsentry.