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, required).
458 pub checkpoint_id: Option<String>,
459 /// Checkpoint label (for checkpoint, optional). Also accepts `label` as an alias.
460 #[serde(alias = "label")]
461 pub checkpoint_label: Option<String>,
462 /// State snapshot as JSON (for checkpoint).
463 pub state: Option<serde_json::Value>,
464 /// Starting checkpoint ID (for `events_between`).
465 pub from: Option<String>,
466 /// Ending checkpoint ID (for `events_between`).
467 pub to: Option<String>,
468 /// Only return events after this index (for `get_events`).
469 pub since_index: Option<usize>,
470 /// JSON string of a previously exported `RecordedSession` (for import).
471 pub session_json: Option<String>,
472 /// Target webview label (for replay).
473 pub webview_label: Option<String>,
474}
475
476// ── inspect ─────────────────────────────────────────────────────────────────
477
478/// Action for the compound `inspect` tool.
479#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
480#[serde(rename_all = "snake_case")]
481pub enum InspectAction {
482 /// Get computed CSS styles for an element.
483 GetStyles,
484 /// Get bounding boxes for elements.
485 GetBoundingBoxes,
486 /// Add a debug highlight overlay to an element.
487 Highlight,
488 /// Remove all debug highlight overlays.
489 ClearHighlights,
490 /// Run an accessibility audit.
491 AuditAccessibility,
492 /// Get performance metrics (timing, heap, DOM).
493 GetPerformance,
494}
495
496impl fmt::Display for InspectAction {
497 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
498 match self {
499 Self::GetStyles => f.write_str("get_styles"),
500 Self::GetBoundingBoxes => f.write_str("get_bounding_boxes"),
501 Self::Highlight => f.write_str("highlight"),
502 Self::ClearHighlights => f.write_str("clear_highlights"),
503 Self::AuditAccessibility => f.write_str("audit_accessibility"),
504 Self::GetPerformance => f.write_str("get_performance"),
505 }
506 }
507}
508
509/// Parameters for the compound `inspect` tool (styles, bounding boxes, highlight, a11y, perf).
510#[derive(Debug, Deserialize, JsonSchema)]
511pub struct InspectParams {
512 /// Action to perform: `get_styles`, `get_bounding_boxes`, highlight, `clear_highlights`, `audit_accessibility`, `get_performance`.
513 pub action: InspectAction,
514 /// Ref handle ID (for `get_styles`, highlight).
515 pub ref_id: Option<String>,
516 /// List of ref handle IDs (for `get_bounding_boxes`).
517 pub ref_ids: Option<Vec<String>>,
518 /// CSS property names to return (for `get_styles` — if omitted, returns key properties).
519 pub properties: Option<Vec<String>>,
520 /// CSS color for the highlight overlay (for highlight, default: "rgba(255, 0, 0, 0.3)").
521 pub color: Option<String>,
522 /// Text label displayed above the highlight (for highlight).
523 #[serde(rename = "highlight_label")]
524 pub label: Option<String>,
525 /// Target webview label.
526 pub webview_label: Option<String>,
527}
528
529// ── css ─────────────────────────────────────────────────────────────────────
530
531/// Action for the compound `css` tool.
532#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
533#[serde(rename_all = "snake_case")]
534pub enum CssAction {
535 /// Inject custom CSS into the page.
536 Inject,
537 /// Remove previously injected CSS.
538 Remove,
539}
540
541impl fmt::Display for CssAction {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 match self {
544 Self::Inject => f.write_str("inject"),
545 Self::Remove => f.write_str("remove"),
546 }
547 }
548}
549
550/// Parameters for the compound `css` tool (inject, remove).
551#[derive(Debug, Deserialize, JsonSchema)]
552pub struct CssParams {
553 /// Action to perform: inject, remove.
554 pub action: CssAction,
555 /// CSS text to inject (for inject action).
556 pub css: Option<String>,
557 /// Allow remote references (`@import`, remote `url(...)`) in injected CSS. Default
558 /// false: remote refs are blocked because they turn `css inject` into a data-exfil /
559 /// SSRF vector (especially when chained with page-sourced prompt injection). Set true
560 /// only when intentionally loading a remote stylesheet/asset for debugging.
561 #[serde(default)]
562 pub allow_remote: bool,
563 /// Target webview label.
564 pub webview_label: Option<String>,
565}
566
567// ── route (network interception) ──────────────────────────────────────────
568
569/// Action for the compound `route` tool.
570#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
571#[serde(rename_all = "snake_case")]
572pub enum RouteAction {
573 /// Add a network route rule.
574 Add,
575 /// List active route rules.
576 List,
577 /// Remove a route rule by id.
578 Clear,
579 /// Remove all route rules.
580 ClearAll,
581 /// Return the log of intercepted requests.
582 Matches,
583}
584
585impl fmt::Display for RouteAction {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 match self {
588 Self::Add => f.write_str("add"),
589 Self::List => f.write_str("list"),
590 Self::Clear => f.write_str("clear"),
591 Self::ClearAll => f.write_str("clear_all"),
592 Self::Matches => f.write_str("matches"),
593 }
594 }
595}
596
597/// How a route rule's pattern is matched against the request URL.
598#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
599#[serde(rename_all = "snake_case")]
600pub enum RouteMatchType {
601 /// URL contains the pattern (default).
602 Substring,
603 /// Glob with `*` wildcards.
604 Glob,
605 /// JavaScript regular expression.
606 Regex,
607 /// Exact URL match.
608 Exact,
609}
610
611impl RouteMatchType {
612 /// Bridge string for this match type.
613 #[must_use]
614 pub fn as_str(self) -> &'static str {
615 match self {
616 Self::Substring => "substring",
617 Self::Glob => "glob",
618 Self::Regex => "regex",
619 Self::Exact => "exact",
620 }
621 }
622}
623
624/// What a matched route rule does to the request.
625#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
626#[serde(rename_all = "snake_case")]
627pub enum RouteBehavior {
628 /// Abort the request (the app sees a network failure).
629 Block,
630 /// Return a synthetic mock response (fetch only; XHR falls back to delay).
631 Fulfill,
632 /// Let the request proceed, but after `delay_ms` (latency injection).
633 Delay,
634}
635
636impl RouteBehavior {
637 /// Bridge string for this behavior.
638 #[must_use]
639 pub fn as_str(self) -> &'static str {
640 match self {
641 Self::Block => "block",
642 Self::Fulfill => "fulfill",
643 Self::Delay => "delay",
644 }
645 }
646}
647
648/// Parameters for the compound `route` tool (network interception / mock / block / delay).
649#[derive(Debug, Deserialize, JsonSchema)]
650pub struct RouteParams {
651 /// Action: add, list, clear, `clear_all`, matches.
652 pub action: RouteAction,
653 /// URL pattern to match (for add). Interpreted per `match_type`.
654 pub pattern: Option<String>,
655 /// How `pattern` is matched: substring (default), glob, regex, exact.
656 pub match_type: Option<RouteMatchType>,
657 /// Restrict to a single HTTP method (for add, optional).
658 pub method: Option<String>,
659 /// What the rule does: block, fulfill, delay (for add). Defaults to fulfill.
660 pub behavior: Option<RouteBehavior>,
661 /// Mock response status code (for fulfill). Default 200.
662 pub status: Option<u16>,
663 /// Mock response status text (for fulfill).
664 pub status_text: Option<String>,
665 /// Mock response headers as a JSON object (for fulfill).
666 pub headers: Option<serde_json::Value>,
667 /// Mock response body (for fulfill). Strings sent as-is; other JSON is serialized.
668 pub body: Option<serde_json::Value>,
669 /// Mock response content-type (for fulfill). Default "application/json".
670 pub content_type: Option<String>,
671 /// Delay in milliseconds (for delay, or to delay a fulfill).
672 pub delay_ms: Option<u64>,
673 /// Maximum times this rule fires (for add). 0 or omitted = unlimited.
674 pub times: Option<u64>,
675 /// Route rule id (for clear).
676 pub id: Option<u64>,
677 /// Maximum match-log entries to return (for matches).
678 pub limit: Option<usize>,
679 /// Target webview label.
680 pub webview_label: Option<String>,
681}
682
683// ── trace (screencast) ──────────────────────────────────────────────────────
684
685/// Action for the compound `trace` tool.
686#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
687#[serde(rename_all = "snake_case")]
688pub enum TraceAction {
689 /// Start capturing screenshots at a fixed interval.
690 Start,
691 /// Stop capturing and return a summary.
692 Stop,
693 /// Report whether a trace is active and how many frames are buffered.
694 Status,
695 /// Return captured frames (base64 PNGs).
696 Frames,
697}
698
699impl fmt::Display for TraceAction {
700 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
701 match self {
702 Self::Start => f.write_str("start"),
703 Self::Stop => f.write_str("stop"),
704 Self::Status => f.write_str("status"),
705 Self::Frames => f.write_str("frames"),
706 }
707 }
708}
709
710/// Parameters for the compound `trace` tool (screencast / visual timeline).
711#[derive(Debug, Deserialize, JsonSchema)]
712pub struct TraceParams {
713 /// Action: start, stop, status, frames.
714 pub action: TraceAction,
715 /// Capture interval in milliseconds (for start). Default 500, min 50.
716 pub interval_ms: Option<u64>,
717 /// Maximum frames to retain in the ring buffer (for start). Default 60, max 600.
718 pub max_frames: Option<usize>,
719 /// If true (for start), also start the event recorder so the trace bundles
720 /// the IPC/DOM/console event timeline alongside the screencast.
721 pub with_events: Option<bool>,
722 /// Maximum frames to return (for frames). 0 or omitted returns all buffered.
723 pub limit: Option<usize>,
724 /// Target webview label to capture.
725 pub webview_label: Option<String>,
726}
727
728// ── animation ─────────────────────────────────────────────────────────────
729
730/// Action for the compound `animation` tool (motion introspection / scrubbing).
731#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
732#[serde(rename_all = "snake_case")]
733pub enum AnimationAction {
734 /// List running CSS animations/transitions with timing, easing, and keyframes.
735 List,
736 /// Deterministically pause + seek the target's animation to N points,
737 /// returning the geometry curve and (optionally) a contact-sheet filmstrip.
738 Scrub,
739 /// Real-time motion + jank recorder. `record=true` arms a rAF watcher;
740 /// `record=false` reads back the measured curve and dropped-frame stats.
741 Sample,
742}
743
744impl fmt::Display for AnimationAction {
745 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
746 match self {
747 Self::List => f.write_str("list"),
748 Self::Scrub => f.write_str("scrub"),
749 Self::Sample => f.write_str("sample"),
750 }
751 }
752}
753
754/// Parameters for the compound `animation` tool.
755#[derive(Debug, Deserialize, JsonSchema)]
756pub struct AnimationParams {
757 /// Action to perform: `list`, `scrub`.
758 pub action: AnimationAction,
759 /// CSS selector for the target element. For `list`, scopes the query (omit
760 /// for all running animations). For `scrub`, selects the element to seek
761 /// (omit to auto-pick the first currently-animating element).
762 pub selector: Option<String>,
763 /// (`scrub`) Number of evenly-spaced progress points to sample. Default 20,
764 /// clamped to 2..=120.
765 pub points: Option<usize>,
766 /// (`scrub`) If true, capture a native screenshot at each point and return a
767 /// single contact-sheet filmstrip PNG. Default false (geometry curve only).
768 pub capture: Option<bool>,
769 /// (`scrub`) If true (default), resume the animation after scrubbing;
770 /// otherwise leave it paused at the final point.
771 pub restore: Option<bool>,
772 /// (`scrub`, with `capture`) Columns in the filmstrip grid. Default ~sqrt(n).
773 pub cols: Option<usize>,
774 /// (`sample`) If true, arm the rAF recorder; if false (default), read back
775 /// recorded sessions.
776 pub record: Option<bool>,
777 /// (`sample`, read) If true, clear recorded sessions after returning them.
778 pub clear: Option<bool>,
779 /// Target webview label.
780 pub webview_label: Option<String>,
781}
782
783// ── logs ────────────────────────────────────────────────────────────────────
784
785/// Action for the compound `logs` tool.
786#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
787#[serde(rename_all = "snake_case")]
788pub enum LogsAction {
789 /// Get captured console.log/warn/error entries.
790 Console,
791 /// Get intercepted fetch/XHR network requests.
792 Network,
793 /// Get IPC call log.
794 Ipc,
795 /// Get URL change history.
796 Navigation,
797 /// Get alert/confirm/prompt dialog events.
798 Dialogs,
799 /// Get combined event stream.
800 Events,
801 /// Find slow IPC calls exceeding a threshold.
802 SlowIpc,
803 /// Clear the IPC + network logs (per-test isolation — start a clean window
804 /// before exercising the app so `detect_ghost_commands`/`logs ipc` reflect
805 /// only the current test's traffic, not stale accumulated probe history).
806 Clear,
807}
808
809impl fmt::Display for LogsAction {
810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811 match self {
812 Self::Console => f.write_str("console"),
813 Self::Network => f.write_str("network"),
814 Self::Ipc => f.write_str("ipc"),
815 Self::Navigation => f.write_str("navigation"),
816 Self::Dialogs => f.write_str("dialogs"),
817 Self::Events => f.write_str("events"),
818 Self::SlowIpc => f.write_str("slow_ipc"),
819 Self::Clear => f.write_str("clear"),
820 }
821 }
822}
823
824/// Parameters for the compound `logs` tool (console, network, ipc, navigation, dialogs, events).
825#[derive(Debug, Deserialize, JsonSchema)]
826pub struct LogsParams {
827 /// Action to perform: console, network, ipc, navigation, dialogs, events, `slow_ipc`.
828 pub action: LogsAction,
829 /// Only return entries after this Unix timestamp in milliseconds (for console, events).
830 pub since: Option<f64>,
831 /// Filter by URL substring (for network).
832 pub filter: Option<String>,
833 /// Maximum number of entries to return (for ipc, network, `slow_ipc`).
834 pub limit: Option<usize>,
835 /// Threshold in milliseconds for slow IPC calls (for `slow_ipc`).
836 pub threshold_ms: Option<u64>,
837 /// When true (for ipc action), await up to 500ms for the latest IPC entry's
838 /// response body to be fully captured. Uses event-driven signaling from the
839 /// fetch interceptor rather than polling.
840 pub wait_for_capture: Option<bool>,
841 /// Target webview label.
842 pub webview_label: Option<String>,
843}