libretto 0.6.20 → 0.6.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +5 -1
  2. package/README.template.md +5 -1
  3. package/dist/cli/commands/execution.js +8 -1
  4. package/dist/cli/core/browser.js +8 -3
  5. package/dist/cli/core/daemon/daemon.js +8 -6
  6. package/dist/cli/core/providers/kernel.js +107 -29
  7. package/dist/cli/core/providers/libretto-cloud.js +22 -3
  8. package/dist/cli/core/providers/steel.js +10 -1
  9. package/dist/index.d.ts +3 -2
  10. package/dist/index.js +15 -1
  11. package/dist/runtime/recovery/agent.d.ts +50 -2
  12. package/dist/runtime/recovery/agent.js +159 -45
  13. package/dist/runtime/recovery/index.d.ts +2 -1
  14. package/dist/runtime/recovery/index.js +16 -2
  15. package/dist/runtime/recovery/page-fallbacks.d.ts +45 -0
  16. package/dist/runtime/recovery/page-fallbacks.js +342 -0
  17. package/dist/shared/state/index.d.ts +1 -1
  18. package/dist/shared/state/session-state.d.ts +4 -1
  19. package/dist/shared/state/session-state.js +2 -1
  20. package/dist/shared/workflow/workflow.d.ts +19 -6
  21. package/dist/shared/workflow/workflow.js +38 -9
  22. package/docs/reference/runtime/page-fallbacks.mdx +85 -0
  23. package/docs/understand-libretto/error-handling-and-recovery.mdx +45 -0
  24. package/package.json +1 -1
  25. package/skills/libretto/SKILL.md +8 -2
  26. package/skills/libretto/references/code-generation-rules.md +23 -6
  27. package/skills/libretto-readonly/SKILL.md +1 -1
  28. package/src/cli/commands/execution.ts +8 -1
  29. package/src/cli/core/browser.ts +7 -2
  30. package/src/cli/core/daemon/daemon.ts +9 -4
  31. package/src/cli/core/daemon/ipc.ts +1 -0
  32. package/src/cli/core/providers/kernel.ts +153 -29
  33. package/src/cli/core/providers/libretto-cloud.ts +29 -6
  34. package/src/cli/core/providers/steel.ts +11 -1
  35. package/src/cli/core/providers/types.ts +3 -0
  36. package/src/index.ts +22 -2
  37. package/src/runtime/recovery/agent.ts +227 -50
  38. package/src/runtime/recovery/index.ts +21 -1
  39. package/src/runtime/recovery/page-fallbacks.ts +476 -0
  40. package/src/shared/state/index.ts +1 -0
  41. package/src/shared/state/session-state.ts +2 -0
  42. package/src/shared/workflow/workflow.ts +90 -20
