@morphllm/morphsdk 0.2.93 → 0.2.95

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 (135) hide show
  1. package/dist/chunk-2AMEQAO2.js +46 -0
  2. package/dist/chunk-2AMEQAO2.js.map +1 -0
  3. package/dist/{chunk-EI4UKP24.js → chunk-2HMEZZKK.js} +2 -2
  4. package/dist/{chunk-EI4UKP24.js.map → chunk-2HMEZZKK.js.map} +1 -1
  5. package/dist/chunk-2VERUKO2.js +177 -0
  6. package/dist/chunk-2VERUKO2.js.map +1 -0
  7. package/dist/{chunk-VHOWYK66.js → chunk-43LQLGP6.js} +23 -33
  8. package/dist/chunk-43LQLGP6.js.map +1 -0
  9. package/dist/{chunk-LMUZ3NGC.js → chunk-73RV6EXR.js} +2 -2
  10. package/dist/{chunk-PBLPZ6AU.js → chunk-7D6TXC7X.js} +2 -2
  11. package/dist/{chunk-GU6DACME.js → chunk-O7LDZA52.js} +2 -2
  12. package/dist/{chunk-5QIWYEHJ.js → chunk-PE4KGDA6.js} +1 -8
  13. package/dist/chunk-PE4KGDA6.js.map +1 -0
  14. package/dist/{chunk-SQN4DUQS.js → chunk-Q6Y4R236.js} +26 -2
  15. package/dist/chunk-Q6Y4R236.js.map +1 -0
  16. package/dist/{chunk-PUGIOVSP.js → chunk-QAT5UVPX.js} +2 -2
  17. package/dist/{chunk-MIIJWDOQ.js → chunk-QJP62BXH.js} +166 -71
  18. package/dist/chunk-QJP62BXH.js.map +1 -0
  19. package/dist/{chunk-EYGBUH2R.js → chunk-R7IQWNSA.js} +8 -8
  20. package/dist/chunk-R7IQWNSA.js.map +1 -0
  21. package/dist/chunk-SI2CKRKJ.js +389 -0
  22. package/dist/chunk-SI2CKRKJ.js.map +1 -0
  23. package/dist/{chunk-4WLGDYWQ.js → chunk-TSENDJQI.js} +6 -6
  24. package/dist/chunk-TSENDJQI.js.map +1 -0
  25. package/dist/{chunk-IUG2FHNN.js → chunk-XH7P7HVT.js} +1 -8
  26. package/dist/chunk-XH7P7HVT.js.map +1 -0
  27. package/dist/{chunk-FNLNDMIX.js → chunk-YZ5NCWO2.js} +6 -6
  28. package/dist/chunk-YZ5NCWO2.js.map +1 -0
  29. package/dist/{chunk-IJ54DTJ3.js → chunk-ZYTAKEBW.js} +13 -13
  30. package/dist/client.cjs +770 -110
  31. package/dist/client.cjs.map +1 -1
  32. package/dist/client.d.ts +2 -0
  33. package/dist/client.js +16 -13
  34. package/dist/index.cjs +770 -110
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +16 -13
  38. package/dist/tools/browser/anthropic.cjs +58 -23
  39. package/dist/tools/browser/anthropic.cjs.map +1 -1
  40. package/dist/tools/browser/anthropic.js +7 -4
  41. package/dist/tools/browser/core.cjs +750 -70
  42. package/dist/tools/browser/core.cjs.map +1 -1
  43. package/dist/tools/browser/core.d.ts +30 -24
  44. package/dist/tools/browser/core.js +5 -2
  45. package/dist/tools/browser/errors.cjs +208 -0
  46. package/dist/tools/browser/errors.cjs.map +1 -0
  47. package/dist/tools/browser/errors.d.ts +158 -0
  48. package/dist/tools/browser/errors.js +22 -0
  49. package/dist/tools/browser/errors.js.map +1 -0
  50. package/dist/tools/browser/index.cjs +783 -85
  51. package/dist/tools/browser/index.cjs.map +1 -1
  52. package/dist/tools/browser/index.d.ts +5 -2
  53. package/dist/tools/browser/index.js +32 -9
  54. package/dist/tools/browser/index.js.map +1 -1
  55. package/dist/tools/browser/live.cjs +25 -1
  56. package/dist/tools/browser/live.cjs.map +1 -1
  57. package/dist/tools/browser/live.js +1 -1
  58. package/dist/tools/browser/openai.cjs +58 -23
  59. package/dist/tools/browser/openai.cjs.map +1 -1
  60. package/dist/tools/browser/openai.js +7 -4
  61. package/dist/tools/browser/profiles/core.cjs +670 -0
  62. package/dist/tools/browser/profiles/core.cjs.map +1 -0
  63. package/dist/tools/browser/profiles/core.d.ts +187 -0
  64. package/dist/tools/browser/profiles/core.js +29 -0
  65. package/dist/tools/browser/profiles/core.js.map +1 -0
  66. package/dist/tools/browser/profiles/index.cjs +670 -0
  67. package/dist/tools/browser/profiles/index.cjs.map +1 -0
  68. package/dist/tools/browser/profiles/index.d.ts +4 -0
  69. package/dist/tools/browser/profiles/index.js +29 -0
  70. package/dist/tools/browser/profiles/index.js.map +1 -0
  71. package/dist/tools/browser/profiles/types.cjs +74 -0
  72. package/dist/tools/browser/profiles/types.cjs.map +1 -0
  73. package/dist/tools/browser/profiles/types.d.ts +195 -0
  74. package/dist/tools/browser/profiles/types.js +16 -0
  75. package/dist/tools/browser/profiles/types.js.map +1 -0
  76. package/dist/tools/browser/prompts.cjs +1 -1
  77. package/dist/tools/browser/prompts.cjs.map +1 -1
  78. package/dist/tools/browser/prompts.d.ts +1 -1
  79. package/dist/tools/browser/prompts.js +1 -1
  80. package/dist/tools/browser/types.cjs.map +1 -1
  81. package/dist/tools/browser/types.d.ts +55 -51
  82. package/dist/tools/browser/vercel.cjs +60 -25
  83. package/dist/tools/browser/vercel.cjs.map +1 -1
  84. package/dist/tools/browser/vercel.d.ts +1 -1
  85. package/dist/tools/browser/vercel.js +7 -4
  86. package/dist/tools/fastapply/anthropic.cjs +0 -7
  87. package/dist/tools/fastapply/anthropic.cjs.map +1 -1
  88. package/dist/tools/fastapply/anthropic.js +1 -1
  89. package/dist/tools/fastapply/index.cjs +0 -14
  90. package/dist/tools/fastapply/index.cjs.map +1 -1
  91. package/dist/tools/fastapply/index.js +5 -5
  92. package/dist/tools/fastapply/openai.cjs +0 -7
  93. package/dist/tools/fastapply/openai.cjs.map +1 -1
  94. package/dist/tools/fastapply/openai.js +1 -1
  95. package/dist/tools/index.cjs +0 -14
  96. package/dist/tools/index.cjs.map +1 -1
  97. package/dist/tools/index.js +5 -5
  98. package/dist/tools/warp_grep/agent/runner.cjs +18 -98
  99. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  100. package/dist/tools/warp_grep/agent/runner.js +2 -3
  101. package/dist/tools/warp_grep/anthropic.cjs +18 -98
  102. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  103. package/dist/tools/warp_grep/anthropic.js +8 -9
  104. package/dist/tools/warp_grep/client.cjs +18 -98
  105. package/dist/tools/warp_grep/client.cjs.map +1 -1
  106. package/dist/tools/warp_grep/client.js +5 -6
  107. package/dist/tools/warp_grep/gemini.cjs +18 -98
  108. package/dist/tools/warp_grep/gemini.cjs.map +1 -1
  109. package/dist/tools/warp_grep/gemini.js +7 -8
  110. package/dist/tools/warp_grep/gemini.js.map +1 -1
  111. package/dist/tools/warp_grep/harness.js +10 -10
  112. package/dist/tools/warp_grep/index.cjs +18 -98
  113. package/dist/tools/warp_grep/index.cjs.map +1 -1
  114. package/dist/tools/warp_grep/index.js +8 -9
  115. package/dist/tools/warp_grep/openai.cjs +18 -98
  116. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  117. package/dist/tools/warp_grep/openai.js +8 -9
  118. package/dist/tools/warp_grep/vercel.cjs +18 -98
  119. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  120. package/dist/tools/warp_grep/vercel.js +8 -9
  121. package/dist/{vercel-CsnNSdze.d.ts → vercel-CVF27qFK.d.ts} +10 -10
  122. package/package.json +7 -2
  123. package/dist/chunk-4WLGDYWQ.js.map +0 -1
  124. package/dist/chunk-5QIWYEHJ.js.map +0 -1
  125. package/dist/chunk-EYGBUH2R.js.map +0 -1
  126. package/dist/chunk-FNLNDMIX.js.map +0 -1
  127. package/dist/chunk-IUG2FHNN.js.map +0 -1
  128. package/dist/chunk-MIIJWDOQ.js.map +0 -1
  129. package/dist/chunk-SQN4DUQS.js.map +0 -1
  130. package/dist/chunk-VHOWYK66.js.map +0 -1
  131. /package/dist/{chunk-LMUZ3NGC.js.map → chunk-73RV6EXR.js.map} +0 -0
  132. /package/dist/{chunk-PBLPZ6AU.js.map → chunk-7D6TXC7X.js.map} +0 -0
  133. /package/dist/{chunk-GU6DACME.js.map → chunk-O7LDZA52.js.map} +0 -0
  134. /package/dist/{chunk-PUGIOVSP.js.map → chunk-QAT5UVPX.js.map} +0 -0
  135. /package/dist/{chunk-IJ54DTJ3.js.map → chunk-ZYTAKEBW.js.map} +0 -0
