Skip to main content

victauri_plugin/mcp/
compound_params.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5// ── Enums ──────────────────────────────────────────────────────────────────
6
7/// Web storage type for browser storage operations.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
9#[serde(rename_all = "snake_case")]
10pub enum StorageType {
11    /// Browser localStorage (persistent across sessions).
12    Local,
13    /// Browser sessionStorage (cleared when tab closes).
14    Session,
15}
16
17impl StorageType {
18    /// Returns the JavaScript property name for this storage type.
19    #[must_use]
20    pub fn js_property(self) -> &'static str {
21        match self {
22            Self::Local => "localStorage",
23            Self::Session => "sessionStorage",
24        }
25    }
26}
27
28impl fmt::Display for StorageType {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Self::Local => f.write_str("local"),
32            Self::Session => f.write_str("session"),
33        }
34    }
35}
36
37/// Browser dialog type for dialog response configuration.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
39#[serde(rename_all = "snake_case")]
40pub enum DialogType {
41    /// JavaScript `alert()` dialog.
42    Alert,
43    /// JavaScript `confirm()` dialog.
44    Confirm,
45    /// JavaScript `prompt()` dialog.
46    Prompt,
47}
48
49impl DialogType {
50    /// Returns the lowercase string for JS bridge consumption.
51    #[must_use]
52    pub fn as_str(self) -> &'static str {
53        match self {
54            Self::Alert => "alert",
55            Self::Confirm => "confirm",
56            Self::Prompt => "prompt",
57        }
58    }
59}
60
61impl fmt::Display for DialogType {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.write_str(self.as_str())
64    }
65}
66
67/// Action to take on a browser dialog.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
69#[serde(rename_all = "snake_case")]
70pub enum DialogAction {
71    /// Accept the dialog (click OK/Yes).
72    Accept,
73    /// Dismiss the dialog (click Cancel/No).
74    Dismiss,
75}
76
77impl DialogAction {
78    /// Returns the lowercase string for JS bridge consumption.
79    #[must_use]
80    pub fn as_str(self) -> &'static str {
81        match self {
82            Self::Accept => "accept",
83            Self::Dismiss => "dismiss",
84        }
85    }
86}
87
88impl fmt::Display for DialogAction {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        f.write_str(self.as_str())
91    }
92}
93
94// ── interact ────────────────────────────────────────────────────────────────
95
96/// Action for the compound `interact` tool.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
98#[serde(rename_all = "snake_case")]
99pub enum InteractAction {
100    /// Click an element.
101    Click,
102    /// Double-click an element.
103    DoubleClick,
104    /// Hover over an element.
105    Hover,
106    /// Focus an element.
107    Focus,
108    /// Scroll an element into view.
109    ScrollIntoView,
110    /// Select an option in a `<select>` element.
111    SelectOption,
112}
113
114impl fmt::Display for InteractAction {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        match self {
117            Self::Click => f.write_str("click"),
118            Self::DoubleClick => f.write_str("double_click"),
119            Self::Hover => f.write_str("hover"),
120            Self::Focus => f.write_str("focus"),
121            Self::ScrollIntoView => f.write_str("scroll_into_view"),
122            Self::SelectOption => f.write_str("select_option"),
123        }
124    }
125}
126
127/// Parameters for the compound `interact` tool (click, hover, focus, scroll, select).
128#[derive(Debug, Deserialize, JsonSchema)]
129pub struct InteractParams {
130    /// Action to perform: click, `double_click`, hover, focus, `scroll_into_view`, `select_option`.
131    pub action: InteractAction,
132    /// Ref handle ID from a DOM snapshot (e.g. "e5"). Required for click, `double_click`, hover, focus, `select_option`.
133    pub ref_id: Option<String>,
134    /// Option values for `select_option` action.
135    pub values: Option<Vec<String>>,
136    /// Single option value for `select_option` (convenience alias for `values`).
137    pub value: Option<String>,
138    /// Horizontal scroll position (pixels). Used with `scroll_into_view` when `ref_id` is null.
139    pub x: Option<f64>,
140    /// Vertical scroll position (pixels). Used with `scroll_into_view` when `ref_id` is null.
141    pub y: Option<f64>,
142    /// If true, deliver a real OS mouse click (`isTrusted: true`) at the
143    /// element's center instead of a synthetic DOM click (for `click`). Falls
144    /// back with an error on platforms without native-input support. Currently
145    /// implemented on Windows.
146    pub trusted: Option<bool>,
147    /// Target webview label.
148    pub webview_label: Option<String>,
149}
150
151// ── input ───────────────────────────────────────────────────────────────────
152
153/// Action for the compound `input` tool.
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
155#[serde(rename_all = "snake_case")]
156pub enum InputAction {
157    /// Set an input element's value directly.
158    Fill,
159    /// Type text character-by-character.
160    TypeText,
161    /// Press a keyboard key.
162    PressKey,
163}
164
165impl fmt::Display for InputAction {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        match self {
168            Self::Fill => f.write_str("fill"),
169            Self::TypeText => f.write_str("type_text"),
170            Self::PressKey => f.write_str("press_key"),
171        }
172    }
173}
174
175/// Parameters for the compound `input` tool (fill, `type_text`, `press_key`).
176#[derive(Debug, Deserialize, JsonSchema)]
177pub struct InputParams {
178    /// Action to perform: fill, `type_text`, `press_key`.
179    pub action: InputAction,
180    /// Ref handle ID of the target element. Required for fill and `type_text`.
181    pub ref_id: Option<String>,
182    /// Value to set (for fill action).
183    pub value: Option<String>,
184    /// Text to type character-by-character (for `type_text` action).
185    pub text: Option<String>,
186    /// Key to press (for `press_key` action, e.g. "Enter", "Escape", "Tab", "`ArrowDown`").
187    pub key: Option<String>,
188    /// If true, deliver real OS keyboard input (`isTrusted: true`) instead of
189    /// synthetic DOM events — for `type_text`/`press_key`. The target element is
190    /// focused first (via `ref_id`). Falls back with an error on platforms
191    /// without native-input support. Currently implemented on Windows.
192    pub trusted: Option<bool>,
193    /// Target webview label.
194    pub webview_label: Option<String>,
195}
196
197// ── window ──────────────────────────────────────────────────────────────────
198
199/// Action for the compound `window` tool.
200#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
201#[serde(rename_all = "snake_case")]
202pub enum WindowAction {
203    /// Get the current state of a window.
204    GetState,
205    /// List all window labels.
206    List,
207    /// Manage a window (minimize, maximize, close, etc.).
208    Manage,
209    /// Resize a window.
210    Resize,
211    /// Move a window to a new position.
212    MoveTo,
213    /// Set a window's title.
214    SetTitle,
215    /// Probe every window and report which ones Victauri can actually introspect
216    /// (i.e. have a responding JS bridge) vs. which are blind — usually because
217    /// the window's capability is missing `victauri:default`.
218    Introspectability,
219}
220
221impl fmt::Display for WindowAction {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        match self {
224            Self::GetState => f.write_str("get_state"),
225            Self::List => f.write_str("list"),
226            Self::Manage => f.write_str("manage"),
227            Self::Resize => f.write_str("resize"),
228            Self::MoveTo => f.write_str("move_to"),
229            Self::SetTitle => f.write_str("set_title"),
230            Self::Introspectability => f.write_str("introspectability"),
231        }
232    }
233}
234
235/// Window management sub-action for the `manage` action.
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
237#[serde(rename_all = "snake_case")]
238pub enum ManageAction {
239    /// Minimize the window.
240    Minimize,
241    /// Restore from minimized state.
242    Unminimize,
243    /// Maximize the window.
244    Maximize,
245    /// Restore from maximized state.
246    Unmaximize,
247    /// Close the window.
248    Close,
249    /// Focus the window.
250    Focus,
251    /// Show the window.
252    Show,
253    /// Hide the window.
254    Hide,
255    /// Enter fullscreen mode.
256    Fullscreen,
257    /// Exit fullscreen mode.
258    Unfullscreen,
259    /// Set the window to always be on top.
260    AlwaysOnTop,
261    /// Remove the always-on-top flag.
262    NotAlwaysOnTop,
263}
264
265impl ManageAction {
266    /// Returns the `snake_case` string for bridge consumption.
267    #[must_use]
268    pub fn as_str(self) -> &'static str {
269        match self {
270            Self::Minimize => "minimize",
271            Self::Unminimize => "unminimize",
272            Self::Maximize => "maximize",
273            Self::Unmaximize => "unmaximize",
274            Self::Close => "close",
275            Self::Focus => "focus",
276            Self::Show => "show",
277            Self::Hide => "hide",
278            Self::Fullscreen => "fullscreen",
279            Self::Unfullscreen => "unfullscreen",
280            Self::AlwaysOnTop => "always_on_top",
281            Self::NotAlwaysOnTop => "not_always_on_top",
282        }
283    }
284}
285
286impl fmt::Display for ManageAction {
287    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        f.write_str(self.as_str())
289    }
290}
291
292/// Parameters for the compound `window` tool (`get_state`, list, manage, resize, move, title).
293#[derive(Debug, Deserialize, JsonSchema)]
294pub struct WindowParams {
295    /// Action to perform: `get_state`, list, manage, resize, `move_to`, `set_title`.
296    pub action: WindowAction,
297    /// Target window label.
298    pub label: Option<String>,
299    /// Window management action (for manage): minimize, unminimize, maximize, unmaximize, close, focus, show, hide, fullscreen, unfullscreen, `always_on_top`, `not_always_on_top`.
300    pub manage_action: Option<ManageAction>,
301    /// Width in logical pixels (for resize).
302    pub width: Option<u32>,
303    /// Height in logical pixels (for resize).
304    pub height: Option<u32>,
305    /// X position in logical pixels (for `move_to`).
306    pub x: Option<i32>,
307    /// Y position in logical pixels (for `move_to`).
308    pub y: Option<i32>,
309    /// New window title (for `set_title`).
310    pub title: Option<String>,
311}
312
313// ── storage ─────────────────────────────────────────────────────────────────
314
315/// Action for the compound `storage` tool.
316#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
317#[serde(rename_all = "snake_case")]
318pub enum StorageAction {
319    /// Read a value from storage.
320    Get,
321    /// Write a value to storage.
322    Set,
323    /// Delete a value from storage.
324    Delete,
325    /// Get all cookies.
326    GetCookies,
327}
328
329impl fmt::Display for StorageAction {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        match self {
332            Self::Get => f.write_str("get"),
333            Self::Set => f.write_str("set"),
334            Self::Delete => f.write_str("delete"),
335            Self::GetCookies => f.write_str("get_cookies"),
336        }
337    }
338}
339
340/// Parameters for the compound `storage` tool (get, set, delete, `get_cookies`).
341#[derive(Debug, Deserialize, JsonSchema)]
342pub struct StorageParams {
343    /// Action to perform: get, set, delete, `get_cookies`.
344    pub action: StorageAction,
345    /// Storage type for get/set/delete. Defaults to local if omitted.
346    pub storage_type: Option<StorageType>,
347    /// Key to read, write, or delete.
348    pub key: Option<String>,
349    /// Value to store (for set action). Will be JSON-serialized if not a string.
350    pub value: Option<serde_json::Value>,
351    /// Target webview label.
352    pub webview_label: Option<String>,
353}
354
355// ── navigate ────────────────────────────────────────────────────────────────
356
357/// Action for the compound `navigate` tool.
358#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
359#[serde(rename_all = "snake_case")]
360pub enum NavigateAction {
361    /// Navigate to a URL.
362    GoTo,
363    /// Navigate back in browser history.
364    GoBack,
365    /// Get the navigation history log.
366    GetHistory,
367    /// Set an auto-response for browser dialogs.
368    SetDialogResponse,
369    /// Get the dialog event log.
370    GetDialogLog,
371}
372
373impl fmt::Display for NavigateAction {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        match self {
376            Self::GoTo => f.write_str("go_to"),
377            Self::GoBack => f.write_str("go_back"),
378            Self::GetHistory => f.write_str("get_history"),
379            Self::SetDialogResponse => f.write_str("set_dialog_response"),
380            Self::GetDialogLog => f.write_str("get_dialog_log"),
381        }
382    }
383}
384
385/// Parameters for the compound `navigate` tool (`go_to`, `go_back`, history, dialogs).
386#[derive(Debug, Deserialize, JsonSchema)]
387pub struct NavigateParams {
388    /// Action to perform: `go_to`, `go_back`, `get_history`, `set_dialog_response`, `get_dialog_log`.
389    pub action: NavigateAction,
390    /// URL to navigate to (for `go_to` action).
391    pub url: Option<String>,
392    /// Dialog type (for `set_dialog_response`).
393    pub dialog_type: Option<DialogType>,
394    /// Dialog action (for `set_dialog_response`).
395    pub dialog_action: Option<DialogAction>,
396    /// Response text for prompt dialogs (for `set_dialog_response`).
397    pub text: Option<String>,
398    /// Target webview label.
399    pub webview_label: Option<String>,
400}
401
402// ── recording ───────────────────────────────────────────────────────────────
403
404/// Action for the compound `recording` tool.
405#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
406#[serde(rename_all = "snake_case")]
407pub enum RecordingAction {
408    /// Begin recording events.
409    Start,
410    /// Stop recording and return the session.
411    Stop,
412    /// Save a state checkpoint.
413    Checkpoint,
414    /// List all checkpoints in the current session.
415    ListCheckpoints,
416    /// Get events since an index.
417    GetEvents,
418    /// Get events between two checkpoints.
419    EventsBetween,
420    /// Get an IPC replay sequence.
421    GetReplay,
422    /// Export the current session as JSON.
423    Export,
424    /// Import a previously exported session.
425    Import,
426    /// Replay recorded IPC commands and compare responses to baseline.
427    Replay,
428    /// Immediately drain pending bridge events into the recording (no 1-second wait).
429    Flush,
430}
431
432impl fmt::Display for RecordingAction {
433    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434        match self {
435            Self::Start => f.write_str("start"),
436            Self::Stop => f.write_str("stop"),
437            Self::Checkpoint => f.write_str("checkpoint"),
438            Self::ListCheckpoints => f.write_str("list_checkpoints"),
439            Self::GetEvents => f.write_str("get_events"),
440            Self::EventsBetween => f.write_str("events_between"),
441            Self::GetReplay => f.write_str("get_replay"),
442            Self::Export => f.write_str("export"),
443            Self::Import => f.write_str("import"),
444            Self::Replay => f.write_str("replay"),
445            Self::Flush => f.write_str("flush"),
446        }
447    }
448}
449
450/// Parameters for the compound `recording` tool (start, stop, checkpoint, replay, export, import).
451#[derive(Debug, Deserialize, JsonSchema)]
452pub struct RecordingParams {
453    /// Action to perform: start, stop, checkpoint, `list_checkpoints`, `get_events`, `events_between`, `get_replay`, export, import, replay, flush.
454    pub action: RecordingAction,
455    /// Session ID (for start — optional, UUID generated if omitted).
456    pub session_id: Option<String>,
457    /// Checkpoint ID (for checkpoint — optional, a `cp-<uuid>` is generated and
458    /// returned if omitted).
459    pub checkpoint_id: Option<String>,
460    /// Checkpoint label (for checkpoint, optional). Also accepts `label` as an alias.
461    #[serde(alias = "label")]
462    pub checkpoint_label: Option<String>,
463    /// State snapshot as JSON (for checkpoint).
464    pub state: Option<serde_json::Value>,
465    /// Starting checkpoint ID (for `events_between`).
466    pub from: Option<String>,
467    /// Ending checkpoint ID (for `events_between`).
468    pub to: Option<String>,
469    /// Only return events after this index (for `get_events`).
470    pub since_index: Option<usize>,
471    /// JSON string of a previously exported `RecordedSession` (for import).
472    pub session_json: Option<String>,
473    /// Target webview label (for replay).
474    pub webview_label: Option<String>,
475}
476
477// ── inspect ─────────────────────────────────────────────────────────────────
478
479/// Action for the compound `inspect` tool.
480#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
481#[serde(rename_all = "snake_case")]
482pub enum InspectAction {
483    /// Get computed CSS styles for an element.
484    GetStyles,
485    /// Get bounding boxes for elements.
486    GetBoundingBoxes,
487    /// Add a debug highlight overlay to an element.
488    Highlight,
489    /// Remove all debug highlight overlays.
490    ClearHighlights,
491    /// Run an accessibility audit.
492    AuditAccessibility,
493    /// Get performance metrics (timing, heap, DOM).
494    GetPerformance,
495}
496
497impl fmt::Display for InspectAction {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        match self {
500            Self::GetStyles => f.write_str("get_styles"),
501            Self::GetBoundingBoxes => f.write_str("get_bounding_boxes"),
502            Self::Highlight => f.write_str("highlight"),
503            Self::ClearHighlights => f.write_str("clear_highlights"),
504            Self::AuditAccessibility => f.write_str("audit_accessibility"),
505            Self::GetPerformance => f.write_str("get_performance"),
506        }
507    }
508}
509
510/// Parameters for the compound `inspect` tool (styles, bounding boxes, highlight, a11y, perf).
511#[derive(Debug, Deserialize, JsonSchema)]
512pub struct InspectParams {
513    /// Action to perform: `get_styles`, `get_bounding_boxes`, highlight, `clear_highlights`, `audit_accessibility`, `get_performance`.
514    pub action: InspectAction,
515    /// Ref handle ID (for `get_styles`, highlight).
516    pub ref_id: Option<String>,
517    /// List of ref handle IDs (for `get_bounding_boxes`).
518    pub ref_ids: Option<Vec<String>>,
519    /// CSS property names to return (for `get_styles` — if omitted, returns key properties).
520    pub properties: Option<Vec<String>>,
521    /// CSS color for the highlight overlay (for highlight, default: "rgba(255, 0, 0, 0.3)").
522    pub color: Option<String>,
523    /// Text label displayed above the highlight (for highlight).
524    #[serde(rename = "highlight_label")]
525    pub label: Option<String>,
526    /// Target webview label.
527    pub webview_label: Option<String>,
528}
529
530// ── css ─────────────────────────────────────────────────────────────────────
531
532/// Action for the compound `css` tool.
533#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
534#[serde(rename_all = "snake_case")]
535pub enum CssAction {
536    /// Inject custom CSS into the page.
537    Inject,
538    /// Remove previously injected CSS.
539    Remove,
540}
541
542impl fmt::Display for CssAction {
543    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
544        match self {
545            Self::Inject => f.write_str("inject"),
546            Self::Remove => f.write_str("remove"),
547        }
548    }
549}
550
551/// Parameters for the compound `css` tool (inject, remove).
552#[derive(Debug, Deserialize, JsonSchema)]
553pub struct CssParams {
554    /// Action to perform: inject, remove.
555    pub action: CssAction,
556    /// CSS text to inject (for inject action).
557    pub css: Option<String>,
558    /// Allow remote references (`@import`, remote `url(...)`) in injected CSS. Default
559    /// false: remote refs are blocked because they turn `css inject` into a data-exfil /
560    /// SSRF vector (especially when chained with page-sourced prompt injection). Set true
561    /// only when intentionally loading a remote stylesheet/asset for debugging.
562    #[serde(default)]
563    pub allow_remote: bool,
564    /// Target webview label.
565    pub webview_label: Option<String>,
566}
567
568// ── route (network interception) ──────────────────────────────────────────
569
570/// Action for the compound `route` tool.
571#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
572#[serde(rename_all = "snake_case")]
573pub enum RouteAction {
574    /// Add a network route rule.
575    Add,
576    /// List active route rules.
577    List,
578    /// Remove a route rule by id.
579    Clear,
580    /// Remove all route rules.
581    ClearAll,
582    /// Return the log of intercepted requests.
583    Matches,
584}
585
586impl fmt::Display for RouteAction {
587    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
588        match self {
589            Self::Add => f.write_str("add"),
590            Self::List => f.write_str("list"),
591            Self::Clear => f.write_str("clear"),
592            Self::ClearAll => f.write_str("clear_all"),
593            Self::Matches => f.write_str("matches"),
594        }
595    }
596}
597
598/// How a route rule's pattern is matched against the request URL.
599#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
600#[serde(rename_all = "snake_case")]
601pub enum RouteMatchType {
602    /// URL contains the pattern (default).
603    Substring,
604    /// Glob with `*` wildcards.
605    Glob,
606    /// JavaScript regular expression.
607    Regex,
608    /// Exact URL match.
609    Exact,
610}
611
612impl RouteMatchType {
613    /// Bridge string for this match type.
614    #[must_use]
615    pub fn as_str(self) -> &'static str {
616        match self {
617            Self::Substring => "substring",
618            Self::Glob => "glob",
619            Self::Regex => "regex",
620            Self::Exact => "exact",
621        }
622    }
623}
624
625/// What a matched route rule does to the request.
626#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
627#[serde(rename_all = "snake_case")]
628pub enum RouteBehavior {
629    /// Abort the request (the app sees a network failure).
630    Block,
631    /// Return a synthetic mock response (fetch only; XHR falls back to delay).
632    Fulfill,
633    /// Let the request proceed, but after `delay_ms` (latency injection).
634    Delay,
635}
636
637impl RouteBehavior {
638    /// Bridge string for this behavior.
639    #[must_use]
640    pub fn as_str(self) -> &'static str {
641        match self {
642            Self::Block => "block",
643            Self::Fulfill => "fulfill",
644            Self::Delay => "delay",
645        }
646    }
647}
648
649/// Parameters for the compound `route` tool (network interception / mock / block / delay).
650#[derive(Debug, Deserialize, JsonSchema)]
651pub struct RouteParams {
652    /// Action: add, list, clear, `clear_all`, matches.
653    pub action: RouteAction,
654    /// URL pattern to match (for add). Interpreted per `match_type`.
655    pub pattern: Option<String>,
656    /// How `pattern` is matched: substring (default), glob, regex, exact.
657    pub match_type: Option<RouteMatchType>,
658    /// Restrict to a single HTTP method (for add, optional).
659    pub method: Option<String>,
660    /// What the rule does: block, fulfill, delay (for add). Defaults to fulfill.
661    pub behavior: Option<RouteBehavior>,
662    /// Mock response status code (for fulfill). Default 200.
663    pub status: Option<u16>,
664    /// Mock response status text (for fulfill).
665    pub status_text: Option<String>,
666    /// Mock response headers as a JSON object (for fulfill).
667    pub headers: Option<serde_json::Value>,
668    /// Mock response body (for fulfill). Strings sent as-is; other JSON is serialized.
669    pub body: Option<serde_json::Value>,
670    /// Mock response content-type (for fulfill). Default "application/json".
671    pub content_type: Option<String>,
672    /// Delay in milliseconds (for delay, or to delay a fulfill).
673    pub delay_ms: Option<u64>,
674    /// Maximum times this rule fires (for add). 0 or omitted = unlimited.
675    pub times: Option<u64>,
676    /// Route rule id (for clear).
677    pub id: Option<u64>,
678    /// Maximum match-log entries to return (for matches).
679    pub limit: Option<usize>,
680    /// Target webview label.
681    pub webview_label: Option<String>,
682}
683
684// ── trace (screencast) ──────────────────────────────────────────────────────
685
686/// Action for the compound `trace` tool.
687#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
688#[serde(rename_all = "snake_case")]
689pub enum TraceAction {
690    /// Start capturing screenshots at a fixed interval.
691    Start,
692    /// Stop capturing and return a summary.
693    Stop,
694    /// Report whether a trace is active and how many frames are buffered.
695    Status,
696    /// Return captured frames (base64 PNGs).
697    Frames,
698}
699
700impl fmt::Display for TraceAction {
701    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
702        match self {
703            Self::Start => f.write_str("start"),
704            Self::Stop => f.write_str("stop"),
705            Self::Status => f.write_str("status"),
706            Self::Frames => f.write_str("frames"),
707        }
708    }
709}
710
711/// Parameters for the compound `trace` tool (screencast / visual timeline).
712#[derive(Debug, Deserialize, JsonSchema)]
713pub struct TraceParams {
714    /// Action: start, stop, status, frames.
715    pub action: TraceAction,
716    /// Capture interval in milliseconds (for start). Default 500, min 50.
717    pub interval_ms: Option<u64>,
718    /// Maximum frames to retain in the ring buffer (for start). Default 60, max 600.
719    pub max_frames: Option<usize>,
720    /// If true (for start), also start the event recorder so the trace bundles
721    /// the IPC/DOM/console event timeline alongside the screencast.
722    pub with_events: Option<bool>,
723    /// Maximum frames to return (for frames). 0 or omitted returns all buffered.
724    pub limit: Option<usize>,
725    /// Target webview label to capture.
726    pub webview_label: Option<String>,
727}
728
729// ── animation ─────────────────────────────────────────────────────────────
730
731/// Action for the compound `animation` tool (motion introspection / scrubbing).
732#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
733#[serde(rename_all = "snake_case")]
734pub enum AnimationAction {
735    /// List running CSS animations/transitions with timing, easing, and keyframes.
736    List,
737    /// Deterministically pause + seek the target's animation to N points,
738    /// returning the geometry curve and (optionally) a contact-sheet filmstrip.
739    Scrub,
740    /// Real-time motion + jank recorder. `record=true` arms a rAF watcher;
741    /// `record=false` reads back the measured curve and dropped-frame stats.
742    Sample,
743}
744
745impl fmt::Display for AnimationAction {
746    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
747        match self {
748            Self::List => f.write_str("list"),
749            Self::Scrub => f.write_str("scrub"),
750            Self::Sample => f.write_str("sample"),
751        }
752    }
753}
754
755/// Parameters for the compound `animation` tool.
756#[derive(Debug, Deserialize, JsonSchema)]
757pub struct AnimationParams {
758    /// Action to perform: `list`, `scrub`.
759    pub action: AnimationAction,
760    /// CSS selector for the target element. For `list`, scopes the query (omit
761    /// for all running animations). For `scrub`, selects the element to seek
762    /// (omit to auto-pick the first currently-animating element).
763    pub selector: Option<String>,
764    /// (`scrub`) Number of evenly-spaced progress points to sample. Default 20,
765    /// clamped to 2..=120.
766    pub points: Option<usize>,
767    /// (`scrub`) If true, capture a native screenshot at each point and return a
768    /// single contact-sheet filmstrip PNG. Default false (geometry curve only).
769    pub capture: Option<bool>,
770    /// (`scrub`) If true (default), resume the animation after scrubbing;
771    /// otherwise leave it paused at the final point.
772    pub restore: Option<bool>,
773    /// (`scrub`, with `capture`) Columns in the filmstrip grid. Default ~sqrt(n).
774    pub cols: Option<usize>,
775    /// (`sample`) If true, arm the rAF recorder; if false (default), read back
776    /// recorded sessions.
777    pub record: Option<bool>,
778    /// (`sample`, read) If true, clear recorded sessions after returning them.
779    pub clear: Option<bool>,
780    /// Target webview label.
781    pub webview_label: Option<String>,
782}
783
784// ── logs ────────────────────────────────────────────────────────────────────
785
786/// Action for the compound `logs` tool.
787#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
788#[serde(rename_all = "snake_case")]
789pub enum LogsAction {
790    /// Get captured console.log/warn/error entries.
791    Console,
792    /// Get intercepted fetch/XHR network requests.
793    Network,
794    /// Get IPC call log.
795    Ipc,
796    /// Get URL change history.
797    Navigation,
798    /// Get alert/confirm/prompt dialog events.
799    Dialogs,
800    /// Get combined event stream.
801    Events,
802    /// Find slow IPC calls exceeding a threshold.
803    SlowIpc,
804    /// Clear the IPC + network logs (per-test isolation — start a clean window
805    /// before exercising the app so `detect_ghost_commands`/`logs ipc` reflect
806    /// only the current test's traffic, not stale accumulated probe history).
807    Clear,
808}
809
810impl fmt::Display for LogsAction {
811    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812        match self {
813            Self::Console => f.write_str("console"),
814            Self::Network => f.write_str("network"),
815            Self::Ipc => f.write_str("ipc"),
816            Self::Navigation => f.write_str("navigation"),
817            Self::Dialogs => f.write_str("dialogs"),
818            Self::Events => f.write_str("events"),
819            Self::SlowIpc => f.write_str("slow_ipc"),
820            Self::Clear => f.write_str("clear"),
821        }
822    }
823}
824
825/// Parameters for the compound `logs` tool (console, network, ipc, navigation, dialogs, events).
826#[derive(Debug, Deserialize, JsonSchema)]
827pub struct LogsParams {
828    /// Action to perform: console, network, ipc, navigation, dialogs, events, `slow_ipc`.
829    pub action: LogsAction,
830    /// Only return entries after this Unix timestamp in milliseconds (for console, events).
831    pub since: Option<f64>,
832    /// Filter by URL substring (for network).
833    pub filter: Option<String>,
834    /// Maximum number of entries to return (for ipc, network, `slow_ipc`).
835    pub limit: Option<usize>,
836    /// Threshold in milliseconds for slow IPC calls (for `slow_ipc`).
837    pub threshold_ms: Option<u64>,
838    /// When true (for ipc action), await up to 500ms for the latest IPC entry's
839    /// response body to be fully captured. Uses event-driven signaling from the
840    /// fetch interceptor rather than polling.
841    pub wait_for_capture: Option<bool>,
842    /// Target webview label.
843    pub webview_label: Option<String>,
844}