@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
package/dist/client.cjs CHANGED
@@ -440,7 +440,8 @@ function buildLiveUrl(debugUrl, options = {}) {
440
440
  "debugUrl is required. Ensure your backend returns debugUrl in the task response. Contact support@morphllm.com if you need help."
441
441
  );
442
442
  }
443
- const url = new URL(debugUrl);
443
+ const normalized = normalizeLiveUrl(debugUrl);
444
+ const url = new URL(normalized);
444
445
  if (options.interactive !== void 0) {
445
446
  url.searchParams.set("interactive", String(options.interactive));
446
447
  }
@@ -458,6 +459,29 @@ function buildLiveUrl(debugUrl, options = {}) {
458
459
  }
459
460
  return url.toString();
460
461
  }
462
+ function normalizeLiveUrl(debugUrl) {
463
+ const trimmed = debugUrl.trim();
464
+ if (!trimmed) {
465
+ return trimmed;
466
+ }
467
+ if (trimmed.startsWith("wss://") || trimmed.startsWith("ws://")) {
468
+ return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
469
+ }
470
+ let url;
471
+ try {
472
+ url = new URL(trimmed);
473
+ } catch {
474
+ return trimmed;
475
+ }
476
+ if (url.protocol === "wss:" || url.protocol === "ws:") {
477
+ return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
478
+ }
479
+ const wssParam = url.searchParams.get("wss");
480
+ if (wssParam && (wssParam.startsWith("wss://") || wssParam.startsWith("ws://"))) {
481
+ url.searchParams.set("wss", wssParam);
482
+ }
483
+ return url.toString();
484
+ }
461
485
  function buildLiveIframe(debugUrl, options = {}) {
462
486
  const {
463
487
  width = "100%",
@@ -501,6 +525,570 @@ function resolvePreset(optionsOrPreset) {
501
525
  return optionsOrPreset;
502
526
  }
503
527
 
528
+ // tools/browser/errors.ts
529
+ var MorphError = class extends Error {
530
+ /** Error code for programmatic handling */
531
+ code;
532
+ /** Original cause of the error, if any */
533
+ cause;
534
+ constructor(message, code, cause) {
535
+ super(message);
536
+ this.name = "MorphError";
537
+ this.code = code;
538
+ this.cause = cause;
539
+ if (Error.captureStackTrace) {
540
+ Error.captureStackTrace(this, this.constructor);
541
+ }
542
+ }
543
+ /**
544
+ * Returns a JSON representation of the error for logging.
545
+ */
546
+ toJSON() {
547
+ return {
548
+ name: this.name,
549
+ message: this.message,
550
+ code: this.code,
551
+ cause: this.cause?.message
552
+ };
553
+ }
554
+ };
555
+ var MorphValidationError = class extends MorphError {
556
+ /** The field that failed validation */
557
+ field;
558
+ constructor(message, field) {
559
+ super(message, "validation_error");
560
+ this.name = "MorphValidationError";
561
+ this.field = field;
562
+ }
563
+ toJSON() {
564
+ return {
565
+ ...super.toJSON(),
566
+ field: this.field
567
+ };
568
+ }
569
+ };
570
+ var MorphAPIError = class extends MorphError {
571
+ /** HTTP status code */
572
+ statusCode;
573
+ /** Request ID for debugging (if available) */
574
+ requestId;
575
+ /** Raw response body */
576
+ rawResponse;
577
+ constructor(message, code, statusCode, options) {
578
+ super(message, code, options?.cause);
579
+ this.name = "MorphAPIError";
580
+ this.statusCode = statusCode;
581
+ this.requestId = options?.requestId;
582
+ this.rawResponse = options?.rawResponse;
583
+ }
584
+ toJSON() {
585
+ return {
586
+ ...super.toJSON(),
587
+ statusCode: this.statusCode,
588
+ requestId: this.requestId
589
+ };
590
+ }
591
+ };
592
+ var MorphAuthenticationError = class extends MorphAPIError {
593
+ constructor(message = "Authentication required. Please provide a valid API key.") {
594
+ super(message, "authentication_required", 401);
595
+ this.name = "MorphAuthenticationError";
596
+ }
597
+ };
598
+ var MorphRateLimitError = class extends MorphAPIError {
599
+ /** When the rate limit resets (Unix timestamp) */
600
+ resetAt;
601
+ /** Number of seconds until reset */
602
+ retryAfter;
603
+ constructor(message = "Rate limit exceeded. Please retry later.", options) {
604
+ super(message, "rate_limit_exceeded", 429, { requestId: options?.requestId });
605
+ this.name = "MorphRateLimitError";
606
+ this.resetAt = options?.resetAt;
607
+ this.retryAfter = options?.retryAfter;
608
+ }
609
+ toJSON() {
610
+ return {
611
+ ...super.toJSON(),
612
+ resetAt: this.resetAt,
613
+ retryAfter: this.retryAfter
614
+ };
615
+ }
616
+ };
617
+ var MorphNotFoundError = class extends MorphAPIError {
618
+ /** The type of resource that was not found */
619
+ resourceType;
620
+ /** The ID of the resource that was not found */
621
+ resourceId;
622
+ constructor(resourceType, resourceId) {
623
+ const message = resourceId ? `${resourceType} '${resourceId}' not found` : `${resourceType} not found`;
624
+ super(message, "resource_not_found", 404);
625
+ this.name = "MorphNotFoundError";
626
+ this.resourceType = resourceType;
627
+ this.resourceId = resourceId;
628
+ }
629
+ toJSON() {
630
+ return {
631
+ ...super.toJSON(),
632
+ resourceType: this.resourceType,
633
+ resourceId: this.resourceId
634
+ };
635
+ }
636
+ };
637
+ var MorphProfileLimitError = class extends MorphAPIError {
638
+ /** Current number of profiles */
639
+ currentCount;
640
+ /** Maximum allowed profiles for the plan */
641
+ maxAllowed;
642
+ constructor(message = "Profile limit exceeded for your plan.", options) {
643
+ super(message, "profile_limit_exceeded", 403, { requestId: options?.requestId });
644
+ this.name = "MorphProfileLimitError";
645
+ this.currentCount = options?.currentCount;
646
+ this.maxAllowed = options?.maxAllowed;
647
+ }
648
+ toJSON() {
649
+ return {
650
+ ...super.toJSON(),
651
+ currentCount: this.currentCount,
652
+ maxAllowed: this.maxAllowed
653
+ };
654
+ }
655
+ };
656
+ function parseAPIError(statusCode, responseText, requestId) {
657
+ let errorData = {};
658
+ try {
659
+ errorData = JSON.parse(responseText);
660
+ } catch {
661
+ }
662
+ const message = errorData.detail || errorData.message || responseText || "Unknown error";
663
+ const code = errorData.code;
664
+ switch (statusCode) {
665
+ case 401:
666
+ return new MorphAuthenticationError(message);
667
+ case 403:
668
+ if (code === "profile_limit_exceeded" || message.toLowerCase().includes("limit")) {
669
+ return new MorphProfileLimitError(message, { requestId });
670
+ }
671
+ return new MorphAPIError(message, "insufficient_permissions", statusCode, { requestId, rawResponse: responseText });
672
+ case 404:
673
+ if (message.toLowerCase().includes("profile")) {
674
+ return new MorphNotFoundError("Profile", void 0);
675
+ }
676
+ if (message.toLowerCase().includes("session")) {
677
+ return new MorphNotFoundError("Session", void 0);
678
+ }
679
+ return new MorphAPIError(message, "resource_not_found", statusCode, { requestId, rawResponse: responseText });
680
+ case 429:
681
+ return new MorphRateLimitError(message, { requestId });
682
+ case 422:
683
+ return new MorphAPIError(message, "validation_error", statusCode, { requestId, rawResponse: responseText });
684
+ case 500:
685
+ case 502:
686
+ case 503:
687
+ case 504:
688
+ return new MorphAPIError(message, "service_unavailable", statusCode, { requestId, rawResponse: responseText });
689
+ default:
690
+ return new MorphAPIError(message, "network_error", statusCode, { requestId, rawResponse: responseText });
691
+ }
692
+ }
693
+
694
+ // tools/browser/profiles/types.ts
695
+ function transformProfile(api) {
696
+ return {
697
+ id: api.id,
698
+ name: api.name,
699
+ repoId: api.repo_id,
700
+ cookieDomains: api.cookie_domains,
701
+ lastUsedAt: api.last_used_at,
702
+ createdAt: api.created_at,
703
+ updatedAt: api.updated_at
704
+ };
705
+ }
706
+ function transformCreateInput(input) {
707
+ return {
708
+ name: input.name,
709
+ repo_id: input.repoId
710
+ };
711
+ }
712
+ function transformSession(api) {
713
+ return {
714
+ sessionId: api.session_id,
715
+ debugUrl: api.debug_url || ""
716
+ };
717
+ }
718
+ function transformSaveInput(input) {
719
+ return {
720
+ session_id: input.sessionId,
721
+ profile_id: input.profileId
722
+ };
723
+ }
724
+ function transformStateResponse(api) {
725
+ return {
726
+ profileId: api.profile_id,
727
+ stateUrl: api.state_url,
728
+ expiresIn: api.expires_in
729
+ };
730
+ }
731
+
732
+ // tools/browser/profiles/core.ts
733
+ var DEFAULT_API_URL = process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com";
734
+ var ProfilesClient = class {
735
+ config;
736
+ constructor(config) {
737
+ this.config = config;
738
+ }
739
+ /**
740
+ * Create a new browser profile and immediately start a live session.
741
+ *
742
+ * @param input - Profile creation parameters
743
+ * @returns Profile setup handle with live URL + save()
744
+ * @throws {MorphValidationError} If input validation fails
745
+ * @throws {MorphProfileLimitError} If profile limit is exceeded
746
+ * @throws {MorphAuthenticationError} If API key is missing or invalid
747
+ *
748
+ * @example
749
+ * ```typescript
750
+ * const setup = await morph.browser.profiles.createProfile({
751
+ * name: 'LinkedIn Production',
752
+ * repoId: 'owner/repo'
753
+ * });
754
+ * console.log(setup.session.debugUrl);
755
+ * await setup.save();
756
+ * ```
757
+ */
758
+ async createProfile(input) {
759
+ return createProfile(input, this.config);
760
+ }
761
+ /**
762
+ * List all profiles for the authenticated user.
763
+ *
764
+ * @param repoId - Optional repository ID to filter by
765
+ * @returns Array of profiles
766
+ *
767
+ * @example
768
+ * ```typescript
769
+ * // List all profiles
770
+ * const allProfiles = await morph.browser.profiles.listProfiles();
771
+ *
772
+ * // List profiles for a specific repo
773
+ * const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');
774
+ * ```
775
+ */
776
+ async listProfiles(repoId) {
777
+ return listProfiles(this.config, repoId);
778
+ }
779
+ /**
780
+ * Get a profile by ID with convenience methods.
781
+ *
782
+ * @param id - Profile ID
783
+ * @returns Profile with attached methods
784
+ * @throws {MorphNotFoundError} If profile is not found
785
+ *
786
+ * @example
787
+ * ```typescript
788
+ * const profile = await morph.browser.profiles.getProfile('profile-id');
789
+ * const state = await profile.getState();
790
+ * await profile.delete();
791
+ * ```
792
+ */
793
+ async getProfile(id) {
794
+ return getProfile(id, this.config);
795
+ }
796
+ /**
797
+ * Update a profile by opening a live session (no rename).
798
+ *
799
+ * @param id - Profile ID
800
+ * @returns Profile setup handle with live URL + save()
801
+ */
802
+ async updateProfile(id) {
803
+ return updateProfile(id, this.config);
804
+ }
805
+ /**
806
+ * Delete a profile.
807
+ *
808
+ * @param id - Profile ID
809
+ * @throws {MorphNotFoundError} If profile is not found
810
+ */
811
+ async deleteProfile(id) {
812
+ return deleteProfile(id, this.config);
813
+ }
814
+ /**
815
+ * Start a browser session for profile setup.
816
+ *
817
+ * Returns a live URL where the user can sign into accounts.
818
+ * After signing in, call `saveSession` to persist the state.
819
+ *
820
+ * @param input - Optional session parameters
821
+ * @returns Session with debug URL
822
+ *
823
+ * @example
824
+ * ```typescript
825
+ * const session = await morph.browser.profiles.startSession();
826
+ * console.log('Sign in at:', session.debugUrl);
827
+ * // Open debugUrl in browser, user signs in...
828
+ * await morph.browser.profiles.saveSession(session.sessionId, profile.id);
829
+ * ```
830
+ */
831
+ async startSession(input) {
832
+ return startProfileSession(this.config, input);
833
+ }
834
+ /**
835
+ * Save browser state from a session to a profile.
836
+ *
837
+ * Call this after the user is done signing into accounts.
838
+ * Extracts cookies, localStorage, and sessionStorage.
839
+ *
840
+ * @param sessionId - Browser session ID from startSession
841
+ * @param profileId - Profile ID to save state to
842
+ * @returns Updated profile with cookie domains
843
+ */
844
+ async saveSession(sessionId, profileId) {
845
+ return saveProfileSession({ sessionId, profileId }, this.config);
846
+ }
847
+ /**
848
+ * List available repo IDs (discovery).
849
+ *
850
+ * @returns Repo summaries with profile counts
851
+ */
852
+ async listRepos() {
853
+ return listRepos(this.config);
854
+ }
855
+ /**
856
+ * Get the presigned URL for a profile's state.
857
+ *
858
+ * Use this to download the raw state JSON for debugging
859
+ * or to restore state manually.
860
+ *
861
+ * @param profileId - Profile ID
862
+ * @returns State URL with expiry information
863
+ */
864
+ async getProfileState(profileId) {
865
+ return getProfileState(profileId, this.config);
866
+ }
867
+ };
868
+ function validateCreateInput(input) {
869
+ if (!input.name || typeof input.name !== "string") {
870
+ throw new MorphValidationError("name is required", "name");
871
+ }
872
+ const trimmedName = input.name.trim();
873
+ if (trimmedName.length === 0) {
874
+ throw new MorphValidationError("name cannot be empty", "name");
875
+ }
876
+ if (trimmedName.length > 100) {
877
+ throw new MorphValidationError("name must be 100 characters or less", "name");
878
+ }
879
+ if (!input.repoId || typeof input.repoId !== "string") {
880
+ throw new MorphValidationError("repoId is required", "repoId");
881
+ }
882
+ if (input.repoId.trim().length === 0) {
883
+ throw new MorphValidationError("repoId cannot be empty", "repoId");
884
+ }
885
+ }
886
+ function validateId(id, fieldName) {
887
+ if (!id || typeof id !== "string") {
888
+ throw new MorphValidationError(`${fieldName} is required`, fieldName);
889
+ }
890
+ if (id.trim().length === 0) {
891
+ throw new MorphValidationError(`${fieldName} cannot be empty`, fieldName);
892
+ }
893
+ }
894
+ async function createProfile(input, config = {}) {
895
+ validateCreateInput(input);
896
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
897
+ const headers = buildHeaders(config);
898
+ const response = await fetchWithRetry(
899
+ `${apiUrl}/profiles`,
900
+ {
901
+ method: "POST",
902
+ headers,
903
+ body: JSON.stringify(transformCreateInput(input))
904
+ },
905
+ config.retryConfig
906
+ );
907
+ if (!response.ok) {
908
+ const errorText = await response.text().catch(() => response.statusText);
909
+ const requestId = response.headers.get("x-request-id") || void 0;
910
+ throw parseAPIError(response.status, errorText, requestId);
911
+ }
912
+ const apiProfile = await response.json();
913
+ const profile = transformProfile(apiProfile);
914
+ const session = await startProfileSession(config, { profileId: profile.id });
915
+ return buildProfileSetup(profile, session, config);
916
+ }
917
+ async function listProfiles(config = {}, repoId) {
918
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
919
+ const headers = buildHeaders(config);
920
+ const url = repoId ? `${apiUrl}/profiles?repo_id=${encodeURIComponent(repoId)}` : `${apiUrl}/profiles`;
921
+ const response = await fetchWithRetry(
922
+ url,
923
+ { method: "GET", headers },
924
+ config.retryConfig
925
+ );
926
+ if (!response.ok) {
927
+ const errorText = await response.text().catch(() => response.statusText);
928
+ const requestId = response.headers.get("x-request-id") || void 0;
929
+ throw parseAPIError(response.status, errorText, requestId);
930
+ }
931
+ const data = await response.json();
932
+ return data.profiles.map(transformProfile);
933
+ }
934
+ async function getProfile(id, config = {}) {
935
+ validateId(id, "id");
936
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
937
+ const headers = buildHeaders(config);
938
+ const response = await fetchWithRetry(
939
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
940
+ { method: "GET", headers },
941
+ config.retryConfig
942
+ );
943
+ if (!response.ok) {
944
+ const errorText = await response.text().catch(() => response.statusText);
945
+ const requestId = response.headers.get("x-request-id") || void 0;
946
+ throw parseAPIError(response.status, errorText, requestId);
947
+ }
948
+ const apiProfile = await response.json();
949
+ const profile = transformProfile(apiProfile);
950
+ return {
951
+ ...profile,
952
+ getState: () => getProfileState(id, config),
953
+ delete: () => deleteProfile(id, config)
954
+ };
955
+ }
956
+ async function updateProfile(id, config = {}) {
957
+ validateId(id, "id");
958
+ const profile = await fetchProfile(id, config);
959
+ const session = await startProfileSession(config, { profileId: profile.id });
960
+ return buildProfileSetup(profile, session, config);
961
+ }
962
+ async function deleteProfile(id, config = {}) {
963
+ validateId(id, "id");
964
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
965
+ const headers = buildHeaders(config);
966
+ const response = await fetchWithRetry(
967
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
968
+ { method: "DELETE", headers },
969
+ config.retryConfig
970
+ );
971
+ if (!response.ok) {
972
+ const errorText = await response.text().catch(() => response.statusText);
973
+ const requestId = response.headers.get("x-request-id") || void 0;
974
+ throw parseAPIError(response.status, errorText, requestId);
975
+ }
976
+ }
977
+ async function startProfileSession(config = {}, input) {
978
+ if (!config.apiKey) {
979
+ throw new MorphAuthenticationError();
980
+ }
981
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
982
+ const headers = buildHeaders(config);
983
+ const body = input?.profileId ? { profile_id: input.profileId } : {};
984
+ const response = await fetchWithRetry(
985
+ `${apiUrl}/profiles/session/start`,
986
+ {
987
+ method: "POST",
988
+ headers,
989
+ body: JSON.stringify(body)
990
+ },
991
+ config.retryConfig
992
+ );
993
+ if (!response.ok) {
994
+ const errorText = await response.text().catch(() => response.statusText);
995
+ const requestId = response.headers.get("x-request-id") || void 0;
996
+ throw parseAPIError(response.status, errorText, requestId);
997
+ }
998
+ const apiSession = await response.json();
999
+ return transformSession(apiSession);
1000
+ }
1001
+ async function saveProfileSession(input, config = {}) {
1002
+ validateId(input.sessionId, "sessionId");
1003
+ validateId(input.profileId, "profileId");
1004
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1005
+ const headers = buildHeaders(config);
1006
+ const response = await fetchWithRetry(
1007
+ `${apiUrl}/profiles/session/save`,
1008
+ {
1009
+ method: "POST",
1010
+ headers,
1011
+ body: JSON.stringify(transformSaveInput(input))
1012
+ },
1013
+ config.retryConfig
1014
+ );
1015
+ if (!response.ok) {
1016
+ const errorText = await response.text().catch(() => response.statusText);
1017
+ const requestId = response.headers.get("x-request-id") || void 0;
1018
+ throw parseAPIError(response.status, errorText, requestId);
1019
+ }
1020
+ const apiProfile = await response.json();
1021
+ return transformProfile(apiProfile);
1022
+ }
1023
+ async function listRepos(config = {}) {
1024
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1025
+ const headers = buildHeaders(config);
1026
+ const response = await fetchWithRetry(
1027
+ `${apiUrl}/repos`,
1028
+ { method: "GET", headers },
1029
+ config.retryConfig
1030
+ );
1031
+ if (!response.ok) {
1032
+ const errorText = await response.text().catch(() => response.statusText);
1033
+ const requestId = response.headers.get("x-request-id") || void 0;
1034
+ throw parseAPIError(response.status, errorText, requestId);
1035
+ }
1036
+ const data = await response.json();
1037
+ const repos = Array.isArray(data?.repos) ? data.repos : [];
1038
+ return repos.map((repo) => ({
1039
+ repoId: repo.repo_id,
1040
+ repoFullName: repo.repo_full_name,
1041
+ profileCount: repo.profile_count ?? 0
1042
+ }));
1043
+ }
1044
+ async function getProfileState(profileId, config = {}) {
1045
+ validateId(profileId, "profileId");
1046
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1047
+ const headers = buildHeaders(config);
1048
+ const response = await fetchWithRetry(
1049
+ `${apiUrl}/profiles/${encodeURIComponent(profileId)}/state`,
1050
+ { method: "GET", headers },
1051
+ config.retryConfig
1052
+ );
1053
+ if (!response.ok) {
1054
+ const errorText = await response.text().catch(() => response.statusText);
1055
+ const requestId = response.headers.get("x-request-id") || void 0;
1056
+ throw parseAPIError(response.status, errorText, requestId);
1057
+ }
1058
+ const apiState = await response.json();
1059
+ return transformStateResponse(apiState);
1060
+ }
1061
+ function buildHeaders(config) {
1062
+ const headers = { "Content-Type": "application/json" };
1063
+ if (config.apiKey) {
1064
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
1065
+ }
1066
+ return headers;
1067
+ }
1068
+ async function fetchProfile(id, config) {
1069
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1070
+ const headers = buildHeaders(config);
1071
+ const response = await fetchWithRetry(
1072
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
1073
+ { method: "GET", headers },
1074
+ config.retryConfig
1075
+ );
1076
+ if (!response.ok) {
1077
+ const errorText = await response.text().catch(() => response.statusText);
1078
+ const requestId = response.headers.get("x-request-id") || void 0;
1079
+ throw parseAPIError(response.status, errorText, requestId);
1080
+ }
1081
+ const apiProfile = await response.json();
1082
+ return transformProfile(apiProfile);
1083
+ }
1084
+ function buildProfileSetup(profile, session, config) {
1085
+ return {
1086
+ profile,
1087
+ session,
1088
+ save: () => saveProfileSession({ sessionId: session.sessionId, profileId: profile.id }, config)
1089
+ };
1090
+ }
1091
+
504
1092
  // tools/browser/core.ts
505
1093
  var DEFAULT_CONFIG2 = {
506
1094
  apiUrl: process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com",
@@ -510,11 +1098,16 @@ var DEFAULT_CONFIG2 = {
510
1098
  };
511
1099
  var BrowserClient = class {
512
1100
  config;
1101
+ /**
1102
+ * Profile management - create and manage browser profiles for storing login state.
1103
+ */
1104
+ profiles;
513
1105
  constructor(config = {}) {
514
1106
  this.config = {
515
1107
  ...DEFAULT_CONFIG2,
516
1108
  ...config
517
1109
  };
1110
+ this.profiles = new ProfilesClient(this.config);
518
1111
  }
519
1112
  /**
520
1113
  * Execute a browser automation task
@@ -537,19 +1130,21 @@ var BrowserClient = class {
537
1130
  body: JSON.stringify({
538
1131
  task: input.task,
539
1132
  url: input.url,
540
- max_steps: input.max_steps ?? 10,
1133
+ max_steps: input.maxSteps ?? 10,
541
1134
  model: input.model ?? "morph-computer-use-v0",
542
- viewport_width: input.viewport_width ?? 1280,
543
- viewport_height: input.viewport_height ?? 720,
544
- external_id: input.external_id,
545
- repo_id: input.repo_id,
546
- commit_id: input.commit_id,
547
- record_video: input.record_video ?? false,
548
- video_width: input.video_width ?? input.viewport_width ?? 1280,
549
- video_height: input.video_height ?? input.viewport_height ?? 720,
550
- allow_resizing: input.allow_resizing ?? false,
1135
+ viewport_width: input.viewportWidth ?? 1280,
1136
+ viewport_height: input.viewportHeight ?? 720,
1137
+ external_id: input.externalId,
1138
+ repo_id: input.repoId,
1139
+ repo_full_name: input.repoFullName,
1140
+ commit_id: input.commitId,
1141
+ record_video: input.recordVideo ?? false,
1142
+ video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
1143
+ video_height: input.videoHeight ?? input.viewportHeight ?? 720,
1144
+ allow_resizing: input.allowResizing ?? false,
551
1145
  structured_output: "schema" in input ? stringifyStructuredOutput(input.schema) : void 0,
552
- auth: input.auth
1146
+ auth: input.auth,
1147
+ profile_id: input.profileId
553
1148
  })
554
1149
  });
555
1150
  if (!response.ok) {
@@ -557,9 +1152,10 @@ var BrowserClient = class {
557
1152
  if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
558
1153
  throw new Error(`HTTP ${response.status}: ${errorText}`);
559
1154
  }
560
- const result = await response.json();
1155
+ const result = mapTaskResult(await response.json());
561
1156
  if (debug) {
562
- console.log(`[Browser] \u2705 Task created: recording_id=${result.recording_id ?? "none"} debug_url=${result.debugUrl ? "available" : "none"}`);
1157
+ const debugUrl = result.debugUrl;
1158
+ console.log(`[Browser] \u2705 Task created: recordingId=${result.recordingId ?? "none"} debugUrl=${debugUrl ? "available" : "none"}`);
563
1159
  }
564
1160
  if ("schema" in input) {
565
1161
  return wrapTaskResponseWithSchema(result, this.config, input.schema);
@@ -614,15 +1210,15 @@ async function executeBrowserTask(input, config = {}) {
614
1210
  error: 'Task description is required. Example: "Go to example.com and click the login button"'
615
1211
  };
616
1212
  }
617
- if (input.max_steps !== void 0 && (input.max_steps < 1 || input.max_steps > 50)) {
1213
+ if (input.maxSteps !== void 0 && (input.maxSteps < 1 || input.maxSteps > 50)) {
618
1214
  return {
619
1215
  success: false,
620
- error: "max_steps must be between 1 and 50. Use more steps for complex multi-page flows."
1216
+ error: "maxSteps must be between 1 and 50. Use more steps for complex multi-page flows."
621
1217
  };
622
1218
  }
623
1219
  if (debug) {
624
- console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.max_steps ?? 10}`);
625
- console.log(`[Browser] Recording: ${input.record_video ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
1220
+ console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.maxSteps ?? 10}`);
1221
+ console.log(`[Browser] Recording: ${input.recordVideo ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
626
1222
  }
627
1223
  const startTime = Date.now();
628
1224
  try {
@@ -636,19 +1232,20 @@ async function executeBrowserTask(input, config = {}) {
636
1232
  body: JSON.stringify({
637
1233
  task: input.task,
638
1234
  url: input.url,
639
- max_steps: input.max_steps ?? 10,
1235
+ max_steps: input.maxSteps ?? 10,
640
1236
  model: input.model ?? "morph-computer-use-v0",
641
- viewport_width: input.viewport_width ?? 1280,
642
- viewport_height: input.viewport_height ?? 720,
643
- external_id: input.external_id,
644
- repo_id: input.repo_id,
645
- commit_id: input.commit_id,
646
- record_video: input.record_video ?? false,
647
- video_width: input.video_width ?? input.viewport_width ?? 1280,
648
- video_height: input.video_height ?? input.viewport_height ?? 720,
649
- allow_resizing: input.allow_resizing ?? false,
650
- structured_output: input.structured_output,
651
- auth: input.auth
1237
+ viewport_width: input.viewportWidth ?? 1280,
1238
+ viewport_height: input.viewportHeight ?? 720,
1239
+ external_id: input.externalId,
1240
+ repo_id: input.repoId,
1241
+ commit_id: input.commitId,
1242
+ record_video: input.recordVideo ?? false,
1243
+ video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
1244
+ video_height: input.videoHeight ?? input.viewportHeight ?? 720,
1245
+ allow_resizing: input.allowResizing ?? false,
1246
+ structured_output: input.structuredOutput,
1247
+ auth: input.auth,
1248
+ profile_id: input.profileId
652
1249
  })
653
1250
  },
654
1251
  config.retryConfig
@@ -656,17 +1253,17 @@ async function executeBrowserTask(input, config = {}) {
656
1253
  const response = await withTimeout(
657
1254
  fetchPromise,
658
1255
  timeout,
659
- `Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing max_steps.`
1256
+ `Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing maxSteps.`
660
1257
  );
661
1258
  if (!response.ok) {
662
1259
  const errorText = await response.text().catch(() => response.statusText);
663
1260
  if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
664
1261
  throw new Error(`HTTP ${response.status}: ${errorText}`);
665
1262
  }
666
- const result = await response.json();
1263
+ const result = mapTaskResult(await response.json());
667
1264
  const elapsed = Date.now() - startTime;
668
1265
  if (debug) {
669
- console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.steps_taken ?? 0} recordingId=${result.recording_id ?? "none"}`);
1266
+ console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.stepsTaken ?? 0} recordingId=${result.recordingId ?? "none"}`);
670
1267
  }
671
1268
  return result;
672
1269
  } catch (error) {
@@ -704,7 +1301,7 @@ async function getRecording(recordingId, config = {}) {
704
1301
  if (debug) console.error(`[Browser] getRecording error: ${response.status} - ${errorText}`);
705
1302
  throw new Error(`HTTP ${response.status}: ${errorText}`);
706
1303
  }
707
- const data = await response.json();
1304
+ const data = mapRecordingStatus(await response.json());
708
1305
  if (debug) console.log(`[Browser] Recording status: ${data.status}`);
709
1306
  return {
710
1307
  ...data,
@@ -727,10 +1324,10 @@ async function waitForRecording(recordingId, config = {}, options = {}) {
727
1324
  }
728
1325
  async function executeWithRecording(input, config = {}) {
729
1326
  const taskResult = await executeBrowserTask(input, config);
730
- if (taskResult.recording_id) {
1327
+ if (taskResult.recordingId) {
731
1328
  try {
732
1329
  const recording = await waitForRecording(
733
- taskResult.recording_id,
1330
+ taskResult.recordingId,
734
1331
  config,
735
1332
  { timeout: 6e4, pollInterval: 2e3 }
736
1333
  );
@@ -740,12 +1337,12 @@ async function executeWithRecording(input, config = {}) {
740
1337
  };
741
1338
  } catch (error) {
742
1339
  const errorRecording = {
743
- id: taskResult.recording_id,
1340
+ id: taskResult.recordingId,
744
1341
  status: "ERROR",
745
1342
  error: error instanceof Error ? error.message : String(error),
746
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
747
- getWebp: (options) => getWebp(taskResult.recording_id, config, options),
748
- getErrors: () => getErrors(taskResult.recording_id, config)
1343
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1344
+ getWebp: (options) => getWebp(taskResult.recordingId, config, options),
1345
+ getErrors: () => getErrors(taskResult.recordingId, config)
749
1346
  };
750
1347
  return {
751
1348
  ...taskResult,
@@ -771,8 +1368,8 @@ async function getErrors(recordingId, config = {}) {
771
1368
  if (debug) console.error(`[Browser] getErrors error: ${response.status} - ${errorText}`);
772
1369
  throw new Error(`HTTP ${response.status}: ${errorText}`);
773
1370
  }
774
- const errors = await response.json();
775
- if (debug) console.log(`[Browser] Found ${errors.total_errors} errors`);
1371
+ const errors = mapErrorsResponse(await response.json());
1372
+ if (debug) console.log(`[Browser] Found ${errors.totalErrors} errors`);
776
1373
  return errors;
777
1374
  }
778
1375
  function stringifyStructuredOutput(schema) {
@@ -805,6 +1402,84 @@ function parseStructuredTaskOutput(result, schema) {
805
1402
  throw error;
806
1403
  }
807
1404
  }
1405
+ function mapTaskResult(api) {
1406
+ if (!api || typeof api !== "object") {
1407
+ return api;
1408
+ }
1409
+ return {
1410
+ success: api.success,
1411
+ result: api.result,
1412
+ error: api.error,
1413
+ stepsTaken: api.steps_taken,
1414
+ executionTimeMs: api.execution_time_ms,
1415
+ urls: api.urls,
1416
+ actionNames: api.action_names,
1417
+ errors: api.errors,
1418
+ modelActions: api.model_actions,
1419
+ isDone: api.is_done,
1420
+ actionHistory: api.action_history,
1421
+ actionResults: api.action_results,
1422
+ hasErrors: api.has_errors,
1423
+ numberOfSteps: api.number_of_steps,
1424
+ judgement: api.judgement,
1425
+ isValidated: api.is_validated,
1426
+ replayId: api.replay_id,
1427
+ replayUrl: api.replay_url,
1428
+ recordingId: api.recording_id,
1429
+ recordingStatus: api.recording_status,
1430
+ taskId: api.task_id,
1431
+ status: api.status,
1432
+ output: api.output,
1433
+ debugUrl: api.debug_url
1434
+ };
1435
+ }
1436
+ function mapRecordingStatus(api) {
1437
+ return {
1438
+ id: api.id,
1439
+ status: api.status,
1440
+ replayUrl: api.replay_url,
1441
+ networkUrl: api.network_url,
1442
+ consoleUrl: api.console_url,
1443
+ videoUrl: api.video_url,
1444
+ totalEvents: api.total_events,
1445
+ fileSize: api.file_size,
1446
+ duration: api.duration,
1447
+ error: api.error,
1448
+ createdAt: api.created_at
1449
+ };
1450
+ }
1451
+ function mapBrowserError(api) {
1452
+ return {
1453
+ type: api.type,
1454
+ message: api.message,
1455
+ url: api.url,
1456
+ timestamp: api.timestamp,
1457
+ screenshotUrl: api.screenshot_url,
1458
+ capturedAt: api.captured_at,
1459
+ status: api.status
1460
+ };
1461
+ }
1462
+ function mapErrorsResponse(api) {
1463
+ return {
1464
+ recordingId: api.recording_id,
1465
+ totalErrors: api.total_errors,
1466
+ errors: Array.isArray(api.errors) ? api.errors.map(mapBrowserError) : []
1467
+ };
1468
+ }
1469
+ function mapWebpResponse(api) {
1470
+ return {
1471
+ webpUrl: api.webp_url,
1472
+ cached: api.cached,
1473
+ width: api.width,
1474
+ fps: api.fps,
1475
+ maxDuration: api.max_duration,
1476
+ fileSize: api.file_size,
1477
+ maxSizeMb: api.max_size_mb,
1478
+ budgetMet: api.budget_met,
1479
+ qualityUsed: api.quality_used,
1480
+ attempts: api.attempts
1481
+ };
1482
+ }
808
1483
  async function getTaskStatus(taskId, config) {
809
1484
  const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
810
1485
  const debug = config.debug || false;
@@ -820,7 +1495,7 @@ async function getTaskStatus(taskId, config) {
820
1495
  if (debug) console.error(`[Browser] getTaskStatus error: ${response.status} - ${errorText}`);
821
1496
  throw new Error(`HTTP ${response.status}: ${errorText}`);
822
1497
  }
823
- const result = await response.json();
1498
+ const result = mapTaskResult(await response.json());
824
1499
  if (debug) console.log(`[Browser] Task status: ${result.status}`);
825
1500
  return result;
826
1501
  }
@@ -843,42 +1518,44 @@ async function pollTaskUntilComplete(taskId, config, pollConfig = {}) {
843
1518
  throw new Error(`Task polling timeout after ${timeout}ms`);
844
1519
  }
845
1520
  function wrapTaskResponse(result, config) {
1521
+ const debugUrl = result.debugUrl ?? "";
846
1522
  const wrapped = {
847
1523
  ...result,
848
- task_id: result.task_id || "",
849
- liveUrl: result.task_id ? generateLiveUrl(result.task_id, config) : result.debugUrl || "",
1524
+ debugUrl,
1525
+ taskId: result.taskId || "",
1526
+ liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
850
1527
  complete: async (pollConfig) => {
851
- if (result.task_id) {
852
- return pollTaskUntilComplete(result.task_id, config, pollConfig);
1528
+ if (result.taskId) {
1529
+ return pollTaskUntilComplete(result.taskId, config, pollConfig);
853
1530
  }
854
- if (result.recording_id) {
1531
+ if (result.recordingId) {
855
1532
  const recording = await waitForRecording(
856
- result.recording_id,
1533
+ result.recordingId,
857
1534
  config,
858
1535
  pollConfig
859
1536
  );
860
1537
  return {
861
1538
  ...result,
862
- recording_status: recording.status
1539
+ recordingStatus: recording.status
863
1540
  };
864
1541
  }
865
- throw new Error("Cannot poll completion: no task_id or recording_id available");
1542
+ throw new Error("Cannot poll completion: no taskId or recordingId available");
866
1543
  },
867
1544
  // Add Steel live session helpers - either functional or error-throwing
868
- getLiveUrl: result.debugUrl ? (options) => buildLiveUrl(result.debugUrl, options) : () => {
1545
+ getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
869
1546
  throw new Error(
870
1547
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
871
1548
  );
872
1549
  },
873
- getLiveIframe: result.debugUrl ? (optionsOrPreset) => {
1550
+ getLiveIframe: debugUrl ? (optionsOrPreset) => {
874
1551
  const options = resolvePreset(optionsOrPreset);
875
- return buildLiveIframe(result.debugUrl, options);
1552
+ return buildLiveIframe(debugUrl, options);
876
1553
  } : () => {
877
1554
  throw new Error(
878
1555
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
879
1556
  );
880
1557
  },
881
- getEmbedCode: result.debugUrl ? () => buildEmbedCode(result.debugUrl) : () => {
1558
+ getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
882
1559
  throw new Error(
883
1560
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
884
1561
  );
@@ -887,44 +1564,46 @@ function wrapTaskResponse(result, config) {
887
1564
  return wrapped;
888
1565
  }
889
1566
  function wrapTaskResponseWithSchema(result, config, schema) {
1567
+ const debugUrl = result.debugUrl ?? "";
890
1568
  const parsed = result.output ? parseStructuredTaskOutput(result, schema) : { ...result, parsed: null };
891
1569
  const wrapped = {
892
1570
  ...parsed,
893
- task_id: result.task_id || "",
894
- liveUrl: result.task_id ? generateLiveUrl(result.task_id, config) : result.debugUrl || "",
1571
+ debugUrl,
1572
+ taskId: result.taskId || "",
1573
+ liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
895
1574
  complete: async (pollConfig) => {
896
- if (result.task_id) {
897
- const finalResult = await pollTaskUntilComplete(result.task_id, config, pollConfig);
1575
+ if (result.taskId) {
1576
+ const finalResult = await pollTaskUntilComplete(result.taskId, config, pollConfig);
898
1577
  return parseStructuredTaskOutput(finalResult, schema);
899
1578
  }
900
- if (result.recording_id) {
1579
+ if (result.recordingId) {
901
1580
  const recording = await waitForRecording(
902
- result.recording_id,
1581
+ result.recordingId,
903
1582
  config,
904
1583
  pollConfig
905
1584
  );
906
1585
  return {
907
1586
  ...parsed,
908
- recording_status: recording.status
1587
+ recordingStatus: recording.status
909
1588
  };
910
1589
  }
911
- throw new Error("Cannot poll completion: no task_id or recording_id available");
1590
+ throw new Error("Cannot poll completion: no taskId or recordingId available");
912
1591
  },
913
1592
  // Add Steel live session helpers - either functional or error-throwing
914
- getLiveUrl: result.debugUrl ? (options) => buildLiveUrl(result.debugUrl, options) : () => {
1593
+ getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
915
1594
  throw new Error(
916
1595
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions. "
917
1596
  );
918
1597
  },
919
- getLiveIframe: result.debugUrl ? (optionsOrPreset) => {
1598
+ getLiveIframe: debugUrl ? (optionsOrPreset) => {
920
1599
  const options = resolvePreset(optionsOrPreset);
921
- return buildLiveIframe(result.debugUrl, options);
1600
+ return buildLiveIframe(debugUrl, options);
922
1601
  } : () => {
923
1602
  throw new Error(
924
1603
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
925
1604
  );
926
1605
  },
927
- getEmbedCode: result.debugUrl ? () => buildEmbedCode(result.debugUrl) : () => {
1606
+ getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
928
1607
  throw new Error(
929
1608
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
930
1609
  );
@@ -939,10 +1618,11 @@ async function getWebp(recordingId, config = {}, options = {}) {
939
1618
  throw new Error("API key required for getWebp");
940
1619
  }
941
1620
  const params = new URLSearchParams();
942
- if (options.max_duration !== void 0) params.set("max_duration", String(options.max_duration));
1621
+ if (options.maxDuration !== void 0) params.set("max_duration", String(options.maxDuration));
943
1622
  if (options.fps !== void 0) params.set("fps", String(options.fps));
944
1623
  if (options.width !== void 0) params.set("width", String(options.width));
945
1624
  if (options.quality !== void 0) params.set("quality", String(options.quality));
1625
+ if (options.maxSizeMb !== void 0) params.set("max_size_mb", String(options.maxSizeMb));
946
1626
  const url = `${apiUrl}/recordings/${recordingId}/webp${params.toString() ? "?" + params.toString() : ""}`;
947
1627
  if (debug) console.log(`[Browser] getWebp: ${url}`);
948
1628
  const response = await fetch(url, {
@@ -954,8 +1634,8 @@ async function getWebp(recordingId, config = {}, options = {}) {
954
1634
  if (debug) console.error(`[Browser] getWebp error: ${response.status} - ${errorText}`);
955
1635
  throw new Error(`HTTP ${response.status}: ${errorText}`);
956
1636
  }
957
- const result = await response.json();
958
- if (debug) console.log(`[Browser] WebP ready: ${result.webp_url} (cached: ${result.cached})`);
1637
+ const result = mapWebpResponse(await response.json());
1638
+ if (debug) console.log(`[Browser] WebP ready: ${result.webpUrl} (cached: ${result.cached})`);
959
1639
  return result;
960
1640
  }
961
1641
  async function checkHealth(config = {}) {
@@ -1851,42 +2531,36 @@ function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS
1851
2531
  }
1852
2532
 
1853
2533
  // tools/warp_grep/agent/runner.ts
2534
+ var import_openai = __toESM(require("openai"), 1);
1854
2535
  var import_path3 = __toESM(require("path"), 1);
1855
2536
  var parser = new LLMResponseParser();
1856
- var DEFAULT_API_URL = "https://api.morphllm.com";
2537
+ var DEFAULT_API_URL2 = "https://api.morphllm.com";
1857
2538
  async function callModel(messages, model, options = {}) {
1858
- const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
2539
+ const baseUrl = options.morphApiUrl || DEFAULT_API_URL2;
1859
2540
  const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
1860
- const fetchPromise = fetchWithRetry(
1861
- `${baseUrl}/v1/chat/completions`,
1862
- {
1863
- method: "POST",
1864
- headers: {
1865
- "Content-Type": "application/json",
1866
- Authorization: `Bearer ${apiKey}`
1867
- },
1868
- body: JSON.stringify({
1869
- model,
1870
- temperature: 0,
1871
- max_tokens: 1024,
1872
- repetition_penalty: 1.05,
1873
- messages
1874
- })
1875
- },
1876
- options.retryConfig
1877
- );
1878
2541
  const timeoutMs = options.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1879
- const resp = await withTimeout(fetchPromise, timeoutMs, "morph-warp-grep request timed out");
1880
- if (!resp.ok) {
1881
- if (resp.status === 404) {
2542
+ const client = new import_openai.default({
2543
+ apiKey,
2544
+ baseURL: baseUrl,
2545
+ maxRetries: options.retryConfig?.maxRetries,
2546
+ timeout: timeoutMs
2547
+ });
2548
+ let data;
2549
+ try {
2550
+ data = await client.chat.completions.create({
2551
+ model,
2552
+ temperature: 0,
2553
+ max_tokens: 1024,
2554
+ messages
2555
+ });
2556
+ } catch (error) {
2557
+ if (error instanceof import_openai.default.APIError && error.status === 404) {
1882
2558
  throw new Error(
1883
2559
  "The endpoint you are trying to call is likely deprecated. Please update with: npm cache clean --force && npx -y @morphllm/morphmcp@latest or visit: https://morphllm.com/mcp"
1884
2560
  );
1885
2561
  }
1886
- const t = await resp.text();
1887
- throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
2562
+ throw error;
1888
2563
  }
1889
- const data = await resp.json();
1890
2564
  const content = data?.choices?.[0]?.message?.content;
1891
2565
  if (!content || typeof content !== "string") {
1892
2566
  throw new Error("Invalid response from model");
@@ -3842,13 +4516,6 @@ function formatResult3(result) {
3842
4516
  changes.linesRemoved && `-${changes.linesRemoved} lines`,
3843
4517
  changes.linesModified && `~${changes.linesModified} lines modified`
3844
4518
  ].filter(Boolean).join(", ");
3845
- if (result.udiff) {
3846
- return `Successfully applied changes to ${result.filepath}:
3847
-
3848
- ${result.udiff}
3849
-
3850
- Summary: ${summary}`;
3851
- }
3852
4519
  return `Successfully applied changes to ${result.filepath}. ${summary}`;
3853
4520
  }
3854
4521
  function createEditFileTool(config = {}) {
@@ -4049,13 +4716,6 @@ function formatResult5(result) {
4049
4716
  changes.linesRemoved && `-${changes.linesRemoved} lines`,
4050
4717
  changes.linesModified && `~${changes.linesModified} lines modified`
4051
4718
  ].filter(Boolean).join(", ");
4052
- if (result.udiff) {
4053
- return `Successfully applied changes to ${result.filepath}:
4054
-
4055
- ${result.udiff}
4056
-
4057
- Summary: ${summary}`;
4058
- }
4059
4719
  return `Successfully applied changes to ${result.filepath}. ${summary}`;
4060
4720
  }
4061
4721
  function createEditFileTool2(config = {}) {