@@ -24,6 +24,14 @@ __export(browser_exports, {
24
24
  BROWSER_TOOL_DESCRIPTION: () => BROWSER_TOOL_DESCRIPTION,
25
25
  BrowserClient: () => BrowserClient,
26
26
  LIVE_PRESETS: () => LIVE_PRESETS,
27
+ MorphAPIError: () => MorphAPIError,
28
+ MorphAuthenticationError: () => MorphAuthenticationError,
29
+ MorphError: () => MorphError,
30
+ MorphNotFoundError: () => MorphNotFoundError,
31
+ MorphProfileLimitError: () => MorphProfileLimitError,
32
+ MorphRateLimitError: () => MorphRateLimitError,
33
+ MorphValidationError: () => MorphValidationError,
34
+ ProfilesClient: () => ProfilesClient,
27
35
  anthropic: () => anthropic_exports,
28
36
  buildEmbedCode: () => buildEmbedCode,
29
37
  buildLiveIframe: () => buildLiveIframe,
@@ -36,6 +44,7 @@ __export(browser_exports, {
36
44
  getRecording: () => getRecording,
37
45
  getWebp: () => getWebp,
38
46
  openai: () => openai_exports,
47
+ parseAPIError: () => parseAPIError,
39
48
  vercel: () => vercel_exports,
40
49
  waitForRecording: () => waitForRecording
41
50
  });
@@ -130,7 +139,8 @@ function buildLiveUrl(debugUrl, options = {}) {
130
139
  "debugUrl is required. Ensure your backend returns debugUrl in the task response. Contact support@morphllm.com if you need help."
131
140
  );
132
141
  }
133
- const url = new URL(debugUrl);
142
+ const normalized = normalizeLiveUrl(debugUrl);
143
+ const url = new URL(normalized);
134
144
  if (options.interactive !== void 0) {
135
145
  url.searchParams.set("interactive", String(options.interactive));
136
146
  }
@@ -148,6 +158,29 @@ function buildLiveUrl(debugUrl, options = {}) {
148
158
  }
149
159
  return url.toString();
150
160
  }