@@ -33,6 +33,83 @@ const KEY_MAPPINGS = {
33
33
  function mapKeyName(key) {
34
34
  return KEY_MAPPINGS[key.toUpperCase()] ?? key;
35
35
  }
36
+ function clamp(value, min, max) {
37
+ return Math.min(Math.max(value, min), max);
38
+ }
39
+ function scalePoint(x, y, scale) {
40
+ return {
41
+ x: clamp(x * scale.scaleX, 0, Math.max(scale.viewportWidth - 1, 0)),
42
+ y: clamp(y * scale.scaleY, 0, Math.max(scale.viewportHeight - 1, 0))
43
+ };
44
+ }
45
+ function scaleBrowserAction(action, scale) {
46
+ switch (action.type) {
47
+ case "click": {
48
+ const point = scalePoint(action.x, action.y, scale);
49
+ return { ...action, ...point };
50
+ }
51
+ case "double_click": {
52
+ const point = scalePoint(action.x, action.y, scale);
53
+ return { ...action, ...point };
54
+ }
55
+ case "scroll": {
56
+ const point = scalePoint(action.x, action.y, scale);
57
+ return {
58
+ ...action,
59
+ ...point,
60
+ scroll_x: action.scroll_x * scale.scaleX,
61
+ scroll_y: action.scroll_y * scale.scaleY
62
+ };
63
+ }
64
+ case "drag":
65
+ return {
66
+ ...action,
67
+ path: action.path.map((point) => scalePoint(point.x, point.y, scale))
68
+ };
69
+ case "move": {
70
+ const point = scalePoint(action.x, action.y, scale);
71
+ return { ...action, ...point };
72
+ }
73
+ case "keypress":
74
+ case "type":
75
+ case "wait":
76
+ case "screenshot":
77
+ case "done":
78
+ return action;
79
+ }
80
+ }
81
+ function readPngDimensions(buffer) {
82
+ const pngSignature = "89504e470d0a1a0a";
83
+ if (buffer.subarray(0, 8).toString("hex") !== pngSignature) {
84
+ throw new Error("Recovery screenshot is not a PNG image.");
85
+ }
86
+ return {
87
+ width: buffer.readUInt32BE(16),
88
+ height: buffer.readUInt32BE(20)
89
+ };
90
+ }
91
+ async function takeViewportScreenshot(page) {
92
+ const viewport = page.viewportSize();
93
+ if (!viewport) {
94
+ throw new Error("Viewport size not found");
95
+ }
96
+ const screenshot = await page.screenshot({
97
+ fullPage: false,
98
+ scale: "css",
99
+ timeout: 1e4
100
+ });
101
+ const dimensions = readPngDimensions(screenshot);
102
+ return {
103
+ screenshot,
104
+ dimensions,
105
+ scale: {
106
+ scaleX: viewport.width / dimensions.width,
107
+ scaleY: viewport.height / dimensions.height,
108
+ viewportWidth: viewport.width,
109
+ viewportHeight: viewport.height
110
+ }
111
+ };
112
+ }
36
113
  async function executeBrowserAction(page, action, logger = defaultLogger) {
37
114
  switch (action.type) {
38
115
  case "click": {
@@ -109,56 +186,79 @@ async function executeBrowserAction(page, action, logger = defaultLogger) {
109
186
  import { z } from "zod";
110
187
  const recoveryActionSchema = z.object({
111
188
  reasoning: z.string().describe("Your reasoning about what you see and what action to take"),
112
- action: z.discriminatedUnion("type", [
113
- z.object({
114
- type: z.literal("click"),
115
- x: z.number(),
116
- y: z.number()
117
- }),
118
- z.object({
119
- type: z.literal("type"),
120
- text: z.string()
121
- }),
122
- z.object({
123
- type: z.literal("keypress"),
124
- keys: z.array(z.string())
125
- }),
126
- z.object({
127
- type: z.literal("scroll"),
128
- x: z.number(),
129
- y: z.number(),
130
- scroll_x: z.number(),
131
- scroll_y: z.number()
132
- }),
133
- z.object({
134
- type: z.literal("wait")
135
- }),
136
- z.object({
137
- type: z.literal("done")
138
- })
139
- ])
189
+ action: z.object({
190
+ type: z.enum(["click", "type", "keypress", "scroll", "wait", "done"]).describe("The browser action to execute."),
191
+ x: z.number().nullable().describe("The screenshot pixel x coordinate for click/scroll."),
192
+ y: z.number().nullable().describe("The screenshot pixel y coordinate for click/scroll."),
193
+ text: z.string().nullable().describe("Text for type actions."),
194
+ keys: z.array(z.string()).nullable().describe("Keys for keypress actions."),
195
+ scroll_x: z.number().nullable().describe("Horizontal scroll delta."),
196
+ scroll_y: z.number().nullable().describe("Vertical scroll delta.")
197
+ })
140
198
  });
141
- async function executeRecoveryAgent(page, instruction, logger, model) {
199
+ function numberOrThrow(value, field) {
200
+ if (typeof value === "number") return value;
201
+ throw new Error(`Recovery action is missing ${field}.`);
202
+ }
203
+ function normalizeRecoveryAction(action) {
204
+ switch (action.type) {
205
+ case "click":
206
+ return {
207
+ type: "click",
208
+ x: numberOrThrow(action.x, "x"),
209
+ y: numberOrThrow(action.y, "y")
210
+ };
211
+ case "type":
212
+ return { type: "type", text: action.text ?? "" };
213
+ case "keypress":
214
+ return { type: "keypress", keys: action.keys ?? [] };
215
+ case "scroll":
216
+ return {
217
+ type: "scroll",
218
+ x: numberOrThrow(action.x, "x"),
219
+ y: numberOrThrow(action.y, "y"),
220
+ scroll_x: numberOrThrow(action.scroll_x, "scroll_x"),
221
+ scroll_y: numberOrThrow(action.scroll_y, "scroll_y")
222
+ };
223
+ case "wait":
224
+ return { type: "wait" };
225
+ case "done":
226
+ return { type: "done" };
227
+ }
228
+ }
229
+ function getRecoveryStatus(steps) {
230
+ if (steps.length === 0) {
231
+ return "skipped";
232
+ }
233
+ const actionSteps = steps.filter((step) => step.action.type !== "done");
234
+ const completed = steps.at(-1)?.action.type === "done";
235
+ if (actionSteps.length === 0 && completed) {
236
+ return "no-action-needed";
237
+ }
238
+ if (completed) {
239
+ return "action-taken";
240
+ }
241
+ return "incomplete";
242
+ }
243
+ const DEFAULT_RECOVERY_MAX_STEPS = 3;
244
+ async function executeRecoveryAgent(page, instruction, logger, model, maxSteps = DEFAULT_RECOVERY_MAX_STEPS) {
142
245
  if (!model) {
143
- return;
246
+ return { status: "skipped", steps: [] };
144
247
  }
145
248
  const log = logger ?? defaultLogger;
146
249
  log.info("Executing vision-based recovery agent", { instruction });
147
- const viewport = page.viewportSize();
148
- if (!viewport) {
149
- throw new Error("Viewport size not found");
150
- }
151
- let screenshot;
250
+ let screenshotState;
152
251
  try {
153
- screenshot = (await page.screenshot({ fullPage: false, timeout: 1e4 })).toString("base64");
252
+ screenshotState = await takeViewportScreenshot(page);
154
253
  } catch (screenshotError) {
155
254
  log.warn("Failed to take screenshot for recovery agent, skipping", {
156
255
  screenshotError: screenshotError instanceof Error ? screenshotError.message : String(screenshotError)
157
256
  });
158
257
  throw new Error("Failed to take screenshot for recovery agent");
159
258
  }
160
- const maxSteps = 3;
259
+ const steps = [];
161
260
  for (let step = 1; step <= maxSteps; step++) {
261
+ const { screenshot, dimensions, scale } = screenshotState;
162
262
  const { object: result } = await generateObject({
163
263
  model,
164
264
  schema: recoveryActionSchema,
@@ -172,33 +272,47 @@ async function executeRecoveryAgent(page, instruction, logger, model) {
172
272
 
173
273
  Your task: ${instruction}
174
274
 
175
- Viewport: ${viewport.width}x${viewport.height}px. Complete this in as few steps as possible.
275
+ Screenshot: ${dimensions.width}x${dimensions.height}px. Coordinates must be screenshot pixel coordinates relative to the top-left corner of the screenshot. Complete this in as few steps as possible.
176
276
  Analyze the screenshot and decide what action to take. If the task is complete or no action is needed, use the "done" action type.`
177
277
  },
178
278
  {
179
279
  type: "image",
180
- image: `data:image/png;base64,${screenshot}`
280
+ image: screenshot
181
281
  }
182
282
  ]
183
283
  }
184
284
  ],
185
285
  temperature: 0
186
286
  });
287
+ const imageAction = normalizeRecoveryAction(result.action);
288
+ const action = scaleBrowserAction(imageAction, scale);
187
289
  log.info(`Recovery step ${step}/${maxSteps}`, {
188
290
  reasoning: result.reasoning,
189
- action: result.action
291
+ imageAction,
292
+ action,
293
+ screenshot: dimensions,
294
+ scale
295
+ });
296
+ steps.push({
297
+ step,
298
+ reasoning: result.reasoning,
299
+ action
190
300
  });
191
- if (result.action.type === "done") {
301
+ if (action.type === "done") {
192
302
  log.info("Recovery agent completed - no more actions needed");
193
303
  break;
194
304
  }
195
- await executeBrowserAction(page, result.action, log);
305
+ await executeBrowserAction(page, action, log);
196
306
  await delay(2e3);
197
- screenshot = (await page.screenshot({ fullPage: false })).toString(
198
- "base64"
199
- );
307
+ if (step < maxSteps) {
308
+ screenshotState = await takeViewportScreenshot(page);
309
+ }
200
310
  }
201
311
  log.info("Recovery agent execution completed");
312
+ return {
313
+ status: getRecoveryStatus(steps),
314
+ steps
315
+ };
202
316
  }
203
317
  export {
204
318
  executeRecoveryAgent
@@ -1,6 +1,7 @@
1
- export { executeRecoveryAgent } from './agent.js';
1
+ export { BrowserAction, RecoveryAgentResult, RecoveryAgentStep, executeRecoveryAgent } from './agent.js';
2
2
  export { attemptWithRecovery } from './recovery.js';
3
3
  export { DetectedSubmissionError, KnownSubmissionError, detectSubmissionError } from './errors.js';
4
+ export { COMPUTER_USE_RECOVERY_MODELS, ComputerUseRecoveryActionOptions, POPUP_RECOVERY_INSTRUCTION, PopupRecoveryActionOptions, RecoveryAction, RecoveryActionContext, RecoveryActionHandler, RecoveryActionOptions, RecoveryActionResult, RecoveryActionTargetType, computerUseRecoveryAction, createRecoveryPage, popupRecoveryAction } from './page-fallbacks.js';
4
5
  import 'playwright';
5
6
  import '../../shared/logger/logger.js';
6
7
  import 'ai';
@@ -1,10 +1,24 @@
1
- import { executeRecoveryAgent } from "./agent.js";
1
+ import {
2
+ executeRecoveryAgent
3
+ } from "./agent.js";
2
4
  import { attemptWithRecovery } from "./recovery.js";
3
5
  import {
4
6
  detectSubmissionError
5
7
  } from "./errors.js";
8
+ import {
9
+ COMPUTER_USE_RECOVERY_MODELS,
10
+ POPUP_RECOVERY_INSTRUCTION,
11
+ computerUseRecoveryAction,
12
+ createRecoveryPage,
13
+ popupRecoveryAction
14
+ } from "./page-fallbacks.js";
6
15
  export {
16
+ COMPUTER_USE_RECOVERY_MODELS,
17
+ POPUP_RECOVERY_INSTRUCTION,
7
18
  attemptWithRecovery,
19
+ computerUseRecoveryAction,
20
+ createRecoveryPage,
8
21
  detectSubmissionError,
9
- executeRecoveryAgent
22
+ executeRecoveryAgent,
23
+ popupRecoveryAction
10
24
  };
@@ -0,0 +1,45 @@
1
+ import { Page } from 'playwright';
2
+ import { LanguageModel } from 'ai';
3
+
4
+ type RecoveryActionTargetType = "page" | "locator";
5
+ type RecoveryActionContext = {
6
+ page: Page;
7
+ targetType: RecoveryActionTargetType;
8
+ method: string;
9
+ args: readonly unknown[];
10
+ error: unknown;
11
+ };
12
+ type RecoveryActionResult = Record<string, unknown> | void;
13
+ type RecoveryActionHandler = (context: RecoveryActionContext) => Promise<RecoveryActionResult>;
14
+ type RecoveryAction = RecoveryActionHandler;
15
+ type RecoveryActionOptions = {
16
+ recoveryAction: RecoveryAction;
17
+ };
18
+ type ComputerUseRecoveryModelOptions = {
19
+ languageModel: LanguageModel;
20
+ } | {
21
+ provider: "openai";
22
+ apiKey: string;
23
+ model?: "gpt-5.5";
24
+ } | {
25
+ provider: "anthropic";
26
+ apiKey: string;
27
+ model?: "claude-sonnet-4-6";
28
+ };
29
+ type ComputerUseRecoveryActionOptions = ComputerUseRecoveryModelOptions & {
30
+ instruction: string;
31
+ maxSteps?: number;
32
+ };
33
+ type PopupRecoveryActionOptions = ComputerUseRecoveryModelOptions & {
34
+ maxSteps?: number;
35
+ };
36
+ declare const POPUP_RECOVERY_INSTRUCTION: string;
37
+ declare const COMPUTER_USE_RECOVERY_MODELS: {
38
+ readonly anthropic: "claude-sonnet-4-6";
39
+ readonly openai: "gpt-5.5";
40
+ };
41
+ declare function createRecoveryPage(page: Page, options: RecoveryActionOptions): Page;
42
+ declare function computerUseRecoveryAction(options: ComputerUseRecoveryActionOptions): RecoveryAction;
43
+ declare function popupRecoveryAction(options: PopupRecoveryActionOptions): RecoveryAction;
44
+
45
+ export { COMPUTER_USE_RECOVERY_MODELS, type ComputerUseRecoveryActionOptions, POPUP_RECOVERY_INSTRUCTION, type PopupRecoveryActionOptions, type RecoveryAction, type RecoveryActionContext, type RecoveryActionHandler, type RecoveryActionOptions, type RecoveryActionResult, type RecoveryActionTargetType, computerUseRecoveryAction, createRecoveryPage, popupRecoveryAction };
@@ -0,0 +1,342 @@
1
+ import { executeRecoveryAgent } from "./agent.js";
2
+ const POPUP_RECOVERY_INSTRUCTION = [
3
+ "Look at the page for any popup, modal, cookie banner, overlay, dialog, or interstitial that blocks interaction.",
4
+ "If any blocking popup is visible, close it before returning done.",
5
+ "Prefer obvious close, dismiss, continue, accept, or X buttons.",
6
+ "Do not return done while a blocking overlay or dialog is still visible."
7
+ ].join(" ");
8
+ const COMPUTER_USE_RECOVERY_MODELS = {
9
+ anthropic: "claude-sonnet-4-6",
10
+ openai: "gpt-5.5"
11
+ };
12
+ const PAGE_UI_METHODS = /* @__PURE__ */ new Set([
13
+ "click",
14
+ "dblclick",
15
+ "tap",
16
+ "hover",
17
+ "fill",
18
+ "type",
19
+ "press",
20
+ "pressSequentially",
21
+ "check",
22
+ "uncheck",
23
+ "setChecked",
24
+ "selectOption",
25
+ "setInputFiles",
26
+ "selectText",
27
+ "dispatchEvent",
28
+ "focus",
29
+ "blur",
30
+ "dragAndDrop"
31
+ ]);
32
+ const PAGE_READ_METHODS = /* @__PURE__ */ new Set([
33
+ "title",
34
+ "content",
35
+ "screenshot",
36
+ "waitForLoadState",
37
+ "waitForRequest",
38
+ "waitForResponse",
39
+ "waitForURL"
40
+ ]);
41
+ const LOCATOR_UI_METHODS = /* @__PURE__ */ new Set([
42
+ "click",
43
+ "dblclick",
44
+ "tap",
45
+ "hover",
46
+ "fill",
47
+ "type",
48
+ "press",
49
+ "pressSequentially",
50
+ "check",
51
+ "uncheck",
52
+ "setChecked",
53
+ "selectOption",
54
+ "setInputFiles",
55
+ "selectText",
56
+ "dispatchEvent",
57
+ "focus",
58
+ "blur",
59
+ "clear",
60
+ "dragTo",
61
+ "scrollIntoViewIfNeeded"
62
+ ]);
63
+ const LOCATOR_READ_METHODS = /* @__PURE__ */ new Set([
64
+ "textContent",
65
+ "innerText",
66
+ "innerHTML",
67
+ "allTextContents",
68
+ "allInnerTexts",
69
+ "ariaSnapshot",
70
+ "boundingBox",
71
+ "count",
72
+ "getAttribute",
73
+ "inputValue",
74
+ "isChecked",
75
+ "isDisabled",
76
+ "isEditable",
77
+ "isEnabled",
78
+ "isVisible",
79
+ "isHidden",
80
+ "screenshot",
81
+ "waitFor"
82
+ ]);
83
+ const PAGE_LOCATOR_FACTORY_METHODS = /* @__PURE__ */ new Set([
84
+ "locator",
85
+ "getByRole",
86
+ "getByText",
87
+ "getByLabel",
88
+ "getByPlaceholder",
89
+ "getByAltText",
90
+ "getByTitle",
91
+ "getByTestId"
92
+ ]);
93
+ const LOCATOR_FACTORY_METHODS = /* @__PURE__ */ new Set([
94
+ "locator",
95
+ "getByRole",
96
+ "getByText",
97
+ "getByLabel",
98
+ "getByPlaceholder",
99
+ "getByAltText",
100
+ "getByTitle",
101
+ "getByTestId",
102
+ "filter",
103
+ "and",
104
+ "or",
105
+ "first",
106
+ "last",
107
+ "nth"
108
+ ]);
109
+ const FRAME_LOCATOR_FACTORY_METHODS = /* @__PURE__ */ new Set([
110
+ "locator",
111
+ "getByRole",
112
+ "getByText",
113
+ "getByLabel",
114
+ "getByPlaceholder",
115
+ "getByAltText",
116
+ "getByTitle",
117
+ "getByTestId",
118
+ "owner",
119
+ "first",
120
+ "last",
121
+ "nth",
122
+ "frameLocator"
123
+ ]);
124
+ function isUiMethod(targetType, method) {
125
+ return targetType === "page" ? PAGE_UI_METHODS.has(method) : LOCATOR_UI_METHODS.has(method);
126
+ }
127
+ function isReadMethod(targetType, method) {
128
+ return targetType === "page" ? PAGE_READ_METHODS.has(method) : LOCATOR_READ_METHODS.has(method);
129
+ }
130
+ function isSupportedMethod(targetType, method) {
131
+ return isUiMethod(targetType, method) || isReadMethod(targetType, method);
132
+ }
133
+ async function runWithFallback(args) {
134
+ try {
135
+ return await args.invoke();
136
+ } catch (originalError) {
137
+ const baseContext = {
138
+ page: args.page,
139
+ targetType: args.targetType,
140
+ method: args.method,
141
+ args: args.methodArgs
142
+ };
143
+ if (!isSupportedMethod(baseContext.targetType, baseContext.method)) {
144
+ throw originalError;
145
+ }
146
+ try {
147
+ await args.options.recoveryAction({
148
+ ...baseContext,
149
+ error: originalError
150
+ });
151
+ return await args.invoke();
152
+ } catch {
153
+ throw originalError;
154
+ }
155
+ }
156
+ }
157
+ function bindOrWrapLocatorMethod(locator, rawPage, method, value, options, caches) {
158
+ if (typeof value !== "function") return value;
159
+ if (LOCATOR_FACTORY_METHODS.has(method)) {
160
+ return (...args) => {
161
+ const nextLocator = value.apply(locator, args);
162
+ return createFallbackLocator(nextLocator, rawPage, options, caches);
163
+ };
164
+ }
165
+ if (method === "all") {
166
+ return async (...args) => {
167
+ const locators = await value.apply(locator, args);
168
+ return locators.map(
169
+ (nextLocator) => createFallbackLocator(nextLocator, rawPage, options, caches)
170
+ );
171
+ };
172
+ }
173
+ if (method === "contentFrame") {
174
+ return (...args) => {
175
+ const frameLocator = value.apply(locator, args);
176
+ return createFallbackFrameLocator(frameLocator, rawPage, options, caches);
177
+ };
178
+ }
179
+ if (!isSupportedMethod("locator", method)) {
180
+ return value.bind(locator);
181
+ }
182
+ return (...args) => runWithFallback({
183
+ page: rawPage,
184
+ targetType: "locator",
185
+ method,
186
+ methodArgs: args,
187
+ invoke: () => value.apply(locator, args),
188
+ options
189
+ });
190
+ }
191
+ function createFallbackLocator(locator, rawPage, options, caches) {
192
+ const cached = caches.locators.get(locator);
193
+ if (cached) return cached;
194
+ const proxy = new Proxy(locator, {
195
+ get(target, prop, receiver) {
196
+ if (typeof prop !== "string") {
197
+ return Reflect.get(target, prop, receiver);
198
+ }
199
+ return bindOrWrapLocatorMethod(
200
+ target,
201
+ rawPage,
202
+ prop,
203
+ Reflect.get(target, prop, target),
204
+ options,
205
+ caches
206
+ );
207
+ }
208
+ });
209
+ caches.locators.set(locator, proxy);
210
+ return proxy;
211
+ }
212
+ function createFallbackFrameLocator(frameLocator, rawPage, options, caches) {
213
+ const cached = caches.frameLocators.get(frameLocator);
214
+ if (cached) return cached;
215
+ const proxy = new Proxy(frameLocator, {
216
+ get(target, prop, receiver) {
217
+ if (typeof prop !== "string") {
218
+ return Reflect.get(target, prop, receiver);
219
+ }
220
+ const value = Reflect.get(target, prop, target);
221
+ if (typeof value !== "function") return value;
222
+ if (FRAME_LOCATOR_FACTORY_METHODS.has(prop)) {
223
+ return (...args) => {
224
+ const result = value.apply(target, args);
225
+ if (prop === "first" || prop === "last" || prop === "nth") {
226
+ return createFallbackFrameLocator(
227
+ result,
228
+ rawPage,
229
+ options,
230
+ caches
231
+ );
232
+ }
233
+ if (prop === "frameLocator") {
234
+ return createFallbackFrameLocator(
235
+ result,
236
+ rawPage,
237
+ options,
238
+ caches
239
+ );
240
+ }
241
+ return createFallbackLocator(
242
+ result,
243
+ rawPage,
244
+ options,
245
+ caches
246
+ );
247
+ };
248
+ }
249
+ return value.bind(target);
250
+ }
251
+ });
252
+ caches.frameLocators.set(frameLocator, proxy);
253
+ return proxy;
254
+ }
255
+ function createRecoveryPage(page, options) {
256
+ const caches = {
257
+ locators: /* @__PURE__ */ new WeakMap(),
258
+ frameLocators: /* @__PURE__ */ new WeakMap()
259
+ };
260
+ return new Proxy(page, {
261
+ get(target, prop, receiver) {
262
+ if (typeof prop !== "string") {
263
+ return Reflect.get(target, prop, receiver);
264
+ }
265
+ const value = Reflect.get(target, prop, target);
266
+ if (typeof value !== "function") return value;
267
+ if (PAGE_LOCATOR_FACTORY_METHODS.has(prop)) {
268
+ return (...args) => {
269
+ const locator = value.apply(target, args);
270
+ return createFallbackLocator(locator, page, options, caches);
271
+ };
272
+ }
273
+ if (prop === "frameLocator") {
274
+ return (...args) => {
275
+ const frameLocator = value.apply(target, args);
276
+ return createFallbackFrameLocator(frameLocator, page, options, caches);
277
+ };
278
+ }
279
+ if (!isSupportedMethod("page", prop)) {
280
+ return value.bind(target);
281
+ }
282
+ return (...args) => runWithFallback({
283
+ page,
284
+ targetType: "page",
285
+ method: prop,
286
+ methodArgs: args,
287
+ invoke: () => value.apply(target, args),
288
+ options
289
+ });
290
+ }
291
+ });
292
+ }
293
+ async function resolveComputerUseRecoveryModel(options) {
294
+ if ("provider" in options) {
295
+ if (options.provider === "openai") {
296
+ const model2 = options.model ?? COMPUTER_USE_RECOVERY_MODELS.openai;
297
+ if (model2 !== COMPUTER_USE_RECOVERY_MODELS.openai) {
298
+ throw new Error(
299
+ `Unsupported OpenAI computer use recovery model "${model2}". Supported model: ${COMPUTER_USE_RECOVERY_MODELS.openai}.`
300
+ );
301
+ }
302
+ return import("@ai-sdk/openai").then(
303
+ ({ createOpenAI }) => createOpenAI({ apiKey: options.apiKey })(model2)
304
+ );
305
+ }
306
+ const model = options.model ?? COMPUTER_USE_RECOVERY_MODELS.anthropic;
307
+ if (model !== COMPUTER_USE_RECOVERY_MODELS.anthropic) {
308
+ throw new Error(
309
+ `Unsupported Anthropic computer use recovery model "${model}". Supported model: ${COMPUTER_USE_RECOVERY_MODELS.anthropic}.`
310
+ );
311
+ }
312
+ return import("@ai-sdk/anthropic").then(
313
+ ({ createAnthropic }) => createAnthropic({ apiKey: options.apiKey })(model)
314
+ );
315
+ }
316
+ return options.languageModel;
317
+ }
318
+ function computerUseRecoveryAction(options) {
319
+ return async ({ page }) => {
320
+ const model = await resolveComputerUseRecoveryModel(options);
321
+ return executeRecoveryAgent(
322
+ page,
323
+ options.instruction,
324
+ void 0,
325
+ model,
326
+ options.maxSteps
327
+ );
328
+ };
329
+ }
330
+ function popupRecoveryAction(options) {
331
+ return computerUseRecoveryAction({
332
+ ...options,
333
+ instruction: POPUP_RECOVERY_INSTRUCTION
334
+ });
335
+ }
336
+ export {
337
+ COMPUTER_USE_RECOVERY_MODELS,
338
+ POPUP_RECOVERY_INSTRUCTION,
339
+ computerUseRecoveryAction,
340
+ createRecoveryPage,
341
+ popupRecoveryAction
342
+ };
@@ -1,2 +1,2 @@
1
- export { SESSION_STATE_VERSION, SessionAccessMode, SessionAccessModeSchema, SessionState, SessionStateFile, SessionStateFileSchema, SessionStatus, SessionStatusSchema, parseSessionStateContent, parseSessionStateData, serializeSessionState } from './session-state.js';
1
+ export { ProviderState, SESSION_STATE_VERSION, SessionAccessMode, SessionAccessModeSchema, SessionState, SessionStateFile, SessionStateFileSchema, SessionStatus, SessionStatusSchema, parseSessionStateContent, parseSessionStateData, serializeSessionState } from './session-state.js';
2
2
  import 'zod';