1import { Schema } from "effect";
2
3import { EnvironmentId, ThreadId, TrimmedNonEmptyString } from "./baseSchemas.ts";
4import {
5 PREVIEW_VIEWPORT_MAX_AREA,
6 PreviewRenderedViewportSize,
7 PreviewTabId,
8 PreviewViewportPresetId,
9 PreviewViewportSetting,
10 PreviewViewportSize,
11} from "./preview.ts";
12import { ProviderInstanceId } from "./providerInstance.ts";
13import { BrowserProfileId } from "./browserProfile.ts";
14
15const BoundedUrl = Schema.String.check(Schema.isTrimmed())
16 .check(Schema.isNonEmpty())
17 .check(Schema.isMaxLength(2048));
18const URL_GUIDANCE =
19 "Absolute http(s) URL or a schemeless host such as t3.chat or localhost:5173. Schemeless public hosts use https; loopback hosts use http.";
20const PROFILE_GUIDANCE =
21 "Browser profile for a new tab, by id or name (case-insensitive) from preview_status profiles. Omit to use the user's default profile. A tab keeps the profile it opened with.";
22const OptionalTimeoutMs = Schema.optional(
23 Schema.Int.check(Schema.isGreaterThan(0))
24 .check(Schema.isLessThanOrEqualTo(60_000))
25 .annotate({ description: "Maximum wait in milliseconds. Defaults to 15000; maximum 60000." }),
26).annotate({ description: "Maximum wait in milliseconds. Defaults to 15000; maximum 60000." });
27
28/** Operations understood by desktop hosts predating viewport resizing. */
29export const PREVIEW_AUTOMATION_V1_OPERATIONS = [
30 "status",
31 "open",
32 "navigate",
33 "snapshot",
34 "click",
35 "type",
36 "press",
37 "scroll",
38 "evaluate",
39 "waitFor",
40 "recordingStart",
41 "recordingStop",
42] as const;
43
44/** Advertised by current desktop hosts for mixed-version routing. */
45const PREVIEW_AUTOMATION_OPERATIONS = [
46 ...PREVIEW_AUTOMATION_V1_OPERATIONS,
47 "resize",
48 "setColorScheme",
49] as const;
50
51export const PREVIEW_AUTOMATION_SERVER_OPERATIONS = [
52 ...PREVIEW_AUTOMATION_OPERATIONS,
53 "dialog",
54 "close",
55 "upload",
56 "hover",
57 "select",
58 "drag",
59] as const;
60export const PreviewAutomationOperation = Schema.Literals(PREVIEW_AUTOMATION_SERVER_OPERATIONS);
61export type PreviewAutomationOperation = typeof PreviewAutomationOperation.Type;
62
63const PreviewAutomationTabTargetFields = {
64 tabId: Schema.optional(
65 PreviewTabId.annotate({
66 description:
67 "Exact collaborative browser tab to target, including a tab the user opened (see preview_status tabs). Omit to use this agent session's current tab, or the user's visible tab when this session has none.",
68 }),
69 ).annotate({
70 description:
71 "Exact collaborative browser tab to target, including a tab the user opened (see preview_status tabs). Omit to use this agent session's current tab, or the user's visible tab when this session has none.",
72 }),
73};
74
75export const PreviewAutomationTabTargetInput = Schema.Struct(PreviewAutomationTabTargetFields);
76export type PreviewAutomationTabTargetInput = typeof PreviewAutomationTabTargetInput.Type;
77
78/** `human`: a person controls the tab now; `agent`: an agent session opened it; `unclaimed`: neither. */
79export const PreviewAutomationTabOwner = Schema.Literals(["agent", "human", "unclaimed"]);
80
81export const PreviewAutomationProfile = Schema.Struct({
82 id: BrowserProfileId,
83 name: Schema.String,
84 incognito: Schema.optional(Schema.Boolean),
85});
86export type PreviewAutomationProfile = typeof PreviewAutomationProfile.Type;
87
88export const PreviewAutomationStatus = Schema.Struct({
89 available: Schema.Boolean,
90 visible: Schema.Boolean,
91 tabId: Schema.NullOr(PreviewTabId),
92 url: Schema.NullOr(Schema.String),
93 title: Schema.NullOr(Schema.String),
94 loading: Schema.Boolean,
95 control: Schema.optional(
96 Schema.Struct({
97 owner: PreviewAutomationTabOwner,
98 ownedByCaller: Schema.Boolean,
99 generation: Schema.Number,
100 }),
101 ),
102 dialog: Schema.optional(
103 Schema.NullOr(
104 Schema.Struct({
105 type: Schema.String,
106 message: Schema.String,
107 defaultValue: Schema.String,
108 }),
109 ),
110 ),
111 /** Optional for compatibility with desktop hosts predating viewport sizing. */
112 viewportSetting: Schema.optional(PreviewViewportSetting),
113 /** Measured guest-page viewport in CSS pixels when a webview is ready. */
114 viewport: Schema.optional(PreviewRenderedViewportSize),
115 /** Server hosts: a file picker the page opened, answered with preview_upload. */
116 fileChooser: Schema.optional(
117 Schema.NullOr(Schema.Struct({ multiple: Schema.Boolean, accept: Schema.String })),
118 ),
119 /** Server hosts: files this tab downloaded, saved on the environment until the tab closes. */
120 downloads: Schema.optional(
121 Schema.Array(
122 Schema.Struct({
123 fileName: Schema.String,
124 path: Schema.String,
125 sizeBytes: Schema.Number,
126 url: Schema.String,
127 completedAt: Schema.String,
128 }),
129 ),
130 ),
131 /**
132 * Server hosts: every tab in the thread, including ones the user or another
133 * agent session opened. Any of them can be read; acting needs `ownedByCaller`,
134 * or an `unclaimed` tab while no human controls it.
135 */
136 tabs: Schema.optional(
137 Schema.Array(
138 Schema.Struct({
139 tabId: PreviewTabId,
140 url: Schema.NullOr(Schema.String),
141 openerTabId: Schema.optional(PreviewTabId),
142 owner: Schema.optional(PreviewAutomationTabOwner),
143 ownedByCaller: Schema.optional(Schema.Boolean),
144 visible: Schema.optional(Schema.Boolean),
145 profileId: Schema.optional(BrowserProfileId),
146 }),
147 ),
148 ),
149 /** Server hosts: the browser profiles preview_open accepts, as the user's client reported them. */
150 profiles: Schema.optional(Schema.Array(PreviewAutomationProfile)),
151 /** Server hosts: the profile preview_open uses when given none. */
152 defaultProfileId: Schema.optional(BrowserProfileId),
153});
154export type PreviewAutomationStatus = typeof PreviewAutomationStatus.Type;
155
156export const PreviewAutomationDialogInput = Schema.Struct({
157 ...PreviewAutomationTabTargetFields,
158 accept: Schema.Boolean.annotate({
159 description: "Accept the pending browser dialog when true, or dismiss it when false.",
160 }),
161 promptText: Schema.optional(Schema.String).annotate({
162 description: "Optional text to submit when accepting a prompt dialog.",
163 }),
164});
165export type PreviewAutomationDialogInput = typeof PreviewAutomationDialogInput.Type;
166
167export const PreviewAutomationOpenInput = Schema.Struct({
168 ...PreviewAutomationTabTargetFields,
169 url: Schema.optional(BoundedUrl).annotate({
170 description: `Optional initial page URL. ${URL_GUIDANCE} Omit to open a blank tab.`,
171 }),
172 open: Schema.optional(
173 Schema.Boolean.annotate({
174 description:
175 "Whether to open the thread-bound inline preview for the human. Defaults to true; set false for background-only automation.",
176 }),
177 ),
178 show: Schema.optional(
179 Schema.Boolean.annotate({
180 description:
181 "Deprecated alias for open. Whether to reveal the thread-bound inline preview to the human.",
182 }),
183 ),
184 reuseExistingTab: Schema.optional(
185 Schema.Boolean.annotate({
186 description:
187 "Reuse tabId when supplied, otherwise this agent session's current tab. Defaults to true; set false to create a new tab.",
188 }),
189 ),
190 profileId: Schema.optional(
191 TrimmedNonEmptyString.check(Schema.isMaxLength(64)).annotate({
192 description: PROFILE_GUIDANCE,
193 }),
194 ).annotate({ description: PROFILE_GUIDANCE }),
195})
196 .check(
197 Schema.makeFilter(
198 (input) =>
199 !(input.tabId !== undefined && input.reuseExistingTab === false) ||
200 "tabId cannot be combined with reuseExistingTab=false.",
201 ),
202 )
203 .annotate({
204 description:
205 "Opens the collaborative browser for the current thread. Use preview_navigate afterward when readiness waiting matters.",
206 });
207export type PreviewAutomationOpenInput = typeof PreviewAutomationOpenInput.Type;
208
209export const BrowserNavigationTarget = Schema.Union([
210 Schema.Struct({
211 kind: Schema.Literal("url").annotate({
212 description: "Selects direct URL navigation.",
213 }),
214 url: BoundedUrl.annotate({
215 description: `Direct website URL. ${URL_GUIDANCE}`,
216 }),
217 }),
218 Schema.Struct({
219 kind: Schema.Literal("environment-port").annotate({
220 description: "Selects a dev-server port relative to the current execution environment.",
221 }),
222 port: Schema.Int.check(Schema.isGreaterThan(0))
223 .check(Schema.isLessThan(65_536))
224 .annotate({ description: "Dev-server TCP port inside the current environment." }),
225 protocol: Schema.optional(
226 Schema.Literals(["http", "https"]).annotate({
227 description: "Dev-server protocol. Defaults to http.",
228 }),
229 ),
230 path: Schema.optional(
231 Schema.String.annotate({
232 description: "Optional path, query, and fragment, for example /settings?tab=account.",
233 }),
234 ),
235 }),
236]);
237export type BrowserNavigationTarget = typeof BrowserNavigationTarget.Type;
238
239export const PreviewAutomationNavigateInput = Schema.Struct({
240 ...PreviewAutomationTabTargetFields,
241 url: Schema.optional(BoundedUrl).annotate({
242 description: `Website URL. ${URL_GUIDANCE} Use this for public pages and directly reachable URLs.`,
243 }),
244 target: Schema.optional(
245 BrowserNavigationTarget.annotate({
246 description:
247 "Environment-relative target. Prefer {kind:'environment-port',port:5173} for a dev server in the current environment.",
248 }),
249 ).annotate({
250 description:
251 "Environment-relative target. Prefer {kind:'environment-port',port:5173} for a dev server in the current environment.",
252 }),
253 readiness: Schema.optional(
254 Schema.Literals(["load", "domContentLoaded", "none"]).annotate({
255 description:
256 "Readiness milestone before returning. 'load' waits for loading to stop (default), 'domContentLoaded' waits for an interactive document, and 'none' returns immediately.",
257 }),
258 ).annotate({
259 description:
260 "Readiness milestone before returning. 'load' is the default; use 'none' only when a later wait call will verify the page.",
261 }),
262 timeoutMs: OptionalTimeoutMs,
263})
264 .check(
265 Schema.makeFilter(
266 (input) =>
267 Number(input.url !== undefined) + Number(input.target !== undefined) === 1 ||
268 "Provide exactly one of url or target.",
269 ),
270 )
271 .annotate({
272 description:
273 "Navigates the active browser tab. Provide exactly one of url or target; for most public pages use url.",
274 });
275export type PreviewAutomationNavigateInput = typeof PreviewAutomationNavigateInput.Type;
276
277export const PreviewAutomationResizeInput = Schema.Struct({
278 ...PreviewAutomationTabTargetFields,
279 mode: Schema.Literals(["fill", "freeform", "preset"]).annotate({
280 description:
281 "Viewport mode: fill follows the preview panel, freeform uses exact independently resizable dimensions, and preset uses a named device size.",
282 }),
283 preset: Schema.optional(
284 PreviewViewportPresetId.annotate({
285 description: "Named viewport from Chrome DevTools' standard device catalog.",
286 }),
287 ).annotate({
288 description: "Named device size. Required only when mode is preset.",
289 }),
290 width: Schema.optional(
291 PreviewViewportSize.fields.width.annotate({
292 description: "Freeform viewport width in CSS pixels. Required only in freeform mode.",
293 }),
294 ).annotate({
295 description: "Freeform viewport width in CSS pixels. Required only in freeform mode.",
296 }),
297 height: Schema.optional(
298 PreviewViewportSize.fields.height.annotate({
299 description: "Freeform viewport height in CSS pixels. Required only in freeform mode.",
300 }),
301 ).annotate({
302 description: "Freeform viewport height in CSS pixels. Required only in freeform mode.",
303 }),
304 orientation: Schema.optional(
305 Schema.Literals(["portrait", "landscape"]).annotate({
306 description:
307 "Orientation for a fixed device preset. Defaults to the preset's native orientation.",
308 }),
309 ).annotate({
310 description:
311 "Orientation for a named device preset. It is not accepted in fill or freeform mode.",
312 }),
313 timeoutMs: OptionalTimeoutMs,
314})
315 .check(
316 Schema.makeFilter((input) => {
317 const hasPreset = input.preset !== undefined;
318 const hasWidth = input.width !== undefined;
319 const hasHeight = input.height !== undefined;
320 if (hasWidth !== hasHeight) return "Custom dimensions require both width and height.";
321 if (input.mode === "fill") {
322 return !hasPreset && !hasWidth && input.orientation === undefined
323 ? true
324 : "Fill mode does not accept a preset, dimensions, or orientation.";
325 }
326 if (input.mode === "freeform") {
327 if (!hasWidth || !hasHeight || hasPreset || input.orientation !== undefined) {
328 return "Freeform mode requires width and height and does not accept a preset or orientation.";
329 }
330 } else if (!hasPreset || hasWidth || hasHeight) {
331 return "Preset mode requires a preset and does not accept custom dimensions.";
332 }
333 if (hasWidth && hasHeight && input.width! * input.height! > PREVIEW_VIEWPORT_MAX_AREA) {
334 return `Custom viewport area must not exceed ${PREVIEW_VIEWPORT_MAX_AREA} pixels.`;
335 }
336 return true;
337 }),
338 )
339 .annotate({
340 description:
341 "Sets the active browser tab to fill-panel, independently resizable freeform, or named device-preset sizing.",
342 });
343export type PreviewAutomationResizeInput = typeof PreviewAutomationResizeInput.Type;
344
345export const PreviewAutomationResizeResult = Schema.Struct({
346 tabId: PreviewTabId,
347 setting: PreviewViewportSetting,
348 viewport: PreviewRenderedViewportSize,
349});
350export type PreviewAutomationResizeResult = typeof PreviewAutomationResizeResult.Type;
351
352/** Mirrors DesktopPreviewColorScheme; declared here to keep this module free of ipc.ts imports. */
353export const PreviewAutomationColorScheme = Schema.Literals(["system", "light", "dark"]);
354export type PreviewAutomationColorScheme = typeof PreviewAutomationColorScheme.Type;
355
356export const PreviewAutomationSetColorSchemeInput = Schema.Struct({
357 ...PreviewAutomationTabTargetFields,
358 colorScheme: PreviewAutomationColorScheme.annotate({
359 description:
360 "Emulated prefers-color-scheme for the page: light, dark, or system to follow the OS appearance.",
361 }),
362}).annotate({
363 description:
364 "Emulates prefers-color-scheme in the active browser tab without changing the OS or app theme.",
365});
366export type PreviewAutomationSetColorSchemeInput = typeof PreviewAutomationSetColorSchemeInput.Type;
367
368export const PreviewAutomationSetColorSchemeResult = Schema.Struct({
369 tabId: PreviewTabId,
370 colorScheme: PreviewAutomationColorScheme,
371});
372export type PreviewAutomationSetColorSchemeResult =
373 typeof PreviewAutomationSetColorSchemeResult.Type;
374
375const Locator = TrimmedNonEmptyString.annotate({
376 description:
377 "Use locator='aria-ref=<ref>' with a ref from the latest server snapshot (including iframe elements), or a unique Playwright role/text selector. Refs expire on navigation, a new snapshot, or control handoff; refresh the snapshot after a stale-ref error.",
378});
379
380const LegacySelector = TrimmedNonEmptyString.annotate({
381 description:
382 "Legacy CSS selector such as button[type='submit']. Prefer locator for resilient role/text targeting.",
383});
384
385const PointerTargetFields = {
386 ...PreviewAutomationTabTargetFields,
387 selector: Schema.optional(LegacySelector).annotate({
388 description:
389 "Legacy CSS selector such as button[type='submit']. Prefer locator for resilient role/text targeting.",
390 }),
391 locator: Schema.optional(Locator).annotate({
392 description:
393 "Use locator='aria-ref=<ref>' with a ref from the latest server snapshot (including iframe elements), or a unique Playwright role/text selector. Refs expire on navigation, a new snapshot, or control handoff; refresh the snapshot after a stale-ref error.",
394 }),
395 x: Schema.optional(
396 Schema.Finite.annotate({
397 description: "Viewport-relative X coordinate in CSS pixels. Must be paired with y.",
398 }),
399 ),
400 y: Schema.optional(
401 Schema.Finite.annotate({
402 description: "Viewport-relative Y coordinate in CSS pixels. Must be paired with x.",
403 }),
404 ),
405 timeoutMs: OptionalTimeoutMs,
406};
407
408const singlePointerTarget = Schema.makeFilter(
409 (input: {
410 readonly selector?: string | undefined;
411 readonly locator?: string | undefined;
412 readonly x?: number | undefined;
413 readonly y?: number | undefined;
414 }) => {
415 const selectorModes =
416 Number(input.selector !== undefined) + Number(input.locator !== undefined);
417 const hasX = input.x !== undefined;
418 const hasY = input.y !== undefined;
419 if (hasX !== hasY) return "Coordinates require both x and y.";
420 const coordinateModes = hasX && hasY ? 1 : 0;
421 return selectorModes + coordinateModes === 1 || "Provide exactly one target.";
422 },
423);
424
425export const PreviewAutomationClickInput = Schema.Struct({
426 ...PointerTargetFields,
427 button: Schema.optional(
428 Schema.Literals(["left", "right", "middle"]).annotate({
429 description: "Mouse button. Defaults to left; right opens the page's context menu.",
430 }),
431 ),
432 clickCount: Schema.optional(
433 Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 3 })).annotate({
434 description: "1 for a click, 2 for a double-click, 3 for a triple-click. Defaults to 1.",
435 }),
436 ),
437})
438 .check(singlePointerTarget)
439 .annotate({
440 description:
441 "Clicks one target. Provide exactly one of locator, selector, or the x/y coordinate pair.",
442 });
443export type PreviewAutomationClickInput = typeof PreviewAutomationClickInput.Type;
444
445export const PreviewAutomationHoverInput = Schema.Struct(PointerTargetFields)
446 .check(singlePointerTarget)
447 .annotate({
448 description:
449 "Moves the mouse over one target. Provide exactly one of locator, selector, or the x/y coordinate pair.",
450 });
451export type PreviewAutomationHoverInput = typeof PreviewAutomationHoverInput.Type;
452
453export const PreviewAutomationSelectInput = Schema.Struct({
454 ...PreviewAutomationTabTargetFields,
455 selector: Schema.optional(LegacySelector).annotate({
456 description: "Legacy CSS selector for a <select>. Prefer locator.",
457 }),
458 locator: Schema.optional(Locator).annotate({
459 description: "The <select> element, for example aria-ref=<ref> or role=combobox[name='Size'].",
460 }),
461 values: Schema.Array(Schema.String).check(Schema.isMaxLength(100)).annotate({
462 description:
463 "Option values or visible labels to select. Pass several for a multiple select, or an empty list to clear it.",
464 }),
465 timeoutMs: OptionalTimeoutMs,
466})
467 .check(
468 Schema.makeFilter(
469 (input) =>
470 Number(input.selector !== undefined) + Number(input.locator !== undefined) === 1 ||
471 "Provide exactly one of selector or locator.",
472 ),
473 )
474 .annotate({ description: "Chooses options in one native <select> element." });
475export type PreviewAutomationSelectInput = typeof PreviewAutomationSelectInput.Type;
476
477export const PreviewAutomationSelectResult = Schema.Struct({
478 selected: Schema.Array(Schema.String).annotate({
479 description: "The values of the options now selected.",
480 }),
481});
482export type PreviewAutomationSelectResult = typeof PreviewAutomationSelectResult.Type;
483
484/** A required locator field. Its JSON schema keeps only the description on its last check. */
485const DescribedLocator = (description: string) =>
486 Schema.String.check(Schema.isTrimmed())
487 .check(Schema.isNonEmpty({ description }))
488 .annotateKey({ description });
489
490export const PreviewAutomationDragInput = Schema.Struct({
491 ...PreviewAutomationTabTargetFields,
492 source: DescribedLocator(
493 "Locator of the element to drag, for example aria-ref=<ref> from the latest snapshot.",
494 ),
495 target: DescribedLocator("Locator of the element to drop onto, for example aria-ref=<ref>."),
496 timeoutMs: OptionalTimeoutMs,
497}).annotate({ description: "Drags one element and drops it onto another." });
498export type PreviewAutomationDragInput = typeof PreviewAutomationDragInput.Type;
499
500export const PreviewAutomationUploadInput = Schema.Struct({
501 ...PreviewAutomationTabTargetFields,
502 paths: Schema.Array(TrimmedNonEmptyString).check(Schema.isMaxLength(20)).annotate({
503 description:
504 "Absolute paths of files on the environment to give the page. An empty list cancels the open file picker.",
505 }),
506 selector: Schema.optional(LegacySelector).annotate({
507 description: "Legacy CSS selector for an <input type=file>. Prefer locator.",
508 }),
509 locator: Schema.optional(Locator).annotate({
510 description:
511 "Set files directly on this <input type=file> instead of answering the open file picker.",
512 }),
513 timeoutMs: OptionalTimeoutMs,
514})
515 .check(
516 Schema.makeFilter(
517 (input) =>
518 !(input.selector !== undefined && input.locator !== undefined) ||
519 "Provide at most one of selector or locator.",
520 ),
521 )
522 .annotate({
523 description:
524 "Answers the page's open file picker, or sets files on a file input when locator/selector is given.",
525 });
526export type PreviewAutomationUploadInput = typeof PreviewAutomationUploadInput.Type;
527
528export const PreviewAutomationTypeInput = Schema.Struct({
529 ...PreviewAutomationTabTargetFields,
530 text: Schema.String.annotate({ description: "Literal text to insert." }),
531 selector: Schema.optional(LegacySelector).annotate({
532 description: "Legacy CSS selector for the input. Prefer locator.",
533 }),
534 locator: Schema.optional(Locator).annotate({
535 description:
536 "Playwright selector for the input, for example role=textbox[name='Message'] or textarea[placeholder*='Message'].",
537 }),
538 clear: Schema.optional(
539 Schema.Boolean.annotate({
540 description: "Clear the existing input value before inserting text. Defaults to false.",
541 }),
542 ),
543 timeoutMs: OptionalTimeoutMs,
544})
545 .check(
546 Schema.makeFilter(
547 (input) =>
548 !(input.selector !== undefined && input.locator !== undefined) ||
549 "Provide at most one of selector or locator.",
550 ),
551 )
552 .annotate({
553 description:
554 "Types into locator/selector, or into the currently focused element when neither target is provided.",
555 });
556export type PreviewAutomationTypeInput = typeof PreviewAutomationTypeInput.Type;
557
558export const PreviewAutomationPressInput = Schema.Struct({
559 ...PreviewAutomationTabTargetFields,
560 key: Schema.String.check(Schema.isTrimmed())
561 .check(
562 Schema.isNonEmpty({
563 description:
564 "Keyboard key name such as Enter, Escape, Tab, ArrowDown, Backspace, or a single character.",
565 }),
566 )
567 .annotateKey({
568 description:
569 "Keyboard key name such as Enter, Escape, Tab, ArrowDown, Backspace, or a single character.",
570 }),
571 modifiers: Schema.optional(
572 Schema.Array(Schema.Literals(["Alt", "Control", "Meta", "Shift"])).annotate({
573 description: "Modifier keys held while pressing key.",
574 }),
575 ),
576}).annotate({ description: "Presses one keyboard key in the active browser tab." });
577export type PreviewAutomationPressInput = typeof PreviewAutomationPressInput.Type;
578
579export const PreviewAutomationScrollInput = Schema.Struct({
580 ...PreviewAutomationTabTargetFields,
581 deltaX: Schema.optional(
582 Schema.Finite.annotate({
583 description: "Horizontal scroll delta in CSS pixels. Positive scrolls right. Defaults to 0.",
584 }),
585 ),
586 deltaY: Schema.optional(
587 Schema.Finite.annotate({
588 description: "Vertical scroll delta in CSS pixels. Positive scrolls down. Defaults to 0.",
589 }),
590 ),
591 selector: Schema.optional(LegacySelector).annotate({
592 description: "Legacy CSS selector for a scrollable container. Omit to scroll the viewport.",
593 }),
594 locator: Schema.optional(Locator).annotate({
595 description: "Playwright selector for a scrollable container. Omit to scroll the viewport.",
596 }),
597})
598 .check(
599 Schema.makeFilter((input) => {
600 if (input.selector !== undefined && input.locator !== undefined) {
601 return "Provide at most one of selector or locator.";
602 }
603 return (
604 input.deltaX !== undefined || input.deltaY !== undefined || "Provide deltaX or deltaY."
605 );
606 }),
607 )
608 .annotate({
609 description:
610 "Scrolls the viewport, or a locator/selector container. Provide deltaX, deltaY, or both.",
611 });
612export type PreviewAutomationScrollInput = typeof PreviewAutomationScrollInput.Type;
613
614export const PreviewAutomationEvaluateInput = Schema.Struct({
615 ...PreviewAutomationTabTargetFields,
616 expression: Schema.String.check(Schema.isTrimmed())
617 .check(
618 Schema.isNonEmpty({
619 description:
620 "JavaScript expression evaluated in the page's main frame, for example document.title or (() => ({href: location.href}))().",
621 }),
622 )
623 .check(Schema.isMaxLength(64_000))
624 .annotateKey({
625 description:
626 "JavaScript expression evaluated in the page's main frame, for example document.title or (() => ({href: location.href}))().",
627 }),
628 awaitPromise: Schema.optional(
629 Schema.Boolean.annotate({ description: "Await a returned Promise. Defaults to true." }),
630 ),
631 returnByValue: Schema.optional(
632 Schema.Boolean.annotate({
633 description:
634 "Serialize and return the value instead of a remote object reference. Defaults to true.",
635 }),
636 ),
637}).annotate({
638 description:
639 "Evaluates JavaScript in the page. Prefer snapshot and semantic actions; use evaluate for inspection or unsupported interactions.",
640});
641export type PreviewAutomationEvaluateInput = typeof PreviewAutomationEvaluateInput.Type;
642
643export const PreviewAutomationWaitForInput = Schema.Struct({
644 ...PreviewAutomationTabTargetFields,
645 selector: Schema.optional(LegacySelector).annotate({
646 description: "Legacy CSS selector that must match an element. Prefer locator.",
647 }),
648 locator: Schema.optional(Locator).annotate({
649 description:
650 "Playwright selector that must match an element, for example role=button[name='Send'].",
651 }),
652 text: Schema.optional(
653 TrimmedNonEmptyString.annotate({
654 description: "Case-sensitive substring that must appear in visible document text.",
655 }),
656 ).annotate({
657 description: "Case-sensitive substring that must appear in visible document text.",
658 }),
659 urlIncludes: Schema.optional(
660 TrimmedNonEmptyString.annotate({
661 description: "Substring that must appear in the current absolute URL.",
662 }),
663 ).annotate({ description: "Substring that must appear in the current absolute URL." }),
664 timeoutMs: OptionalTimeoutMs,
665})
666 .check(
667 Schema.makeFilter((input) => {
668 if (input.selector !== undefined && input.locator !== undefined) {
669 return "Provide at most one of selector or locator.";
670 }
671 return (
672 input.selector !== undefined ||
673 input.locator !== undefined ||
674 input.text !== undefined ||
675 input.urlIncludes !== undefined ||
676 "Provide at least one wait condition."
677 );
678 }),
679 )
680 .annotate({
681 description:
682 "Waits until all provided conditions match. Use after click/type when the page changes asynchronously.",
683 });
684export type PreviewAutomationWaitForInput = typeof PreviewAutomationWaitForInput.Type;
685
686export const PreviewAutomationElement = Schema.Struct({
687 tag: Schema.String,
688 role: Schema.NullOr(Schema.String),
689 name: Schema.String,
690 selector: Schema.String,
691 x: Schema.Number,
692 y: Schema.Number,
693 width: Schema.Number,
694 height: Schema.Number,
695});
696export type PreviewAutomationElement = typeof PreviewAutomationElement.Type;
697
698export const PreviewAutomationConsoleEntry = Schema.Struct({
699 level: Schema.String,
700 text: Schema.String,
701 timestamp: Schema.String,
702 source: Schema.optional(Schema.String),
703});
704export type PreviewAutomationConsoleEntry = typeof PreviewAutomationConsoleEntry.Type;
705
706export const PreviewAutomationNetworkEntry = Schema.Struct({
707 url: Schema.String,
708 method: Schema.String,
709 status: Schema.NullOr(Schema.Number),
710 failed: Schema.Boolean,
711 errorText: Schema.optional(Schema.String),
712 timestamp: Schema.String,
713});
714export type PreviewAutomationNetworkEntry = typeof PreviewAutomationNetworkEntry.Type;
715
716export const PreviewAutomationActionEvent = Schema.Struct({
717 id: Schema.String,
718 action: Schema.String,
719 status: Schema.Literals(["running", "succeeded", "failed", "interrupted"]),
720 startedAt: Schema.String,
721 completedAt: Schema.optional(Schema.String),
722 error: Schema.optional(Schema.String),
723});
724export type PreviewAutomationActionEvent = typeof PreviewAutomationActionEvent.Type;
725
726export const PreviewAutomationSnapshot = Schema.Struct({
727 url: Schema.String,
728 title: Schema.String,
729 loading: Schema.Boolean,
730 visibleText: Schema.String,
731 interactiveElements: Schema.Array(PreviewAutomationElement),
732 accessibilityTree: Schema.Unknown,
733 consoleEntries: Schema.Array(PreviewAutomationConsoleEntry),
734 networkEntries: Schema.Array(PreviewAutomationNetworkEntry),
735 actionTimeline: Schema.Array(PreviewAutomationActionEvent),
736 screenshot: Schema.Struct({
737 mimeType: Schema.Literal("image/png"),
738 data: Schema.String,
739 width: Schema.Int,
740 height: Schema.Int,
741 }),
742});
743export type PreviewAutomationSnapshot = typeof PreviewAutomationSnapshot.Type;
744
745export const PreviewAutomationRecordingStatus = Schema.Struct({
746 tabId: PreviewTabId,
747 recording: Schema.Boolean,
748 startedAt: Schema.NullOr(Schema.String),
749});
750export type PreviewAutomationRecordingStatus = typeof PreviewAutomationRecordingStatus.Type;
751
752export const PREVIEW_RECORDING_STOP_TIMEOUT_MS = 120_000;
753
754export const PreviewAutomationRecordingArtifact = Schema.Struct({
755 id: Schema.String,
756 tabId: PreviewTabId,
757 path: Schema.String,
758 mimeType: Schema.String,
759 sizeBytes: Schema.Int,
760 createdAt: Schema.String,
761});
762export type PreviewAutomationRecordingArtifact = typeof PreviewAutomationRecordingArtifact.Type;
763
764export const PreviewAutomationClientId = TrimmedNonEmptyString.check(Schema.isMaxLength(128));
765export type PreviewAutomationClientId = typeof PreviewAutomationClientId.Type;
766export const PreviewAutomationConnectionId = TrimmedNonEmptyString.check(Schema.isMaxLength(64));
767export type PreviewAutomationConnectionId = typeof PreviewAutomationConnectionId.Type;
768
769export const PreviewAutomationHostIdentity = Schema.Struct({
770 clientId: PreviewAutomationClientId,
771 environmentId: EnvironmentId,
772});
773export type PreviewAutomationHostIdentity = typeof PreviewAutomationHostIdentity.Type;
774
775export const PreviewAutomationHost = Schema.Struct({
776 ...PreviewAutomationHostIdentity.fields,
777 /**
778 * Missing means the pre-capability-negotiation V1 operation set. This lets
779 * a newer server safely coexist with an older desktop during rollout.
780 */
781 supportedOperations: Schema.optional(Schema.Array(PreviewAutomationOperation)),
782});
783export type PreviewAutomationHost = typeof PreviewAutomationHost.Type;
784
785export const PreviewAutomationHostFocus = Schema.Struct({
786 ...PreviewAutomationHostIdentity.fields,
787 connectionId: PreviewAutomationConnectionId,
788 focused: Schema.Boolean,
789 liveTabs: Schema.optional(
790 Schema.Array(
791 Schema.Struct({
792 threadId: ThreadId,
793 tabId: PreviewTabId,
794 visible: Schema.optional(Schema.Boolean),
795 }),
796 ),
797 ),
798});
799export type PreviewAutomationHostFocus = typeof PreviewAutomationHostFocus.Type;
800
801export const PreviewAutomationRequest = Schema.Struct({
802 requestId: TrimmedNonEmptyString,
803 threadId: ThreadId,
804 tabId: Schema.optional(PreviewTabId),
805 tabIdExplicit: Schema.optional(Schema.Boolean),
806 /** Supplied by the broker from authenticated scope, never from tool input. */
807 agentSessionId: Schema.optional(Schema.String),
808 operation: PreviewAutomationOperation,
809 input: Schema.Unknown,
810 timeoutMs: Schema.Int.check(Schema.isGreaterThan(0)),
811});
812export type PreviewAutomationRequest = typeof PreviewAutomationRequest.Type;
813
814export const PreviewAutomationStreamEvent = Schema.Union([
815 Schema.Struct({
816 type: Schema.Literal("connected"),
817 connectionId: PreviewAutomationConnectionId,
818 }),
819 Schema.Struct({
820 type: Schema.Literal("request"),
821 connectionId: PreviewAutomationConnectionId,
822 request: PreviewAutomationRequest,
823 }),
824]);
825export type PreviewAutomationStreamEvent = typeof PreviewAutomationStreamEvent.Type;
826
827export const PreviewAutomationResponse = Schema.Struct({
828 clientId: PreviewAutomationClientId,
829 connectionId: PreviewAutomationConnectionId,
830 requestId: TrimmedNonEmptyString,
831 ok: Schema.Boolean,
832 result: Schema.optional(Schema.Unknown),
833 error: Schema.optional(
834 Schema.Struct({
835 _tag: TrimmedNonEmptyString,
836 message: Schema.String,
837 detail: Schema.optional(Schema.Unknown),
838 }),
839 ),
840});
841export type PreviewAutomationResponse = typeof PreviewAutomationResponse.Type;
842
843// Thread fields are absent when the caller signed in from outside a T3 thread.
844const McpCapabilityErrorFields = {
845 environmentId: EnvironmentId,
846 threadId: Schema.optional(ThreadId),
847 providerSessionId: Schema.optional(TrimmedNonEmptyString),
848 providerInstanceId: Schema.optional(ProviderInstanceId),
849};
850
851/** Agents read this message, so it names the next step and not only the failure. */
852export class PreviewAutomationUnavailableError extends Schema.TaggedError<PreviewAutomationUnavailableError>()(
853 "PreviewAutomationUnavailableError",
854 {
855 capability: Schema.Literal("preview"),
856 ...McpCapabilityErrorFields,
857 },
858) {
859 override get message(): string {
860 return `MCP credential does not grant the ${this.capability} capability: browser preview tools are off for this thread. Do not retry them. To check a page, use a headless browser from the shell, such as Playwright, or curl. The user can turn on "Agent browser access" in Settings; it applies when the agent session next starts.`;
861 }
862}
863
864/** A `t3-code` MCP tool was called with a credential that does not carry its capability. */
865export class McpCapabilityUnavailableError extends Schema.TaggedError<McpCapabilityUnavailableError>()(
866 "McpCapabilityUnavailableError",
867 {
868 capability: TrimmedNonEmptyString,
869 ...McpCapabilityErrorFields,
870 },
871) {
872 override get message(): string {
873 return `MCP credential does not grant the ${this.capability} capability.`;
874 }
875}
876
877const PreviewAutomationScopeErrorFields = {
878 operation: PreviewAutomationOperation,
879 environmentId: EnvironmentId,
880 threadId: ThreadId,
881 providerSessionId: TrimmedNonEmptyString,
882 providerInstanceId: ProviderInstanceId,
883};
884
885/** The automation host id of an environment's own server browser. */
886export const SERVER_BROWSER_AUTOMATION_CLIENT_ID = "server-browser";
887
888const PreviewAutomationRequestErrorFields = {
889 ...PreviewAutomationScopeErrorFields,
890 clientId: TrimmedNonEmptyString,
891 connectionId: PreviewAutomationConnectionId,
892 requestId: TrimmedNonEmptyString,
893 tabId: Schema.optional(PreviewTabId),
894 timeoutMs: Schema.Int.check(Schema.isGreaterThan(0)),
895};
896
897const PreviewAutomationRemoteDiagnosticFields = {
898 remoteTag: TrimmedNonEmptyString,
899 remoteMessageLength: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
900 remoteDetailKind: Schema.optional(
901 Schema.Literals(["null", "array", "object", "string", "number", "boolean"]),
902 ),
903 cause: Schema.Defect(),
904};
905
906const PreviewAutomationOptionalRemoteDiagnosticFields = {
907 remoteTag: Schema.optional(TrimmedNonEmptyString),
908 remoteMessageLength: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
909 remoteDetailKind: Schema.optional(
910 Schema.Literals(["null", "array", "object", "string", "number", "boolean"]),
911 ),
912 cause: Schema.optional(Schema.Defect()),
913};
914
915export class PreviewAutomationNoAvailableHostError extends Schema.TaggedError<PreviewAutomationNoAvailableHostError>()(
916 "PreviewAutomationNoAvailableHostError",
917 {
918 ...PreviewAutomationScopeErrorFields,
919 clientId: Schema.optional(TrimmedNonEmptyString),
920 connectionId: Schema.optional(PreviewAutomationConnectionId),
921 requestId: Schema.optional(TrimmedNonEmptyString),
922 tabId: Schema.optional(PreviewTabId),
923 timeoutMs: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
924 ...PreviewAutomationOptionalRemoteDiagnosticFields,
925 },
926) {
927 override get message(): string {
928 return `No preview automation host is available for ${this.operation} in environment ${this.environmentId}. The server-owned browser may be starting or reconnecting. Retry preview_status, then call preview_open if no tab is available. If it remains unavailable, report the browser connection failure.`;
929 }
930}
931
932export class PreviewAutomationUnsupportedClientError extends Schema.TaggedError<PreviewAutomationUnsupportedClientError>()(
933 "PreviewAutomationUnsupportedClientError",
934 {
935 ...PreviewAutomationRequestErrorFields,
936 ...PreviewAutomationRemoteDiagnosticFields,
937 },
938) {
939 override get message(): string {
940 return `Preview automation client ${this.clientId} does not support ${this.operation}.`;
941 }
942}
943
944export class PreviewAutomationTabNotFoundError extends Schema.TaggedError<PreviewAutomationTabNotFoundError>()(
945 "PreviewAutomationTabNotFoundError",
946 {
947 ...PreviewAutomationRequestErrorFields,
948 ...PreviewAutomationRemoteDiagnosticFields,
949 },
950) {
951 override get message(): string {
952 return this.tabId
953 ? `Preview tab ${this.tabId} was not found for ${this.operation}. Omit tabId to use the current tab, or call preview_open.`
954 : `No active preview tab was found for ${this.operation}. Call preview_open first.`;
955 }
956}
957
958export class PreviewAutomationTimeoutError extends Schema.TaggedError<PreviewAutomationTimeoutError>()(
959 "PreviewAutomationTimeoutError",
960 {
961 ...PreviewAutomationRequestErrorFields,
962 ...PreviewAutomationOptionalRemoteDiagnosticFields,
963 },
964) {
965 override get message(): string {
966 const summary = `Preview automation ${this.operation} timed out after ${this.timeoutMs}ms.`;
967 return summary;
968 }
969}
970
971export const PreviewAutomationControlReason = Schema.Literals([
972 "agentMismatch",
973 "humanControl",
974 "tabRequired",
975 "closed",
976 "interrupted",
977 "dialogPending",
978 "tabLimit",
979]);
980export type PreviewAutomationControlReason = typeof PreviewAutomationControlReason.Type;
981
982export class PreviewAutomationControlInterruptedError extends Schema.TaggedError<PreviewAutomationControlInterruptedError>()(
983 "PreviewAutomationControlInterruptedError",
984 {
985 ...PreviewAutomationRequestErrorFields,
986 ...PreviewAutomationRemoteDiagnosticFields,
987 reason: Schema.optional(PreviewAutomationControlReason),
988 },
989) {
990 override get message(): string {
991 if (this.reason === "agentMismatch")
992 return "This browser tab belongs to another agent session or a human. Open your own tab.";
993 if (this.reason === "humanControl")
994 return "A human controls this browser tab. Wait until they release control.";
995 if (this.reason === "tabRequired")
996 return "Multiple browser tabs belong to this session. Pass a tabId from preview_open or from the tabs listed by preview_status.";
997 if (this.reason === "dialogPending")
998 return "A browser dialog is pending. Read preview_status and resolve it with preview_dialog.";
999 if (this.reason === "tabLimit")
1000 return "Too many server browser tabs are open. Close one with t3_preview_close, or reuse a tabId from preview_status tabs.";
1001 if (this.reason === "closed") return "This browser tab is closed. Call preview_open.";
1002 if (this.reason === "interrupted")
1003 return "Browser control changed. Take a fresh snapshot before trying again.";
1004 return `Preview automation ${this.operation} was interrupted on client ${this.clientId}.`;
1005 }
1006}
1007
1008export class PreviewAutomationExecutionError extends Schema.TaggedError<PreviewAutomationExecutionError>()(
1009 "PreviewAutomationExecutionError",
1010 {
1011 ...PreviewAutomationRequestErrorFields,
1012 ...PreviewAutomationRemoteDiagnosticFields,
1013 /**
1014 * What went wrong, as the server's own browser reported it, such as a
1015 * page that refused the connection. Absent for other hosts.
1016 */
1017 reason: Schema.optional(Schema.String),
1018 },
1019) {
1020 override get message(): string {
1021 return this.reason === undefined
1022 ? `Preview automation ${this.operation} failed on client ${this.clientId}.`
1023 : `Preview automation ${this.operation} failed: ${this.reason}`;
1024 }
1025}
1026
1027export class PreviewAutomationInvalidSelectorError extends Schema.TaggedError<PreviewAutomationInvalidSelectorError>()(
1028 "PreviewAutomationInvalidSelectorError",
1029 {
1030 ...PreviewAutomationRequestErrorFields,
1031 ...PreviewAutomationRemoteDiagnosticFields,
1032 selectorKind: Schema.optional(Schema.Literals(["locator", "selector"])),
1033 selectorLength: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
1034 /** The selector was an `aria-ref` from an older snapshot. */
1035 staleRef: Schema.optional(Schema.Boolean),
1036 },
1037) {
1038 override get message(): string {
1039 if (this.staleRef)
1040 return "This element ref is stale. Navigation, dialogs, and new snapshots replace refs; take a fresh snapshot and use its refs.";
1041 if (this.selectorKind !== undefined && this.selectorLength !== undefined) {
1042 return `Preview automation ${this.operation} received an invalid ${this.selectorKind} (${this.selectorLength} characters).`;
1043 }
1044 return `Preview automation ${this.operation} received an invalid selector.`;
1045 }
1046}
1047
1048export class PreviewAutomationTargetNotEditableError extends Schema.TaggedError<PreviewAutomationTargetNotEditableError>()(
1049 "PreviewAutomationTargetNotEditableError",
1050 {
1051 ...PreviewAutomationRequestErrorFields,
1052 ...PreviewAutomationRemoteDiagnosticFields,
1053 selectorKind: Schema.optional(Schema.Literals(["focused-element", "locator", "selector"])),
1054 selectorLength: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
1055 },
1056) {
1057 override get message(): string {
1058 if (this.selectorKind === "focused-element") {
1059 return `Preview automation ${this.operation} requires an editable focused element.`;
1060 }
1061 if (this.selectorKind !== undefined && this.selectorLength !== undefined) {
1062 return `Preview automation ${this.operation} requires an editable ${this.selectorKind} (${this.selectorLength} characters).`;
1063 }
1064 return `Preview automation ${this.operation} requires an editable target.`;
1065 }
1066}
1067
1068export class PreviewAutomationResultTooLargeError extends Schema.TaggedError<PreviewAutomationResultTooLargeError>()(
1069 "PreviewAutomationResultTooLargeError",
1070 {
1071 ...PreviewAutomationRequestErrorFields,
1072 ...PreviewAutomationRemoteDiagnosticFields,
1073 maximumBytes: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
1074 },
1075) {
1076 override get message(): string {
1077 const summary =
1078 this.maximumBytes === undefined
1079 ? `Preview automation ${this.operation} produced a result that is too large.`
1080 : `Preview automation ${this.operation} produced a result larger than ${this.maximumBytes} bytes.`;
1081 return summary;
1082 }
1083}
1084
1085export class PreviewAutomationClientDisconnectedError extends Schema.TaggedError<PreviewAutomationClientDisconnectedError>()(
1086 "PreviewAutomationClientDisconnectedError",
1087 PreviewAutomationRequestErrorFields,
1088) {
1089 override get message(): string {
1090 return `Preview automation client ${this.clientId} disconnected during ${this.operation}.`;
1091 }
1092}
1093
1094export class PreviewAutomationRequestQueueClosedError extends Schema.TaggedError<PreviewAutomationRequestQueueClosedError>()(
1095 "PreviewAutomationRequestQueueClosedError",
1096 PreviewAutomationRequestErrorFields,
1097) {
1098 override get message(): string {
1099 return `Preview automation client ${this.clientId} stopped accepting ${this.operation} requests.`;
1100 }
1101}
1102
1103export class PreviewAutomationRemoteUnavailableError extends Schema.TaggedError<PreviewAutomationRemoteUnavailableError>()(
1104 "PreviewAutomationRemoteUnavailableError",
1105 {
1106 ...PreviewAutomationRequestErrorFields,
1107 ...PreviewAutomationRemoteDiagnosticFields,
1108 },
1109) {
1110 override get message(): string {
1111 return `Preview automation ${this.operation} is unavailable on client ${this.clientId}.`;
1112 }
1113}
1114
1115export class PreviewAutomationMalformedResponseError extends Schema.TaggedError<PreviewAutomationMalformedResponseError>()(
1116 "PreviewAutomationMalformedResponseError",
1117 PreviewAutomationRequestErrorFields,
1118) {
1119 override get message(): string {
1120 return `Preview automation client ${this.clientId} returned a malformed response for ${this.operation}.`;
1121 }
1122}
1123
1124export class PreviewAutomationRecordingTransferError extends Schema.TaggedError<PreviewAutomationRecordingTransferError>()(
1125 "PreviewAutomationRecordingTransferError",
1126 {
1127 threadId: ThreadId,
1128 cause: Schema.optional(Schema.Defect()),
1129 },
1130) {
1131 override get message(): string {
1132 return "Preview recording could not be saved to the agent environment.";
1133 }
1134}
1135
1136export class PreviewAutomationRecordingDesktopUpdateRequiredError extends Schema.TaggedError<PreviewAutomationRecordingDesktopUpdateRequiredError>()(
1137 "PreviewAutomationRecordingDesktopUpdateRequiredError",
1138 { threadId: ThreadId, cause: Schema.optional(Schema.Defect()) },
1139) {
1140 override get message(): string {
1141 return "Update the desktop app to transfer recordings. The recording remains on the desktop.";
1142 }
1143}
1144
1145export class PreviewAutomationRecordingTooLargeError extends Schema.TaggedError<PreviewAutomationRecordingTooLargeError>()(
1146 "PreviewAutomationRecordingTooLargeError",
1147 { threadId: ThreadId, cause: Schema.optional(Schema.Defect()) },
1148) {
1149 override get message(): string {
1150 return "The recording exceeds 50 MiB. Make a shorter recording.";
1151 }
1152}
1153
1154export class PreviewAutomationRecordingDeadlineExpiredError extends Schema.TaggedError<PreviewAutomationRecordingDeadlineExpiredError>()(
1155 "PreviewAutomationRecordingDeadlineExpiredError",
1156 { threadId: ThreadId, cause: Schema.optional(Schema.Defect()) },
1157) {
1158 override get message(): string {
1159 return "The recording transfer deadline expired.";
1160 }
1161}
1162
1163export const PreviewAutomationError = Schema.Union([
1164 PreviewAutomationRecordingTransferError,
1165 PreviewAutomationRecordingDesktopUpdateRequiredError,
1166 PreviewAutomationRecordingTooLargeError,
1167 PreviewAutomationRecordingDeadlineExpiredError,
1168 PreviewAutomationUnavailableError,
1169 PreviewAutomationNoAvailableHostError,
1170 PreviewAutomationUnsupportedClientError,
1171 PreviewAutomationTabNotFoundError,
1172 PreviewAutomationTimeoutError,
1173 PreviewAutomationControlInterruptedError,
1174 PreviewAutomationExecutionError,
1175 PreviewAutomationInvalidSelectorError,
1176 PreviewAutomationTargetNotEditableError,
1177 PreviewAutomationResultTooLargeError,
1178 PreviewAutomationClientDisconnectedError,
1179 PreviewAutomationRequestQueueClosedError,
1180 PreviewAutomationRemoteUnavailableError,
1181 PreviewAutomationMalformedResponseError,
1182]);
1183export type PreviewAutomationError = typeof PreviewAutomationError.Type;
1184
1185export const PreviewUrlResolution = Schema.Struct({
1186 requestedUrl: Schema.String,
1187 resolvedUrl: Schema.String,
1188 resolutionKind: Schema.Literals(["direct", "direct-private-network"]),
1189 environmentId: EnvironmentId,
1190});
1191export type PreviewUrlResolution = typeof PreviewUrlResolution.Type;