161
+ function normalizeLiveUrl(debugUrl) {
162
+ const trimmed = debugUrl.trim();
163
+ if (!trimmed) {
164
+ return trimmed;
165
+ }
166
+ if (trimmed.startsWith("wss://") || trimmed.startsWith("ws://")) {
167
+ return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
168
+ }
169
+ let url;
170
+ try {
171
+ url = new URL(trimmed);
172
+ } catch {
173
+ return trimmed;
174
+ }
175
+ if (url.protocol === "wss:" || url.protocol === "ws:") {
176
+ return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
177
+ }
178
+ const wssParam = url.searchParams.get("wss");
179
+ if (wssParam && (wssParam.startsWith("wss://") || wssParam.startsWith("ws://"))) {
180
+ url.searchParams.set("wss", wssParam);
181
+ }
182
+ return url.toString();
183
+ }
151
184
  function buildLiveIframe(debugUrl, options = {}) {
152
185
  const {
153
186
  width = "100%",
@@ -191,6 +224,570 @@ function resolvePreset(optionsOrPreset) {
191
224
  return optionsOrPreset;
192
225
  }
193
226
 
227
+ // tools/browser/errors.ts
228
+ var MorphError = class extends Error {
229
+ /** Error code for programmatic handling */
230
+ code;
231
+ /** Original cause of the error, if any */
232
+ cause;
233
+ constructor(message, code, cause) {
234
+ super(message);
235
+ this.name = "MorphError";
236
+ this.code = code;
237
+ this.cause = cause;
238
+ if (Error.captureStackTrace) {
239
+ Error.captureStackTrace(this, this.constructor);
240
+ }
241
+ }
242
+ /**
243
+ * Returns a JSON representation of the error for logging.
244
+ */
245
+ toJSON() {
246
+ return {
247
+ name: this.name,
248
+ message: this.message,
249
+ code: this.code,
250
+ cause: this.cause?.message
251
+ };
252
+ }
253
+ };
254
+ var MorphValidationError = class extends MorphError {
255
+ /** The field that failed validation */
256
+ field;
257
+ constructor(message, field) {
258
+ super(message, "validation_error");
259
+ this.name = "MorphValidationError";
260
+ this.field = field;
261
+ }
262
+ toJSON() {
263
+ return {
264
+ ...super.toJSON(),
265
+ field: this.field
266
+ };
267
+ }
268
+ };
269
+ var MorphAPIError = class extends MorphError {
270
+ /** HTTP status code */
271
+ statusCode;
272
+ /** Request ID for debugging (if available) */
273
+ requestId;
274
+ /** Raw response body */
275
+ rawResponse;
276
+ constructor(message, code, statusCode, options) {
277
+ super(message, code, options?.cause);
278
+ this.name = "MorphAPIError";
279
+ this.statusCode = statusCode;
280
+ this.requestId = options?.requestId;
281
+ this.rawResponse = options?.rawResponse;
282
+ }
283
+ toJSON() {
284
+ return {
285
+ ...super.toJSON(),
286
+ statusCode: this.statusCode,
287
+ requestId: this.requestId
288
+ };
289
+ }
290
+ };
291
+ var MorphAuthenticationError = class extends MorphAPIError {
292
+ constructor(message = "Authentication required. Please provide a valid API key.") {
293
+ super(message, "authentication_required", 401);
294
+ this.name = "MorphAuthenticationError";
295
+ }
296
+ };
297
+ var MorphRateLimitError = class extends MorphAPIError {
298
+ /** When the rate limit resets (Unix timestamp) */
299
+ resetAt;
300
+ /** Number of seconds until reset */
301
+ retryAfter;
302
+ constructor(message = "Rate limit exceeded. Please retry later.", options) {
303
+ super(message, "rate_limit_exceeded", 429, { requestId: options?.requestId });
304
+ this.name = "MorphRateLimitError";
305
+ this.resetAt = options?.resetAt;
306
+ this.retryAfter = options?.retryAfter;
307
+ }
308
+ toJSON() {
309
+ return {
310
+ ...super.toJSON(),
311
+ resetAt: this.resetAt,
312
+ retryAfter: this.retryAfter
313
+ };
314
+ }
315
+ };
316
+ var MorphNotFoundError = class extends MorphAPIError {
317
+ /** The type of resource that was not found */
318
+ resourceType;
319
+ /** The ID of the resource that was not found */
320
+ resourceId;
321
+ constructor(resourceType, resourceId) {
322
+ const message = resourceId ? `${resourceType} '${resourceId}' not found` : `${resourceType} not found`;
323
+ super(message, "resource_not_found", 404);
324
+ this.name = "MorphNotFoundError";
325
+ this.resourceType = resourceType;
326
+ this.resourceId = resourceId;
327
+ }
328
+ toJSON() {
329
+ return {
330
+ ...super.toJSON(),
331
+ resourceType: this.resourceType,
332
+ resourceId: this.resourceId
333
+ };
334
+ }
335
+ };
336
+ var MorphProfileLimitError = class extends MorphAPIError {
337
+ /** Current number of profiles */
338
+ currentCount;
339
+ /** Maximum allowed profiles for the plan */
340
+ maxAllowed;
341
+ constructor(message = "Profile limit exceeded for your plan.", options) {
342
+ super(message, "profile_limit_exceeded", 403, { requestId: options?.requestId });
343
+ this.name = "MorphProfileLimitError";
344
+ this.currentCount = options?.currentCount;
345
+ this.maxAllowed = options?.maxAllowed;
346
+ }
347
+ toJSON() {
348
+ return {
349
+ ...super.toJSON(),
350
+ currentCount: this.currentCount,
351
+ maxAllowed: this.maxAllowed
352
+ };
353
+ }
354
+ };
355
+ function parseAPIError(statusCode, responseText, requestId) {
356
+ let errorData = {};
357
+ try {
358
+ errorData = JSON.parse(responseText);
359
+ } catch {
360
+ }
361
+ const message = errorData.detail || errorData.message || responseText || "Unknown error";
362
+ const code = errorData.code;
363
+ switch (statusCode) {
364
+ case 401:
365
+ return new MorphAuthenticationError(message);
366
+ case 403:
367
+ if (code === "profile_limit_exceeded" || message.toLowerCase().includes("limit")) {
368
+ return new MorphProfileLimitError(message, { requestId });
369
+ }
370
+ return new MorphAPIError(message, "insufficient_permissions", statusCode, { requestId, rawResponse: responseText });
371
+ case 404:
372
+ if (message.toLowerCase().includes("profile")) {
373
+ return new MorphNotFoundError("Profile", void 0);
374
+ }
375
+ if (message.toLowerCase().includes("session")) {
376
+ return new MorphNotFoundError("Session", void 0);
377
+ }
378
+ return new MorphAPIError(message, "resource_not_found", statusCode, { requestId, rawResponse: responseText });
379
+ case 429:
380
+ return new MorphRateLimitError(message, { requestId });
381
+ case 422:
382
+ return new MorphAPIError(message, "validation_error", statusCode, { requestId, rawResponse: responseText });
383
+ case 500:
384
+ case 502:
385
+ case 503:
386
+ case 504:
387
+ return new MorphAPIError(message, "service_unavailable", statusCode, { requestId, rawResponse: responseText });
388
+ default:
389
+ return new MorphAPIError(message, "network_error", statusCode, { requestId, rawResponse: responseText });
390
+ }
391
+ }
392
+
393
+ // tools/browser/profiles/types.ts
394
+ function transformProfile(api) {
395
+ return {
396
+ id: api.id,
397
+ name: api.name,
398
+ repoId: api.repo_id,
399
+ cookieDomains: api.cookie_domains,
400
+ lastUsedAt: api.last_used_at,
401
+ createdAt: api.created_at,
402
+ updatedAt: api.updated_at
403
+ };
404
+ }
405
+ function transformCreateInput(input) {
406
+ return {
407
+ name: input.name,
408
+ repo_id: input.repoId
409
+ };
410
+ }
411
+ function transformSession(api) {
412
+ return {
413
+ sessionId: api.session_id,
414
+ debugUrl: api.debug_url || ""
415
+ };
416
+ }
417
+ function transformSaveInput(input) {
418
+ return {
419
+ session_id: input.sessionId,
420
+ profile_id: input.profileId
421
+ };
422
+ }
423
+ function transformStateResponse(api) {
424
+ return {
425
+ profileId: api.profile_id,
426
+ stateUrl: api.state_url,
427
+ expiresIn: api.expires_in
428
+ };
429
+ }
430
+
431
+ // tools/browser/profiles/core.ts
432
+ var DEFAULT_API_URL = process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com";
433
+ var ProfilesClient = class {
434
+ config;
435
+ constructor(config) {
436
+ this.config = config;
437
+ }
438
+ /**
439
+ * Create a new browser profile and immediately start a live session.
440
+ *
441
+ * @param input - Profile creation parameters
442
+ * @returns Profile setup handle with live URL + save()
443
+ * @throws {MorphValidationError} If input validation fails
444
+ * @throws {MorphProfileLimitError} If profile limit is exceeded
445
+ * @throws {MorphAuthenticationError} If API key is missing or invalid
446
+ *
447
+ * @example
448
+ * ```typescript
449
+ * const setup = await morph.browser.profiles.createProfile({
450
+ * name: 'LinkedIn Production',
451
+ * repoId: 'owner/repo'
452
+ * });
453
+ * console.log(setup.session.debugUrl);
454
+ * await setup.save();
455
+ * ```
456
+ */
457
+ async createProfile(input) {
458
+ return createProfile(input, this.config);
459
+ }
460
+ /**
461
+ * List all profiles for the authenticated user.
462
+ *
463
+ * @param repoId - Optional repository ID to filter by
464
+ * @returns Array of profiles
465
+ *
466
+ * @example
467
+ * ```typescript
468
+ * // List all profiles
469
+ * const allProfiles = await morph.browser.profiles.listProfiles();
470
+ *
471
+ * // List profiles for a specific repo
472
+ * const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');
473
+ * ```
474
+ */
475
+ async listProfiles(repoId) {
476
+ return listProfiles(this.config, repoId);
477
+ }
478
+ /**
479
+ * Get a profile by ID with convenience methods.
480
+ *
481
+ * @param id - Profile ID
482
+ * @returns Profile with attached methods
483
+ * @throws {MorphNotFoundError} If profile is not found
484
+ *
485
+ * @example
486
+ * ```typescript
487
+ * const profile = await morph.browser.profiles.getProfile('profile-id');
488
+ * const state = await profile.getState();
489
+ * await profile.delete();
490
+ * ```
491
+ */
492
+ async getProfile(id) {
493
+ return getProfile(id, this.config);
494
+ }
495
+ /**
496
+ * Update a profile by opening a live session (no rename).
497
+ *
498
+ * @param id - Profile ID
499
+ * @returns Profile setup handle with live URL + save()
500
+ */
501
+ async updateProfile(id) {
502
+ return updateProfile(id, this.config);
503
+ }
504
+ /**
505
+ * Delete a profile.
506
+ *
507
+ * @param id - Profile ID
508
+ * @throws {MorphNotFoundError} If profile is not found
509
+ */
510
+ async deleteProfile(id) {
511
+ return deleteProfile(id, this.config);
512
+ }
513
+ /**
514
+ * Start a browser session for profile setup.
515
+ *
516
+ * Returns a live URL where the user can sign into accounts.
517
+ * After signing in, call `saveSession` to persist the state.
518
+ *
519
+ * @param input - Optional session parameters
520
+ * @returns Session with debug URL
521
+ *
522
+ * @example
523
+ * ```typescript
524
+ * const session = await morph.browser.profiles.startSession();
525
+ * console.log('Sign in at:', session.debugUrl);
526
+ * // Open debugUrl in browser, user signs in...
527
+ * await morph.browser.profiles.saveSession(session.sessionId, profile.id);
528
+ * ```
529
+ */
530
+ async startSession(input) {
531
+ return startProfileSession(this.config, input);
532
+ }
533
+ /**
534
+ * Save browser state from a session to a profile.
535
+ *
536
+ * Call this after the user is done signing into accounts.
537
+ * Extracts cookies, localStorage, and sessionStorage.
538
+ *
539
+ * @param sessionId - Browser session ID from startSession
540
+ * @param profileId - Profile ID to save state to
541
+ * @returns Updated profile with cookie domains
542
+ */
543
+ async saveSession(sessionId, profileId) {
544
+ return saveProfileSession({ sessionId, profileId }, this.config);
545
+ }
546
+ /**
547
+ * List available repo IDs (discovery).
548
+ *
549
+ * @returns Repo summaries with profile counts
550
+ */
551
+ async listRepos() {
552
+ return listRepos(this.config);
553
+ }
554
+ /**
555
+ * Get the presigned URL for a profile's state.
556
+ *
557
+ * Use this to download the raw state JSON for debugging
558
+ * or to restore state manually.
559
+ *
560
+ * @param profileId - Profile ID
561
+ * @returns State URL with expiry information
562
+ */
563
+ async getProfileState(profileId) {
564
+ return getProfileState(profileId, this.config);
565
+ }
566
+ };
567
+ function validateCreateInput(input) {
568
+ if (!input.name || typeof input.name !== "string") {
569
+ throw new MorphValidationError("name is required", "name");
570
+ }
571
+ const trimmedName = input.name.trim();
572
+ if (trimmedName.length === 0) {
573
+ throw new MorphValidationError("name cannot be empty", "name");
574
+ }
575
+ if (trimmedName.length > 100) {
576
+ throw new MorphValidationError("name must be 100 characters or less", "name");
577
+ }
578
+ if (!input.repoId || typeof input.repoId !== "string") {
579
+ throw new MorphValidationError("repoId is required", "repoId");
580
+ }
581
+ if (input.repoId.trim().length === 0) {
582
+ throw new MorphValidationError("repoId cannot be empty", "repoId");
583
+ }
584
+ }
585
+ function validateId(id, fieldName) {
586
+ if (!id || typeof id !== "string") {
587
+ throw new MorphValidationError(`${fieldName} is required`, fieldName);
588
+ }
589
+ if (id.trim().length === 0) {
590
+ throw new MorphValidationError(`${fieldName} cannot be empty`, fieldName);
591
+ }
592
+ }
593
+ async function createProfile(input, config = {}) {
594
+ validateCreateInput(input);
595
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
596
+ const headers = buildHeaders(config);
597
+ const response = await fetchWithRetry(
598
+ `${apiUrl}/profiles`,
599
+ {
600
+ method: "POST",
601
+ headers,
602
+ body: JSON.stringify(transformCreateInput(input))
603
+ },
604
+ config.retryConfig
605
+ );
606
+ if (!response.ok) {
607
+ const errorText = await response.text().catch(() => response.statusText);
608
+ const requestId = response.headers.get("x-request-id") || void 0;
609
+ throw parseAPIError(response.status, errorText, requestId);
610
+ }
611
+ const apiProfile = await response.json();
612
+ const profile = transformProfile(apiProfile);
613
+ const session = await startProfileSession(config, { profileId: profile.id });
614
+ return buildProfileSetup(profile, session, config);
615
+ }
616
+ async function listProfiles(config = {}, repoId) {
617
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
618
+ const headers = buildHeaders(config);
619
+ const url = repoId ? `${apiUrl}/profiles?repo_id=${encodeURIComponent(repoId)}` : `${apiUrl}/profiles`;
620
+ const response = await fetchWithRetry(
621
+ url,
622
+ { method: "GET", headers },
623
+ config.retryConfig
624
+ );
625
+ if (!response.ok) {
626
+ const errorText = await response.text().catch(() => response.statusText);
627
+ const requestId = response.headers.get("x-request-id") || void 0;
628
+ throw parseAPIError(response.status, errorText, requestId);
629
+ }
630
+ const data = await response.json();
631
+ return data.profiles.map(transformProfile);
632
+ }
633
+ async function getProfile(id, config = {}) {
634
+ validateId(id, "id");
635
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
636
+ const headers = buildHeaders(config);
637
+ const response = await fetchWithRetry(
638
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
639
+ { method: "GET", headers },
640
+ config.retryConfig
641
+ );
642
+ if (!response.ok) {
643
+ const errorText = await response.text().catch(() => response.statusText);
644
+ const requestId = response.headers.get("x-request-id") || void 0;
645
+ throw parseAPIError(response.status, errorText, requestId);
646
+ }
647
+ const apiProfile = await response.json();
648
+ const profile = transformProfile(apiProfile);
649
+ return {
650
+ ...profile,
651
+ getState: () => getProfileState(id, config),
652
+ delete: () => deleteProfile(id, config)
653
+ };
654
+ }
655
+ async function updateProfile(id, config = {}) {
656
+ validateId(id, "id");
657
+ const profile = await fetchProfile(id, config);
658
+ const session = await startProfileSession(config, { profileId: profile.id });
659
+ return buildProfileSetup(profile, session, config);
660
+ }
661
+ async function deleteProfile(id, config = {}) {
662
+ validateId(id, "id");
663
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
664
+ const headers = buildHeaders(config);
665
+ const response = await fetchWithRetry(
666
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
667
+ { method: "DELETE", headers },
668
+ config.retryConfig
669
+ );
670
+ if (!response.ok) {
671
+ const errorText = await response.text().catch(() => response.statusText);
672
+ const requestId = response.headers.get("x-request-id") || void 0;
673
+ throw parseAPIError(response.status, errorText, requestId);
674
+ }
675
+ }
676
+ async function startProfileSession(config = {}, input) {
677
+ if (!config.apiKey) {
678
+ throw new MorphAuthenticationError();
679
+ }
680
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
681
+ const headers = buildHeaders(config);
682
+ const body = input?.profileId ? { profile_id: input.profileId } : {};
683
+ const response = await fetchWithRetry(
684
+ `${apiUrl}/profiles/session/start`,
685
+ {
686
+ method: "POST",
687
+ headers,
688
+ body: JSON.stringify(body)
689
+ },
690
+ config.retryConfig
691
+ );
692
+ if (!response.ok) {
693
+ const errorText = await response.text().catch(() => response.statusText);
694
+ const requestId = response.headers.get("x-request-id") || void 0;
695
+ throw parseAPIError(response.status, errorText, requestId);
696
+ }
697
+ const apiSession = await response.json();
698
+ return transformSession(apiSession);
699
+ }
700
+ async function saveProfileSession(input, config = {}) {
701
+ validateId(input.sessionId, "sessionId");
702
+ validateId(input.profileId, "profileId");
703
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
704
+ const headers = buildHeaders(config);
705
+ const response = await fetchWithRetry(
706
+ `${apiUrl}/profiles/session/save`,
707
+ {
708
+ method: "POST",
709
+ headers,
710
+ body: JSON.stringify(transformSaveInput(input))
711
+ },
712
+ config.retryConfig
713
+ );
714
+ if (!response.ok) {
715
+ const errorText = await response.text().catch(() => response.statusText);
716
+ const requestId = response.headers.get("x-request-id") || void 0;
717
+ throw parseAPIError(response.status, errorText, requestId);
718
+ }
719
+ const apiProfile = await response.json();
720
+ return transformProfile(apiProfile);
721
+ }
722
+ async function listRepos(config = {}) {
723
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
724
+ const headers = buildHeaders(config);
725
+ const response = await fetchWithRetry(
726
+ `${apiUrl}/repos`,
727
+ { method: "GET", headers },
728
+ config.retryConfig
729
+ );
730
+ if (!response.ok) {
731
+ const errorText = await response.text().catch(() => response.statusText);
732
+ const requestId = response.headers.get("x-request-id") || void 0;
733
+ throw parseAPIError(response.status, errorText, requestId);
734
+ }
735
+ const data = await response.json();
736
+ const repos = Array.isArray(data?.repos) ? data.repos : [];
737
+ return repos.map((repo) => ({
738
+ repoId: repo.repo_id,
739
+ repoFullName: repo.repo_full_name,
740
+ profileCount: repo.profile_count ?? 0
741
+ }));
742
+ }
743
+ async function getProfileState(profileId, config = {}) {
744
+ validateId(profileId, "profileId");
745
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
746
+ const headers = buildHeaders(config);
747
+ const response = await fetchWithRetry(
748
+ `${apiUrl}/profiles/${encodeURIComponent(profileId)}/state`,
749
+ { method: "GET", headers },
750
+ config.retryConfig
751
+ );
752
+ if (!response.ok) {
753
+ const errorText = await response.text().catch(() => response.statusText);
754
+ const requestId = response.headers.get("x-request-id") || void 0;
755
+ throw parseAPIError(response.status, errorText, requestId);
756
+ }
757
+ const apiState = await response.json();
758
+ return transformStateResponse(apiState);
759
+ }
760
+ function buildHeaders(config) {
761
+ const headers = { "Content-Type": "application/json" };
762
+ if (config.apiKey) {
763
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
764
+ }
765
+ return headers;
766
+ }
767
+ async function fetchProfile(id, config) {
768
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
769
+ const headers = buildHeaders(config);
770
+ const response = await fetchWithRetry(
771
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
772
+ { method: "GET", headers },
773
+ config.retryConfig
774
+ );
775
+ if (!response.ok) {
776
+ const errorText = await response.text().catch(() => response.statusText);
777
+ const requestId = response.headers.get("x-request-id") || void 0;
778
+ throw parseAPIError(response.status, errorText, requestId);
779
+ }
780
+ const apiProfile = await response.json();
781
+ return transformProfile(apiProfile);
782
+ }
783
+ function buildProfileSetup(profile, session, config) {
784
+ return {
785
+ profile,
786
+ session,
787
+ save: () => saveProfileSession({ sessionId: session.sessionId, profileId: profile.id }, config)
788
+ };
789
+ }
790
+
194
791
  // tools/browser/core.ts
195
792
  var DEFAULT_CONFIG = {
196
793
  apiUrl: process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com",
@@ -200,11 +797,16 @@ var DEFAULT_CONFIG = {
200
797
  };
201
798
  var BrowserClient = class {
202
799
  config;
800
+ /**
801
+ * Profile management - create and manage browser profiles for storing login state.
802
+ */
803
+ profiles;
203
804
  constructor(config = {}) {
204
805
  this.config = {
205
806
  ...DEFAULT_CONFIG,
206
807
  ...config
207
808
  };
809
+ this.profiles = new ProfilesClient(this.config);
208
810
  }
209
811
  /**
210
812
  * Execute a browser automation task
@@ -227,19 +829,21 @@ var BrowserClient = class {
227
829
  body: JSON.stringify({
228
830
  task: input.task,
229
831
  url: input.url,
230
- max_steps: input.max_steps ?? 10,
832
+ max_steps: input.maxSteps ?? 10,
231
833
  model: input.model ?? "morph-computer-use-v0",
232
- viewport_width: input.viewport_width ?? 1280,
233
- viewport_height: input.viewport_height ?? 720,
234
- external_id: input.external_id,
235
- repo_id: input.repo_id,
236
- commit_id: input.commit_id,
237
- record_video: input.record_video ?? false,
238
- video_width: input.video_width ?? input.viewport_width ?? 1280,
239
- video_height: input.video_height ?? input.viewport_height ?? 720,
240
- allow_resizing: input.allow_resizing ?? false,
834
+ viewport_width: input.viewportWidth ?? 1280,
835
+ viewport_height: input.viewportHeight ?? 720,
836
+ external_id: input.externalId,
837
+ repo_id: input.repoId,
838
+ repo_full_name: input.repoFullName,
839
+ commit_id: input.commitId,
840
+ record_video: input.recordVideo ?? false,
841
+ video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
842
+ video_height: input.videoHeight ?? input.viewportHeight ?? 720,
843
+ allow_resizing: input.allowResizing ?? false,
241
844
  structured_output: "schema" in input ? stringifyStructuredOutput(input.schema) : void 0,
242
- auth: input.auth
845
+ auth: input.auth,
846
+ profile_id: input.profileId
243
847
  })
244
848
  });
245
849
  if (!response.ok) {
@@ -247,9 +851,10 @@ var BrowserClient = class {
247
851
  if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
248
852
  throw new Error(`HTTP ${response.status}: ${errorText}`);
249
853
  }
250
- const result = await response.json();
854
+ const result = mapTaskResult(await response.json());
251
855
  if (debug) {
252
- console.log(`[Browser] \u2705 Task created: recording_id=${result.recording_id ?? "none"} debug_url=${result.debugUrl ? "available" : "none"}`);
856
+ const debugUrl = result.debugUrl;
857
+ console.log(`[Browser] \u2705 Task created: recordingId=${result.recordingId ?? "none"} debugUrl=${debugUrl ? "available" : "none"}`);
253
858
  }
254
859
  if ("schema" in input) {
255
860
  return wrapTaskResponseWithSchema(result, this.config, input.schema);
@@ -304,15 +909,15 @@ async function executeBrowserTask(input, config = {}) {
304
909
  error: 'Task description is required. Example: "Go to example.com and click the login button"'
305
910
  };
306
911
  }
307
- if (input.max_steps !== void 0 && (input.max_steps < 1 || input.max_steps > 50)) {
912
+ if (input.maxSteps !== void 0 && (input.maxSteps < 1 || input.maxSteps > 50)) {
308
913
  return {
309
914
  success: false,
310
- error: "max_steps must be between 1 and 50. Use more steps for complex multi-page flows."
915
+ error: "maxSteps must be between 1 and 50. Use more steps for complex multi-page flows."
311
916
  };
312
917
  }
313
918
  if (debug) {
314
- console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.max_steps ?? 10}`);
315
- console.log(`[Browser] Recording: ${input.record_video ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
919
+ console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.maxSteps ?? 10}`);
920
+ console.log(`[Browser] Recording: ${input.recordVideo ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
316
921
  }
317
922
  const startTime = Date.now();
318
923
  try {
@@ -326,19 +931,20 @@ async function executeBrowserTask(input, config = {}) {
326
931
  body: JSON.stringify({
327
932
  task: input.task,
328
933
  url: input.url,
329
- max_steps: input.max_steps ?? 10,
934
+ max_steps: input.maxSteps ?? 10,
330
935
  model: input.model ?? "morph-computer-use-v0",
331
- viewport_width: input.viewport_width ?? 1280,
332
- viewport_height: input.viewport_height ?? 720,
333
- external_id: input.external_id,
334
- repo_id: input.repo_id,
335
- commit_id: input.commit_id,
336
- record_video: input.record_video ?? false,
337
- video_width: input.video_width ?? input.viewport_width ?? 1280,
338
- video_height: input.video_height ?? input.viewport_height ?? 720,
339
- allow_resizing: input.allow_resizing ?? false,
340
- structured_output: input.structured_output,
341
- auth: input.auth
936
+ viewport_width: input.viewportWidth ?? 1280,
937
+ viewport_height: input.viewportHeight ?? 720,
938
+ external_id: input.externalId,
939
+ repo_id: input.repoId,
940
+ commit_id: input.commitId,
941
+ record_video: input.recordVideo ?? false,
942
+ video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
943
+ video_height: input.videoHeight ?? input.viewportHeight ?? 720,
944
+ allow_resizing: input.allowResizing ?? false,
945
+ structured_output: input.structuredOutput,
946
+ auth: input.auth,
947
+ profile_id: input.profileId
342
948
  })
343
949
  },
344
950
  config.retryConfig
@@ -346,17 +952,17 @@ async function executeBrowserTask(input, config = {}) {
346
952
  const response = await withTimeout(
347
953
  fetchPromise,
348
954
  timeout,
349
- `Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing max_steps.`
955
+ `Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing maxSteps.`
350
956
  );
351
957
  if (!response.ok) {
352
958
  const errorText = await response.text().catch(() => response.statusText);
353
959
  if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
354
960
  throw new Error(`HTTP ${response.status}: ${errorText}`);
355
961
  }
356
- const result = await response.json();
962
+ const result = mapTaskResult(await response.json());
357
963
  const elapsed = Date.now() - startTime;
358
964
  if (debug) {
359
- console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.steps_taken ?? 0} recordingId=${result.recording_id ?? "none"}`);
965
+ console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.stepsTaken ?? 0} recordingId=${result.recordingId ?? "none"}`);
360
966
  }
361
967
  return result;
362
968
  } catch (error) {
@@ -394,7 +1000,7 @@ async function getRecording(recordingId, config = {}) {
394
1000
  if (debug) console.error(`[Browser] getRecording error: ${response.status} - ${errorText}`);
395
1001
  throw new Error(`HTTP ${response.status}: ${errorText}`);
396
1002
  }
397
- const data = await response.json();
1003
+ const data = mapRecordingStatus(await response.json());
398
1004
  if (debug) console.log(`[Browser] Recording status: ${data.status}`);
399
1005
  return {
400
1006
  ...data,
@@ -417,10 +1023,10 @@ async function waitForRecording(recordingId, config = {}, options = {}) {
417
1023
  }
418
1024
  async function executeWithRecording(input, config = {}) {
419
1025
  const taskResult = await executeBrowserTask(input, config);
420
- if (taskResult.recording_id) {
1026
+ if (taskResult.recordingId) {
421
1027
  try {
422
1028
  const recording = await waitForRecording(
423
- taskResult.recording_id,
1029
+ taskResult.recordingId,
424
1030
  config,
425
1031
  { timeout: 6e4, pollInterval: 2e3 }
426
1032
  );
@@ -430,12 +1036,12 @@ async function executeWithRecording(input, config = {}) {
430
1036
  };
431
1037
  } catch (error) {
432
1038
  const errorRecording = {
433
- id: taskResult.recording_id,
1039
+ id: taskResult.recordingId,
434
1040
  status: "ERROR",
435
1041
  error: error instanceof Error ? error.message : String(error),
436
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
437
- getWebp: (options) => getWebp(taskResult.recording_id, config, options),
438
- getErrors: () => getErrors(taskResult.recording_id, config)
1042
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1043
+ getWebp: (options) => getWebp(taskResult.recordingId, config, options),
1044
+ getErrors: () => getErrors(taskResult.recordingId, config)
439
1045
  };
440
1046
  return {
441
1047
  ...taskResult,
@@ -461,8 +1067,8 @@ async function getErrors(recordingId, config = {}) {
461
1067
  if (debug) console.error(`[Browser] getErrors error: ${response.status} - ${errorText}`);
462
1068
  throw new Error(`HTTP ${response.status}: ${errorText}`);
463
1069
  }
464
- const errors = await response.json();
465
- if (debug) console.log(`[Browser] Found ${errors.total_errors} errors`);
1070
+ const errors = mapErrorsResponse(await response.json());
1071
+ if (debug) console.log(`[Browser] Found ${errors.totalErrors} errors`);
466
1072
  return errors;
467
1073
  }
468
1074
  function stringifyStructuredOutput(schema) {
@@ -495,6 +1101,84 @@ function parseStructuredTaskOutput(result, schema) {
495
1101
  throw error;
496
1102
  }
497
1103
  }
1104
+ function mapTaskResult(api) {
1105
+ if (!api || typeof api !== "object") {
1106
+ return api;
1107
+ }
1108
+ return {
1109
+ success: api.success,
1110
+ result: api.result,
1111
+ error: api.error,
1112
+ stepsTaken: api.steps_taken,
1113
+ executionTimeMs: api.execution_time_ms,
1114
+ urls: api.urls,
1115
+ actionNames: api.action_names,
1116
+ errors: api.errors,
1117
+ modelActions: api.model_actions,
1118
+ isDone: api.is_done,
1119
+ actionHistory: api.action_history,
1120
+ actionResults: api.action_results,
1121
+ hasErrors: api.has_errors,
1122
+ numberOfSteps: api.number_of_steps,
1123
+ judgement: api.judgement,
1124
+ isValidated: api.is_validated,
1125
+ replayId: api.replay_id,
1126
+ replayUrl: api.replay_url,
1127
+ recordingId: api.recording_id,
1128
+ recordingStatus: api.recording_status,
1129
+ taskId: api.task_id,
1130
+ status: api.status,
1131
+ output: api.output,
1132
+ debugUrl: api.debug_url
1133
+ };
1134
+ }
1135
+ function mapRecordingStatus(api) {
1136
+ return {
1137
+ id: api.id,
1138
+ status: api.status,
1139
+ replayUrl: api.replay_url,
1140
+ networkUrl: api.network_url,
1141
+ consoleUrl: api.console_url,
1142
+ videoUrl: api.video_url,
1143
+ totalEvents: api.total_events,
1144
+ fileSize: api.file_size,
1145
+ duration: api.duration,
1146
+ error: api.error,
1147
+ createdAt: api.created_at
1148
+ };
1149
+ }
1150
+ function mapBrowserError(api) {
1151
+ return {
1152
+ type: api.type,
1153
+ message: api.message,
1154
+ url: api.url,
1155
+ timestamp: api.timestamp,
1156
+ screenshotUrl: api.screenshot_url,
1157
+ capturedAt: api.captured_at,
1158
+ status: api.status
1159
+ };
1160
+ }
1161
+ function mapErrorsResponse(api) {
1162
+ return {
1163
+ recordingId: api.recording_id,
1164
+ totalErrors: api.total_errors,
1165
+ errors: Array.isArray(api.errors) ? api.errors.map(mapBrowserError) : []
1166
+ };
1167
+ }
1168
+ function mapWebpResponse(api) {
1169
+ return {
1170
+ webpUrl: api.webp_url,
1171
+ cached: api.cached,
1172
+ width: api.width,
1173
+ fps: api.fps,
1174
+ maxDuration: api.max_duration,
1175
+ fileSize: api.file_size,
1176
+ maxSizeMb: api.max_size_mb,
1177
+ budgetMet: api.budget_met,
1178
+ qualityUsed: api.quality_used,
1179
+ attempts: api.attempts
1180
+ };
1181
+ }
498
1182
  async function getTaskStatus(taskId, config) {
499
1183
  const apiUrl = config.apiUrl || DEFAULT_CONFIG.apiUrl;
500
1184
  const debug = config.debug || false;
@@ -510,7 +1194,7 @@ async function getTaskStatus(taskId, config) {
510
1194
  if (debug) console.error(`[Browser] getTaskStatus error: ${response.status} - ${errorText}`);
511
1195
  throw new Error(`HTTP ${response.status}: ${errorText}`);
512
1196
  }
513
- const result = await response.json();
1197
+ const result = mapTaskResult(await response.json());
514
1198
  if (debug) console.log(`[Browser] Task status: ${result.status}`);
515
1199
  return result;
516
1200
  }
@@ -533,42 +1217,44 @@ async function pollTaskUntilComplete(taskId, config, pollConfig = {}) {
533
1217
  throw new Error(`Task polling timeout after ${timeout}ms`);
534
1218
  }
535
1219
  function wrapTaskResponse(result, config) {
1220
+ const debugUrl = result.debugUrl ?? "";
536
1221
  const wrapped = {
537
1222
  ...result,
538
- task_id: result.task_id || "",
539
- liveUrl: result.task_id ? generateLiveUrl(result.task_id, config) : result.debugUrl || "",
1223
+ debugUrl,
1224
+ taskId: result.taskId || "",
1225
+ liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
540
1226
  complete: async (pollConfig) => {
541
- if (result.task_id) {
542
- return pollTaskUntilComplete(result.task_id, config, pollConfig);
1227
+ if (result.taskId) {
1228
+ return pollTaskUntilComplete(result.taskId, config, pollConfig);
543
1229
  }
544
- if (result.recording_id) {
1230
+ if (result.recordingId) {
545
1231
  const recording = await waitForRecording(
546
- result.recording_id,
1232
+ result.recordingId,
547
1233
  config,
548
1234
  pollConfig
549
1235
  );
550
1236
  return {
551
1237
  ...result,
552
- recording_status: recording.status
1238
+ recordingStatus: recording.status
553
1239
  };
554
1240
  }
555
- throw new Error("Cannot poll completion: no task_id or recording_id available");
1241
+ throw new Error("Cannot poll completion: no taskId or recordingId available");
556
1242
  },
557
1243
  // Add Steel live session helpers - either functional or error-throwing
558
- getLiveUrl: result.debugUrl ? (options) => buildLiveUrl(result.debugUrl, options) : () => {
1244
+ getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
559
1245
  throw new Error(
560
1246
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
561
1247
  );
562
1248
  },
563
- getLiveIframe: result.debugUrl ? (optionsOrPreset) => {
1249
+ getLiveIframe: debugUrl ? (optionsOrPreset) => {
564
1250
  const options = resolvePreset(optionsOrPreset);
565
- return buildLiveIframe(result.debugUrl, options);
1251
+ return buildLiveIframe(debugUrl, options);
566
1252
  } : () => {
567
1253
  throw new Error(
568
1254
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
569
1255
  );
570
1256
  },
571
- getEmbedCode: result.debugUrl ? () => buildEmbedCode(result.debugUrl) : () => {
1257
+ getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
572
1258
  throw new Error(
573
1259
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
574
1260
  );
@@ -577,44 +1263,46 @@ function wrapTaskResponse(result, config) {
577
1263
  return wrapped;
578
1264
  }
579
1265
  function wrapTaskResponseWithSchema(result, config, schema) {
1266
+ const debugUrl = result.debugUrl ?? "";
580
1267
  const parsed = result.output ? parseStructuredTaskOutput(result, schema) : { ...result, parsed: null };
581
1268
  const wrapped = {
582
1269
  ...parsed,
583
- task_id: result.task_id || "",
584
- liveUrl: result.task_id ? generateLiveUrl(result.task_id, config) : result.debugUrl || "",
1270
+ debugUrl,
1271
+ taskId: result.taskId || "",
1272
+ liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
585
1273
  complete: async (pollConfig) => {
586
- if (result.task_id) {
587
- const finalResult = await pollTaskUntilComplete(result.task_id, config, pollConfig);
1274
+ if (result.taskId) {
1275
+ const finalResult = await pollTaskUntilComplete(result.taskId, config, pollConfig);
588
1276
  return parseStructuredTaskOutput(finalResult, schema);
589
1277
  }
590
- if (result.recording_id) {
1278
+ if (result.recordingId) {
591
1279
  const recording = await waitForRecording(
592
- result.recording_id,
1280
+ result.recordingId,
593
1281
  config,
594
1282
  pollConfig
595
1283
  );
596
1284
  return {
597
1285
  ...parsed,
598
- recording_status: recording.status
1286
+ recordingStatus: recording.status
599
1287
  };
600
1288
  }
601
- throw new Error("Cannot poll completion: no task_id or recording_id available");
1289
+ throw new Error("Cannot poll completion: no taskId or recordingId available");
602
1290
  },
603
1291
  // Add Steel live session helpers - either functional or error-throwing
604
- getLiveUrl: result.debugUrl ? (options) => buildLiveUrl(result.debugUrl, options) : () => {
1292
+ getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
605
1293
  throw new Error(
606
1294
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions. "
607
1295
  );
608
1296
  },
609
- getLiveIframe: result.debugUrl ? (optionsOrPreset) => {
1297
+ getLiveIframe: debugUrl ? (optionsOrPreset) => {
610
1298
  const options = resolvePreset(optionsOrPreset);
611
- return buildLiveIframe(result.debugUrl, options);
1299
+ return buildLiveIframe(debugUrl, options);
612
1300
  } : () => {
613
1301
  throw new Error(
614
1302
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
615
1303
  );
616
1304
  },
617
- getEmbedCode: result.debugUrl ? () => buildEmbedCode(result.debugUrl) : () => {
1305
+ getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
618
1306
  throw new Error(
619
1307
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
620
1308
  );
@@ -629,10 +1317,11 @@ async function getWebp(recordingId, config = {}, options = {}) {
629
1317
  throw new Error("API key required for getWebp");
630
1318
  }
631
1319
  const params = new URLSearchParams();
632
- if (options.max_duration !== void 0) params.set("max_duration", String(options.max_duration));
1320
+ if (options.maxDuration !== void 0) params.set("max_duration", String(options.maxDuration));
633
1321
  if (options.fps !== void 0) params.set("fps", String(options.fps));
634
1322
  if (options.width !== void 0) params.set("width", String(options.width));
635
1323
  if (options.quality !== void 0) params.set("quality", String(options.quality));
1324
+ if (options.maxSizeMb !== void 0) params.set("max_size_mb", String(options.maxSizeMb));
636
1325
  const url = `${apiUrl}/recordings/${recordingId}/webp${params.toString() ? "?" + params.toString() : ""}`;
637
1326
  if (debug) console.log(`[Browser] getWebp: ${url}`);
638
1327
  const response = await fetch(url, {
@@ -644,8 +1333,8 @@ async function getWebp(recordingId, config = {}, options = {}) {
644
1333
  if (debug) console.error(`[Browser] getWebp error: ${response.status} - ${errorText}`);
645
1334
  throw new Error(`HTTP ${response.status}: ${errorText}`);
646
1335
  }
647
- const result = await response.json();
648
- if (debug) console.log(`[Browser] WebP ready: ${result.webp_url} (cached: ${result.cached})`);
1336
+ const result = mapWebpResponse(await response.json());
1337
+ if (debug) console.log(`[Browser] WebP ready: ${result.webpUrl} (cached: ${result.cached})`);
649
1338
  return result;
650
1339
  }
651
1340
  async function checkHealth(config = {}) {
@@ -712,7 +1401,7 @@ Include verification steps:
712
1401
  ## Requirements
713
1402
  - **URL**: Must be publicly accessible (use tunnels like ngrok, Cloudflare, or deploy to staging)
714
1403
  - **Timing**: Use this after implementation, not during coding
715
- - **Complexity**: Set max_steps higher (20-30) for multi-step user workflows`;
1404
+ - **Complexity**: Set maxSteps higher (20-30) for multi-step user workflows`;
716
1405
  var BROWSER_SYSTEM_PROMPT = `You are an AI agent designed to automate browser tasks to accomplish the <user_request>. Respond with a valid JSON object in the format: {"thinking": "Reason step-by-step about your current state, history, and the user request to decide your next goal and action. Analyze the browser state and screenshot to confirm the outcome of your last action.", "evaluation_previous_goal": "A concise, one-sentence evaluation of your last action's outcome (e.g., Success, Failure, or Uncertain).", "memory": "1-3 sentences summarizing key information and progress so far. This helps you track progress across multiple steps (e.g., items collected, pages visited).", "next_goal": "A clear, one-sentence description of your immediate next objective.", "action": [{"action_name": {"parameter": "value"}}]}`;
717
1406
 
718
1407
  // tools/browser/anthropic.ts
@@ -735,7 +1424,7 @@ var browserTool = {
735
1424
  type: "string",
736
1425
  description: "Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page."
737
1426
  },
738
- max_steps: {
1427
+ maxSteps: {
739
1428
  type: "number",
740
1429
  description: "Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows.",
741
1430
  default: 10
@@ -754,8 +1443,8 @@ function formatResult(result) {
754
1443
  if (result.success) {
755
1444
  const parts = [
756
1445
  "\u2705 Browser task completed successfully",
757
- `Steps taken: ${result.steps_taken ?? 0}`,
758
- result.execution_time_ms ? `Execution time: ${result.execution_time_ms}ms` : null,
1446
+ `Steps taken: ${result.stepsTaken ?? 0}`,
1447
+ result.executionTimeMs ? `Execution time: ${result.executionTimeMs}ms` : null,
759
1448
  "",
760
1449
  "Result:",
761
1450
  result.result || "Task completed"
@@ -803,7 +1492,7 @@ var browserTool2 = {
803
1492
  type: "string",
804
1493
  description: "Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page."
805
1494
  },
806
- max_steps: {
1495
+ maxSteps: {
807
1496
  type: "number",
808
1497
  description: "Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows.",
809
1498
  default: 10
@@ -827,8 +1516,8 @@ function formatResult2(result) {
827
1516
  if (result.success) {
828
1517
  const parts = [
829
1518
  "\u2705 Browser task completed successfully",
830
- `Steps taken: ${result.steps_taken ?? 0}`,
831
- result.execution_time_ms ? `Execution time: ${result.execution_time_ms}ms` : null,
1519
+ `Steps taken: ${result.stepsTaken ?? 0}`,
1520
+ result.executionTimeMs ? `Execution time: ${result.executionTimeMs}ms` : null,
832
1521
  "",
833
1522
  "Result:",
834
1523
  result.result || "Task completed"
@@ -866,24 +1555,24 @@ function createBrowserTool3(config) {
866
1555
  const schema = import_zod.z.object({
867
1556
  task: import_zod.z.string().describe('Natural language description of what to do (e.g., "Test checkout flow for buying a pineapple")'),
868
1557
  url: import_zod.z.string().optional().describe("Starting URL (e.g., https://3000-xyz.e2b.dev)"),
869
- max_steps: import_zod.z.number().min(1).max(50).default(10).describe("Maximum number of browser actions to take"),
1558
+ maxSteps: import_zod.z.number().min(1).max(50).default(10).describe("Maximum number of browser actions to take"),
870
1559
  region: import_zod.z.enum(["sfo", "lon"]).default("sfo").describe("Browserless region: sfo (US West) or lon (Europe)")
871
1560
  });
872
1561
  return (0, import_ai.tool)({
873
1562
  description: BROWSER_TOOL_DESCRIPTION,
874
1563
  inputSchema: schema,
875
1564
  execute: async (params) => {
876
- const { task, url, max_steps, region } = params;
1565
+ const { task, url, maxSteps, region } = params;
877
1566
  const result = await executeBrowserTask(
878
- { task, url, max_steps, region },
1567
+ { task, url, maxSteps, region },
879
1568
  config
880
1569
  );
881
1570
  if (result.success) {
882
1571
  return {
883
1572
  success: true,
884
1573
  result: result.result,
885
- steps_taken: result.steps_taken,
886
- execution_time_ms: result.execution_time_ms
1574
+ stepsTaken: result.stepsTaken,
1575
+ executionTimeMs: result.executionTimeMs
887
1576
  };
888
1577
  }
889
1578
  return {
@@ -918,7 +1607,7 @@ var TOOL_PARAMETERS = {
918
1607
  type: import_generative_ai.SchemaType.STRING,
919
1608
  description: "Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page."
920
1609
  },
921
- max_steps: {
1610
+ maxSteps: {
922
1611
  type: import_generative_ai.SchemaType.NUMBER,
923
1612
  description: "Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows."
924
1613
  },
@@ -942,8 +1631,8 @@ function formatResult3(result) {
942
1631
  if (result.success) {
943
1632
  const parts = [
944
1633
  "\u2705 Browser task completed successfully",
945
- `Steps taken: ${result.steps_taken ?? 0}`,
946
- result.execution_time_ms ? `Execution time: ${result.execution_time_ms}ms` : null,
1634
+ `Steps taken: ${result.stepsTaken ?? 0}`,
1635
+ result.executionTimeMs ? `Execution time: ${result.executionTimeMs}ms` : null,
947
1636
  "",
948
1637
  "Result:",
949
1638
  result.result || "Task completed"
@@ -981,6 +1670,14 @@ var gemini_default = browserFunctionDeclaration;
981
1670
  BROWSER_TOOL_DESCRIPTION,
982
1671
  BrowserClient,
983
1672
  LIVE_PRESETS,
1673
+ MorphAPIError,
1674
+ MorphAuthenticationError,
1675
+ MorphError,
1676
+ MorphNotFoundError,
1677
+ MorphProfileLimitError,
1678
+ MorphRateLimitError,
1679
+ MorphValidationError,
1680
+ ProfilesClient,
984
1681
  anthropic,
985
1682
  buildEmbedCode,
986
1683
  buildLiveIframe,
@@ -993,6 +1690,7 @@ var gemini_default = browserFunctionDeclaration;
993
1690
  getRecording,
994
1691
  getWebp,
995
1692
  openai,
1693
+ parseAPIError,
996
1694
  vercel,
997
1695
  waitForRecording
998
1696
  });