@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/index.cjs CHANGED
@@ -454,7 +454,8 @@ function buildLiveUrl(debugUrl, options = {}) {
454
454
  "debugUrl is required. Ensure your backend returns debugUrl in the task response. Contact support@morphllm.com if you need help."
455
455
  );
456
456
  }
457
- const url = new URL(debugUrl);
457
+ const normalized = normalizeLiveUrl(debugUrl);
458
+ const url = new URL(normalized);
458
459
  if (options.interactive !== void 0) {
459
460
  url.searchParams.set("interactive", String(options.interactive));
460
461
  }
@@ -472,6 +473,29 @@ function buildLiveUrl(debugUrl, options = {}) {
472
473
  }
473
474
  return url.toString();
474
475
  }
476
+ function normalizeLiveUrl(debugUrl) {
477
+ const trimmed = debugUrl.trim();
478
+ if (!trimmed) {
479
+ return trimmed;
480
+ }
481
+ if (trimmed.startsWith("wss://") || trimmed.startsWith("ws://")) {
482
+ return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
483
+ }
484
+ let url;
485
+ try {
486
+ url = new URL(trimmed);
487
+ } catch {
488
+ return trimmed;
489
+ }
490
+ if (url.protocol === "wss:" || url.protocol === "ws:") {
491
+ return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
492
+ }
493
+ const wssParam = url.searchParams.get("wss");
494
+ if (wssParam && (wssParam.startsWith("wss://") || wssParam.startsWith("ws://"))) {
495
+ url.searchParams.set("wss", wssParam);
496
+ }
497
+ return url.toString();
498
+ }
475
499
  function buildLiveIframe(debugUrl, options = {}) {
476
500
  const {
477
501
  width = "100%",
@@ -515,6 +539,570 @@ function resolvePreset(optionsOrPreset) {
515
539
  return optionsOrPreset;
516
540
  }
517
541
 
542
+ // tools/browser/errors.ts
543
+ var MorphError = class extends Error {
544
+ /** Error code for programmatic handling */
545
+ code;
546
+ /** Original cause of the error, if any */
547
+ cause;
548
+ constructor(message, code, cause) {
549
+ super(message);
550
+ this.name = "MorphError";
551
+ this.code = code;
552
+ this.cause = cause;
553
+ if (Error.captureStackTrace) {
554
+ Error.captureStackTrace(this, this.constructor);
555
+ }
556
+ }
557
+ /**
558
+ * Returns a JSON representation of the error for logging.
559
+ */
560
+ toJSON() {
561
+ return {
562
+ name: this.name,
563
+ message: this.message,
564
+ code: this.code,
565
+ cause: this.cause?.message
566
+ };
567
+ }
568
+ };
569
+ var MorphValidationError = class extends MorphError {
570
+ /** The field that failed validation */
571
+ field;
572
+ constructor(message, field) {
573
+ super(message, "validation_error");
574
+ this.name = "MorphValidationError";
575
+ this.field = field;
576
+ }
577
+ toJSON() {
578
+ return {
579
+ ...super.toJSON(),
580
+ field: this.field
581
+ };
582
+ }
583
+ };
584
+ var MorphAPIError = class extends MorphError {
585
+ /** HTTP status code */
586
+ statusCode;
587
+ /** Request ID for debugging (if available) */
588
+ requestId;
589
+ /** Raw response body */
590
+ rawResponse;
591
+ constructor(message, code, statusCode, options) {
592
+ super(message, code, options?.cause);
593
+ this.name = "MorphAPIError";
594
+ this.statusCode = statusCode;
595
+ this.requestId = options?.requestId;
596
+ this.rawResponse = options?.rawResponse;
597
+ }
598
+ toJSON() {
599
+ return {
600
+ ...super.toJSON(),
601
+ statusCode: this.statusCode,
602
+ requestId: this.requestId
603
+ };
604
+ }
605
+ };
606
+ var MorphAuthenticationError = class extends MorphAPIError {
607
+ constructor(message = "Authentication required. Please provide a valid API key.") {
608
+ super(message, "authentication_required", 401);
609
+ this.name = "MorphAuthenticationError";
610
+ }
611
+ };
612
+ var MorphRateLimitError = class extends MorphAPIError {
613
+ /** When the rate limit resets (Unix timestamp) */
614
+ resetAt;
615
+ /** Number of seconds until reset */
616
+ retryAfter;
617
+ constructor(message = "Rate limit exceeded. Please retry later.", options) {
618
+ super(message, "rate_limit_exceeded", 429, { requestId: options?.requestId });
619
+ this.name = "MorphRateLimitError";
620
+ this.resetAt = options?.resetAt;
621
+ this.retryAfter = options?.retryAfter;
622
+ }
623
+ toJSON() {
624
+ return {
625
+ ...super.toJSON(),
626
+ resetAt: this.resetAt,
627
+ retryAfter: this.retryAfter
628
+ };
629
+ }
630
+ };
631
+ var MorphNotFoundError = class extends MorphAPIError {
632
+ /** The type of resource that was not found */
633
+ resourceType;
634
+ /** The ID of the resource that was not found */
635
+ resourceId;
636
+ constructor(resourceType, resourceId) {
637
+ const message = resourceId ? `${resourceType} '${resourceId}' not found` : `${resourceType} not found`;
638
+ super(message, "resource_not_found", 404);
639
+ this.name = "MorphNotFoundError";
640
+ this.resourceType = resourceType;
641
+ this.resourceId = resourceId;
642
+ }
643
+ toJSON() {
644
+ return {
645
+ ...super.toJSON(),
646
+ resourceType: this.resourceType,
647
+ resourceId: this.resourceId
648
+ };
649
+ }
650
+ };
651
+ var MorphProfileLimitError = class extends MorphAPIError {
652
+ /** Current number of profiles */
653
+ currentCount;
654
+ /** Maximum allowed profiles for the plan */
655
+ maxAllowed;
656
+ constructor(message = "Profile limit exceeded for your plan.", options) {
657
+ super(message, "profile_limit_exceeded", 403, { requestId: options?.requestId });
658
+ this.name = "MorphProfileLimitError";
659
+ this.currentCount = options?.currentCount;
660
+ this.maxAllowed = options?.maxAllowed;
661
+ }
662
+ toJSON() {
663
+ return {
664
+ ...super.toJSON(),
665
+ currentCount: this.currentCount,
666
+ maxAllowed: this.maxAllowed
667
+ };
668
+ }
669
+ };
670
+ function parseAPIError(statusCode, responseText, requestId) {
671
+ let errorData = {};
672
+ try {
673
+ errorData = JSON.parse(responseText);
674
+ } catch {
675
+ }
676
+ const message = errorData.detail || errorData.message || responseText || "Unknown error";
677
+ const code = errorData.code;
678
+ switch (statusCode) {
679
+ case 401:
680
+ return new MorphAuthenticationError(message);
681
+ case 403:
682
+ if (code === "profile_limit_exceeded" || message.toLowerCase().includes("limit")) {
683
+ return new MorphProfileLimitError(message, { requestId });
684
+ }
685
+ return new MorphAPIError(message, "insufficient_permissions", statusCode, { requestId, rawResponse: responseText });
686
+ case 404:
687
+ if (message.toLowerCase().includes("profile")) {
688
+ return new MorphNotFoundError("Profile", void 0);
689
+ }
690
+ if (message.toLowerCase().includes("session")) {
691
+ return new MorphNotFoundError("Session", void 0);
692
+ }
693
+ return new MorphAPIError(message, "resource_not_found", statusCode, { requestId, rawResponse: responseText });
694
+ case 429:
695
+ return new MorphRateLimitError(message, { requestId });
696
+ case 422:
697
+ return new MorphAPIError(message, "validation_error", statusCode, { requestId, rawResponse: responseText });
698
+ case 500:
699
+ case 502:
700
+ case 503:
701
+ case 504:
702
+ return new MorphAPIError(message, "service_unavailable", statusCode, { requestId, rawResponse: responseText });
703
+ default:
704
+ return new MorphAPIError(message, "network_error", statusCode, { requestId, rawResponse: responseText });
705
+ }
706
+ }
707
+
708
+ // tools/browser/profiles/types.ts
709
+ function transformProfile(api) {
710
+ return {
711
+ id: api.id,
712
+ name: api.name,
713
+ repoId: api.repo_id,
714
+ cookieDomains: api.cookie_domains,
715
+ lastUsedAt: api.last_used_at,
716
+ createdAt: api.created_at,
717
+ updatedAt: api.updated_at
718
+ };
719
+ }
720
+ function transformCreateInput(input) {
721
+ return {
722
+ name: input.name,
723
+ repo_id: input.repoId
724
+ };
725
+ }
726
+ function transformSession(api) {
727
+ return {
728
+ sessionId: api.session_id,
729
+ debugUrl: api.debug_url || ""
730
+ };
731
+ }
732
+ function transformSaveInput(input) {
733
+ return {
734
+ session_id: input.sessionId,
735
+ profile_id: input.profileId
736
+ };
737
+ }
738
+ function transformStateResponse(api) {
739
+ return {
740
+ profileId: api.profile_id,
741
+ stateUrl: api.state_url,
742
+ expiresIn: api.expires_in
743
+ };
744
+ }
745
+
746
+ // tools/browser/profiles/core.ts
747
+ var DEFAULT_API_URL = process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com";
748
+ var ProfilesClient = class {
749
+ config;
750
+ constructor(config) {
751
+ this.config = config;
752
+ }
753
+ /**
754
+ * Create a new browser profile and immediately start a live session.
755
+ *
756
+ * @param input - Profile creation parameters
757
+ * @returns Profile setup handle with live URL + save()
758
+ * @throws {MorphValidationError} If input validation fails
759
+ * @throws {MorphProfileLimitError} If profile limit is exceeded
760
+ * @throws {MorphAuthenticationError} If API key is missing or invalid
761
+ *
762
+ * @example
763
+ * ```typescript
764
+ * const setup = await morph.browser.profiles.createProfile({
765
+ * name: 'LinkedIn Production',
766
+ * repoId: 'owner/repo'
767
+ * });
768
+ * console.log(setup.session.debugUrl);
769
+ * await setup.save();
770
+ * ```
771
+ */
772
+ async createProfile(input) {
773
+ return createProfile(input, this.config);
774
+ }
775
+ /**
776
+ * List all profiles for the authenticated user.
777
+ *
778
+ * @param repoId - Optional repository ID to filter by
779
+ * @returns Array of profiles
780
+ *
781
+ * @example
782
+ * ```typescript
783
+ * // List all profiles
784
+ * const allProfiles = await morph.browser.profiles.listProfiles();
785
+ *
786
+ * // List profiles for a specific repo
787
+ * const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');
788
+ * ```
789
+ */
790
+ async listProfiles(repoId) {
791
+ return listProfiles(this.config, repoId);
792
+ }
793
+ /**
794
+ * Get a profile by ID with convenience methods.
795
+ *
796
+ * @param id - Profile ID
797
+ * @returns Profile with attached methods
798
+ * @throws {MorphNotFoundError} If profile is not found
799
+ *
800
+ * @example
801
+ * ```typescript
802
+ * const profile = await morph.browser.profiles.getProfile('profile-id');
803
+ * const state = await profile.getState();
804
+ * await profile.delete();
805
+ * ```
806
+ */
807
+ async getProfile(id) {
808
+ return getProfile(id, this.config);
809
+ }
810
+ /**
811
+ * Update a profile by opening a live session (no rename).
812
+ *
813
+ * @param id - Profile ID
814
+ * @returns Profile setup handle with live URL + save()
815
+ */
816
+ async updateProfile(id) {
817
+ return updateProfile(id, this.config);
818
+ }
819
+ /**
820
+ * Delete a profile.
821
+ *
822
+ * @param id - Profile ID
823
+ * @throws {MorphNotFoundError} If profile is not found
824
+ */
825
+ async deleteProfile(id) {
826
+ return deleteProfile(id, this.config);
827
+ }
828
+ /**
829
+ * Start a browser session for profile setup.
830
+ *
831
+ * Returns a live URL where the user can sign into accounts.
832
+ * After signing in, call `saveSession` to persist the state.
833
+ *
834
+ * @param input - Optional session parameters
835
+ * @returns Session with debug URL
836
+ *
837
+ * @example
838
+ * ```typescript
839
+ * const session = await morph.browser.profiles.startSession();
840
+ * console.log('Sign in at:', session.debugUrl);
841
+ * // Open debugUrl in browser, user signs in...
842
+ * await morph.browser.profiles.saveSession(session.sessionId, profile.id);
843
+ * ```
844
+ */
845
+ async startSession(input) {
846
+ return startProfileSession(this.config, input);
847
+ }
848
+ /**
849
+ * Save browser state from a session to a profile.
850
+ *
851
+ * Call this after the user is done signing into accounts.
852
+ * Extracts cookies, localStorage, and sessionStorage.
853
+ *
854
+ * @param sessionId - Browser session ID from startSession
855
+ * @param profileId - Profile ID to save state to
856
+ * @returns Updated profile with cookie domains
857
+ */
858
+ async saveSession(sessionId, profileId) {
859
+ return saveProfileSession({ sessionId, profileId }, this.config);
860
+ }
861
+ /**
862
+ * List available repo IDs (discovery).
863
+ *
864
+ * @returns Repo summaries with profile counts
865
+ */
866
+ async listRepos() {
867
+ return listRepos(this.config);
868
+ }
869
+ /**
870
+ * Get the presigned URL for a profile's state.
871
+ *
872
+ * Use this to download the raw state JSON for debugging
873
+ * or to restore state manually.
874
+ *
875
+ * @param profileId - Profile ID
876
+ * @returns State URL with expiry information
877
+ */
878
+ async getProfileState(profileId) {
879
+ return getProfileState(profileId, this.config);
880
+ }
881
+ };
882
+ function validateCreateInput(input) {
883
+ if (!input.name || typeof input.name !== "string") {
884
+ throw new MorphValidationError("name is required", "name");
885
+ }
886
+ const trimmedName = input.name.trim();
887
+ if (trimmedName.length === 0) {
888
+ throw new MorphValidationError("name cannot be empty", "name");
889
+ }
890
+ if (trimmedName.length > 100) {
891
+ throw new MorphValidationError("name must be 100 characters or less", "name");
892
+ }
893
+ if (!input.repoId || typeof input.repoId !== "string") {
894
+ throw new MorphValidationError("repoId is required", "repoId");
895
+ }
896
+ if (input.repoId.trim().length === 0) {
897
+ throw new MorphValidationError("repoId cannot be empty", "repoId");
898
+ }
899
+ }
900
+ function validateId(id, fieldName) {
901
+ if (!id || typeof id !== "string") {
902
+ throw new MorphValidationError(`${fieldName} is required`, fieldName);
903
+ }
904
+ if (id.trim().length === 0) {
905
+ throw new MorphValidationError(`${fieldName} cannot be empty`, fieldName);
906
+ }
907
+ }
908
+ async function createProfile(input, config = {}) {
909
+ validateCreateInput(input);
910
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
911
+ const headers = buildHeaders(config);
912
+ const response = await fetchWithRetry(
913
+ `${apiUrl}/profiles`,
914
+ {
915
+ method: "POST",
916
+ headers,
917
+ body: JSON.stringify(transformCreateInput(input))
918
+ },
919
+ config.retryConfig
920
+ );
921
+ if (!response.ok) {
922
+ const errorText = await response.text().catch(() => response.statusText);
923
+ const requestId = response.headers.get("x-request-id") || void 0;
924
+ throw parseAPIError(response.status, errorText, requestId);
925
+ }
926
+ const apiProfile = await response.json();
927
+ const profile = transformProfile(apiProfile);
928
+ const session = await startProfileSession(config, { profileId: profile.id });
929
+ return buildProfileSetup(profile, session, config);
930
+ }
931
+ async function listProfiles(config = {}, repoId) {
932
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
933
+ const headers = buildHeaders(config);
934
+ const url = repoId ? `${apiUrl}/profiles?repo_id=${encodeURIComponent(repoId)}` : `${apiUrl}/profiles`;
935
+ const response = await fetchWithRetry(
936
+ url,
937
+ { method: "GET", headers },
938
+ config.retryConfig
939
+ );
940
+ if (!response.ok) {
941
+ const errorText = await response.text().catch(() => response.statusText);
942
+ const requestId = response.headers.get("x-request-id") || void 0;
943
+ throw parseAPIError(response.status, errorText, requestId);
944
+ }
945
+ const data = await response.json();
946
+ return data.profiles.map(transformProfile);
947
+ }
948
+ async function getProfile(id, config = {}) {
949
+ validateId(id, "id");
950
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
951
+ const headers = buildHeaders(config);
952
+ const response = await fetchWithRetry(
953
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
954
+ { method: "GET", headers },
955
+ config.retryConfig
956
+ );
957
+ if (!response.ok) {
958
+ const errorText = await response.text().catch(() => response.statusText);
959
+ const requestId = response.headers.get("x-request-id") || void 0;
960
+ throw parseAPIError(response.status, errorText, requestId);
961
+ }
962
+ const apiProfile = await response.json();
963
+ const profile = transformProfile(apiProfile);
964
+ return {
965
+ ...profile,
966
+ getState: () => getProfileState(id, config),
967
+ delete: () => deleteProfile(id, config)
968
+ };
969
+ }
970
+ async function updateProfile(id, config = {}) {
971
+ validateId(id, "id");
972
+ const profile = await fetchProfile(id, config);
973
+ const session = await startProfileSession(config, { profileId: profile.id });
974
+ return buildProfileSetup(profile, session, config);
975
+ }
976
+ async function deleteProfile(id, config = {}) {
977
+ validateId(id, "id");
978
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
979
+ const headers = buildHeaders(config);
980
+ const response = await fetchWithRetry(
981
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
982
+ { method: "DELETE", headers },
983
+ config.retryConfig
984
+ );
985
+ if (!response.ok) {
986
+ const errorText = await response.text().catch(() => response.statusText);
987
+ const requestId = response.headers.get("x-request-id") || void 0;
988
+ throw parseAPIError(response.status, errorText, requestId);
989
+ }
990
+ }
991
+ async function startProfileSession(config = {}, input) {
992
+ if (!config.apiKey) {
993
+ throw new MorphAuthenticationError();
994
+ }
995
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
996
+ const headers = buildHeaders(config);
997
+ const body = input?.profileId ? { profile_id: input.profileId } : {};
998
+ const response = await fetchWithRetry(
999
+ `${apiUrl}/profiles/session/start`,
1000
+ {
1001
+ method: "POST",
1002
+ headers,
1003
+ body: JSON.stringify(body)
1004
+ },
1005
+ config.retryConfig
1006
+ );
1007
+ if (!response.ok) {
1008
+ const errorText = await response.text().catch(() => response.statusText);
1009
+ const requestId = response.headers.get("x-request-id") || void 0;
1010
+ throw parseAPIError(response.status, errorText, requestId);
1011
+ }
1012
+ const apiSession = await response.json();
1013
+ return transformSession(apiSession);
1014
+ }
1015
+ async function saveProfileSession(input, config = {}) {
1016
+ validateId(input.sessionId, "sessionId");
1017
+ validateId(input.profileId, "profileId");
1018
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1019
+ const headers = buildHeaders(config);
1020
+ const response = await fetchWithRetry(
1021
+ `${apiUrl}/profiles/session/save`,
1022
+ {
1023
+ method: "POST",
1024
+ headers,
1025
+ body: JSON.stringify(transformSaveInput(input))
1026
+ },
1027
+ config.retryConfig
1028
+ );
1029
+ if (!response.ok) {
1030
+ const errorText = await response.text().catch(() => response.statusText);
1031
+ const requestId = response.headers.get("x-request-id") || void 0;
1032
+ throw parseAPIError(response.status, errorText, requestId);
1033
+ }
1034
+ const apiProfile = await response.json();
1035
+ return transformProfile(apiProfile);
1036
+ }
1037
+ async function listRepos(config = {}) {
1038
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1039
+ const headers = buildHeaders(config);
1040
+ const response = await fetchWithRetry(
1041
+ `${apiUrl}/repos`,
1042
+ { method: "GET", headers },
1043
+ config.retryConfig
1044
+ );
1045
+ if (!response.ok) {
1046
+ const errorText = await response.text().catch(() => response.statusText);
1047
+ const requestId = response.headers.get("x-request-id") || void 0;
1048
+ throw parseAPIError(response.status, errorText, requestId);
1049
+ }
1050
+ const data = await response.json();
1051
+ const repos = Array.isArray(data?.repos) ? data.repos : [];
1052
+ return repos.map((repo) => ({
1053
+ repoId: repo.repo_id,
1054
+ repoFullName: repo.repo_full_name,
1055
+ profileCount: repo.profile_count ?? 0
1056
+ }));
1057
+ }
1058
+ async function getProfileState(profileId, config = {}) {
1059
+ validateId(profileId, "profileId");
1060
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1061
+ const headers = buildHeaders(config);
1062
+ const response = await fetchWithRetry(
1063
+ `${apiUrl}/profiles/${encodeURIComponent(profileId)}/state`,
1064
+ { method: "GET", headers },
1065
+ config.retryConfig
1066
+ );
1067
+ if (!response.ok) {
1068
+ const errorText = await response.text().catch(() => response.statusText);
1069
+ const requestId = response.headers.get("x-request-id") || void 0;
1070
+ throw parseAPIError(response.status, errorText, requestId);
1071
+ }
1072
+ const apiState = await response.json();
1073
+ return transformStateResponse(apiState);
1074
+ }
1075
+ function buildHeaders(config) {
1076
+ const headers = { "Content-Type": "application/json" };
1077
+ if (config.apiKey) {
1078
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
1079
+ }
1080
+ return headers;
1081
+ }
1082
+ async function fetchProfile(id, config) {
1083
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
1084
+ const headers = buildHeaders(config);
1085
+ const response = await fetchWithRetry(
1086
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
1087
+ { method: "GET", headers },
1088
+ config.retryConfig
1089
+ );
1090
+ if (!response.ok) {
1091
+ const errorText = await response.text().catch(() => response.statusText);
1092
+ const requestId = response.headers.get("x-request-id") || void 0;
1093
+ throw parseAPIError(response.status, errorText, requestId);
1094
+ }
1095
+ const apiProfile = await response.json();
1096
+ return transformProfile(apiProfile);
1097
+ }
1098
+ function buildProfileSetup(profile, session, config) {
1099
+ return {
1100
+ profile,
1101
+ session,
1102
+ save: () => saveProfileSession({ sessionId: session.sessionId, profileId: profile.id }, config)
1103
+ };
1104
+ }
1105
+
518
1106
  // tools/browser/core.ts
519
1107
  var DEFAULT_CONFIG2 = {
520
1108
  apiUrl: process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com",
@@ -524,11 +1112,16 @@ var DEFAULT_CONFIG2 = {
524
1112
  };
525
1113
  var BrowserClient = class {
526
1114
  config;
1115
+ /**
1116
+ * Profile management - create and manage browser profiles for storing login state.
1117
+ */
1118
+ profiles;
527
1119
  constructor(config = {}) {
528
1120
  this.config = {
529
1121
  ...DEFAULT_CONFIG2,
530
1122
  ...config
531
1123
  };
1124
+ this.profiles = new ProfilesClient(this.config);
532
1125
  }
533
1126
  /**
534
1127
  * Execute a browser automation task
@@ -551,19 +1144,21 @@ var BrowserClient = class {
551
1144
  body: JSON.stringify({
552
1145
  task: input.task,
553
1146
  url: input.url,
554
- max_steps: input.max_steps ?? 10,
1147
+ max_steps: input.maxSteps ?? 10,
555
1148
  model: input.model ?? "morph-computer-use-v0",
556
- viewport_width: input.viewport_width ?? 1280,
557
- viewport_height: input.viewport_height ?? 720,
558
- external_id: input.external_id,
559
- repo_id: input.repo_id,
560
- commit_id: input.commit_id,
561
- record_video: input.record_video ?? false,
562
- video_width: input.video_width ?? input.viewport_width ?? 1280,
563
- video_height: input.video_height ?? input.viewport_height ?? 720,
564
- allow_resizing: input.allow_resizing ?? false,
1149
+ viewport_width: input.viewportWidth ?? 1280,
1150
+ viewport_height: input.viewportHeight ?? 720,
1151
+ external_id: input.externalId,
1152
+ repo_id: input.repoId,
1153
+ repo_full_name: input.repoFullName,
1154
+ commit_id: input.commitId,
1155
+ record_video: input.recordVideo ?? false,
1156
+ video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
1157
+ video_height: input.videoHeight ?? input.viewportHeight ?? 720,
1158
+ allow_resizing: input.allowResizing ?? false,
565
1159
  structured_output: "schema" in input ? stringifyStructuredOutput(input.schema) : void 0,
566
- auth: input.auth
1160
+ auth: input.auth,
1161
+ profile_id: input.profileId
567
1162
  })
568
1163
  });
569
1164
  if (!response.ok) {
@@ -571,9 +1166,10 @@ var BrowserClient = class {
571
1166
  if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
572
1167
  throw new Error(`HTTP ${response.status}: ${errorText}`);
573
1168
  }
574
- const result = await response.json();
1169
+ const result = mapTaskResult(await response.json());
575
1170
  if (debug) {
576
- console.log(`[Browser] \u2705 Task created: recording_id=${result.recording_id ?? "none"} debug_url=${result.debugUrl ? "available" : "none"}`);
1171
+ const debugUrl = result.debugUrl;
1172
+ console.log(`[Browser] \u2705 Task created: recordingId=${result.recordingId ?? "none"} debugUrl=${debugUrl ? "available" : "none"}`);
577
1173
  }
578
1174
  if ("schema" in input) {
579
1175
  return wrapTaskResponseWithSchema(result, this.config, input.schema);
@@ -628,15 +1224,15 @@ async function executeBrowserTask(input, config = {}) {
628
1224
  error: 'Task description is required. Example: "Go to example.com and click the login button"'
629
1225
  };
630
1226
  }
631
- if (input.max_steps !== void 0 && (input.max_steps < 1 || input.max_steps > 50)) {
1227
+ if (input.maxSteps !== void 0 && (input.maxSteps < 1 || input.maxSteps > 50)) {
632
1228
  return {
633
1229
  success: false,
634
- error: "max_steps must be between 1 and 50. Use more steps for complex multi-page flows."
1230
+ error: "maxSteps must be between 1 and 50. Use more steps for complex multi-page flows."
635
1231
  };
636
1232
  }
637
1233
  if (debug) {
638
- console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.max_steps ?? 10}`);
639
- console.log(`[Browser] Recording: ${input.record_video ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
1234
+ console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.maxSteps ?? 10}`);
1235
+ console.log(`[Browser] Recording: ${input.recordVideo ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
640
1236
  }
641
1237
  const startTime = Date.now();
642
1238
  try {
@@ -650,19 +1246,20 @@ async function executeBrowserTask(input, config = {}) {
650
1246
  body: JSON.stringify({
651
1247
  task: input.task,
652
1248
  url: input.url,
653
- max_steps: input.max_steps ?? 10,
1249
+ max_steps: input.maxSteps ?? 10,
654
1250
  model: input.model ?? "morph-computer-use-v0",
655
- viewport_width: input.viewport_width ?? 1280,
656
- viewport_height: input.viewport_height ?? 720,
657
- external_id: input.external_id,
658
- repo_id: input.repo_id,
659
- commit_id: input.commit_id,
660
- record_video: input.record_video ?? false,
661
- video_width: input.video_width ?? input.viewport_width ?? 1280,
662
- video_height: input.video_height ?? input.viewport_height ?? 720,
663
- allow_resizing: input.allow_resizing ?? false,
664
- structured_output: input.structured_output,
665
- auth: input.auth
1251
+ viewport_width: input.viewportWidth ?? 1280,
1252
+ viewport_height: input.viewportHeight ?? 720,
1253
+ external_id: input.externalId,
1254
+ repo_id: input.repoId,
1255
+ commit_id: input.commitId,
1256
+ record_video: input.recordVideo ?? false,
1257
+ video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
1258
+ video_height: input.videoHeight ?? input.viewportHeight ?? 720,
1259
+ allow_resizing: input.allowResizing ?? false,
1260
+ structured_output: input.structuredOutput,
1261
+ auth: input.auth,
1262
+ profile_id: input.profileId
666
1263
  })
667
1264
  },
668
1265
  config.retryConfig
@@ -670,17 +1267,17 @@ async function executeBrowserTask(input, config = {}) {
670
1267
  const response = await withTimeout(
671
1268
  fetchPromise,
672
1269
  timeout,
673
- `Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing max_steps.`
1270
+ `Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing maxSteps.`
674
1271
  );
675
1272
  if (!response.ok) {
676
1273
  const errorText = await response.text().catch(() => response.statusText);
677
1274
  if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
678
1275
  throw new Error(`HTTP ${response.status}: ${errorText}`);
679
1276
  }
680
- const result = await response.json();
1277
+ const result = mapTaskResult(await response.json());
681
1278
  const elapsed = Date.now() - startTime;
682
1279
  if (debug) {
683
- console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.steps_taken ?? 0} recordingId=${result.recording_id ?? "none"}`);
1280
+ console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.stepsTaken ?? 0} recordingId=${result.recordingId ?? "none"}`);
684
1281
  }
685
1282
  return result;
686
1283
  } catch (error) {
@@ -718,7 +1315,7 @@ async function getRecording(recordingId, config = {}) {
718
1315
  if (debug) console.error(`[Browser] getRecording error: ${response.status} - ${errorText}`);
719
1316
  throw new Error(`HTTP ${response.status}: ${errorText}`);
720
1317
  }
721
- const data = await response.json();
1318
+ const data = mapRecordingStatus(await response.json());
722
1319
  if (debug) console.log(`[Browser] Recording status: ${data.status}`);
723
1320
  return {
724
1321
  ...data,
@@ -741,10 +1338,10 @@ async function waitForRecording(recordingId, config = {}, options = {}) {
741
1338
  }
742
1339
  async function executeWithRecording(input, config = {}) {
743
1340
  const taskResult = await executeBrowserTask(input, config);
744
- if (taskResult.recording_id) {
1341
+ if (taskResult.recordingId) {
745
1342
  try {
746
1343
  const recording = await waitForRecording(
747
- taskResult.recording_id,
1344
+ taskResult.recordingId,
748
1345
  config,
749
1346
  { timeout: 6e4, pollInterval: 2e3 }
750
1347
  );
@@ -754,12 +1351,12 @@ async function executeWithRecording(input, config = {}) {
754
1351
  };
755
1352
  } catch (error) {
756
1353
  const errorRecording = {
757
- id: taskResult.recording_id,
1354
+ id: taskResult.recordingId,
758
1355
  status: "ERROR",
759
1356
  error: error instanceof Error ? error.message : String(error),
760
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
761
- getWebp: (options) => getWebp(taskResult.recording_id, config, options),
762
- getErrors: () => getErrors(taskResult.recording_id, config)
1357
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1358
+ getWebp: (options) => getWebp(taskResult.recordingId, config, options),
1359
+ getErrors: () => getErrors(taskResult.recordingId, config)
763
1360
  };
764
1361
  return {
765
1362
  ...taskResult,
@@ -785,8 +1382,8 @@ async function getErrors(recordingId, config = {}) {
785
1382
  if (debug) console.error(`[Browser] getErrors error: ${response.status} - ${errorText}`);
786
1383
  throw new Error(`HTTP ${response.status}: ${errorText}`);
787
1384
  }
788
- const errors = await response.json();
789
- if (debug) console.log(`[Browser] Found ${errors.total_errors} errors`);
1385
+ const errors = mapErrorsResponse(await response.json());
1386
+ if (debug) console.log(`[Browser] Found ${errors.totalErrors} errors`);
790
1387
  return errors;
791
1388
  }
792
1389
  function stringifyStructuredOutput(schema) {
@@ -819,6 +1416,84 @@ function parseStructuredTaskOutput(result, schema) {
819
1416
  throw error;
820
1417
  }
821
1418
  }
1419
+ function mapTaskResult(api) {
1420
+ if (!api || typeof api !== "object") {
1421
+ return api;
1422
+ }
1423
+ return {
1424
+ success: api.success,
1425
+ result: api.result,
1426
+ error: api.error,
1427
+ stepsTaken: api.steps_taken,
1428
+ executionTimeMs: api.execution_time_ms,
1429
+ urls: api.urls,
1430
+ actionNames: api.action_names,
1431
+ errors: api.errors,
1432
+ modelActions: api.model_actions,
1433
+ isDone: api.is_done,
1434
+ actionHistory: api.action_history,
1435
+ actionResults: api.action_results,
1436
+ hasErrors: api.has_errors,
1437
+ numberOfSteps: api.number_of_steps,
1438
+ judgement: api.judgement,
1439
+ isValidated: api.is_validated,
1440
+ replayId: api.replay_id,
1441
+ replayUrl: api.replay_url,
1442
+ recordingId: api.recording_id,
1443
+ recordingStatus: api.recording_status,
1444
+ taskId: api.task_id,
1445
+ status: api.status,
1446
+ output: api.output,
1447
+ debugUrl: api.debug_url
1448
+ };
1449
+ }
1450
+ function mapRecordingStatus(api) {
1451
+ return {
1452
+ id: api.id,
1453
+ status: api.status,
1454
+ replayUrl: api.replay_url,
1455
+ networkUrl: api.network_url,
1456
+ consoleUrl: api.console_url,
1457
+ videoUrl: api.video_url,
1458
+ totalEvents: api.total_events,
1459
+ fileSize: api.file_size,
1460
+ duration: api.duration,
1461
+ error: api.error,
1462
+ createdAt: api.created_at
1463
+ };
1464
+ }
1465
+ function mapBrowserError(api) {
1466
+ return {
1467
+ type: api.type,
1468
+ message: api.message,
1469
+ url: api.url,
1470
+ timestamp: api.timestamp,
1471
+ screenshotUrl: api.screenshot_url,
1472
+ capturedAt: api.captured_at,
1473
+ status: api.status
1474
+ };
1475
+ }
1476
+ function mapErrorsResponse(api) {
1477
+ return {
1478
+ recordingId: api.recording_id,
1479
+ totalErrors: api.total_errors,
1480
+ errors: Array.isArray(api.errors) ? api.errors.map(mapBrowserError) : []
1481
+ };
1482
+ }
1483
+ function mapWebpResponse(api) {
1484
+ return {
1485
+ webpUrl: api.webp_url,
1486
+ cached: api.cached,
1487
+ width: api.width,
1488
+ fps: api.fps,
1489
+ maxDuration: api.max_duration,
1490
+ fileSize: api.file_size,
1491
+ maxSizeMb: api.max_size_mb,
1492
+ budgetMet: api.budget_met,
1493
+ qualityUsed: api.quality_used,
1494
+ attempts: api.attempts
1495
+ };
1496
+ }
822
1497
  async function getTaskStatus(taskId, config) {
823
1498
  const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
824
1499
  const debug = config.debug || false;
@@ -834,7 +1509,7 @@ async function getTaskStatus(taskId, config) {
834
1509
  if (debug) console.error(`[Browser] getTaskStatus error: ${response.status} - ${errorText}`);
835
1510
  throw new Error(`HTTP ${response.status}: ${errorText}`);
836
1511
  }
837
- const result = await response.json();
1512
+ const result = mapTaskResult(await response.json());
838
1513
  if (debug) console.log(`[Browser] Task status: ${result.status}`);
839
1514
  return result;
840
1515
  }
@@ -857,42 +1532,44 @@ async function pollTaskUntilComplete(taskId, config, pollConfig = {}) {
857
1532
  throw new Error(`Task polling timeout after ${timeout}ms`);
858
1533
  }
859
1534
  function wrapTaskResponse(result, config) {
1535
+ const debugUrl = result.debugUrl ?? "";
860
1536
  const wrapped = {
861
1537
  ...result,
862
- task_id: result.task_id || "",
863
- liveUrl: result.task_id ? generateLiveUrl(result.task_id, config) : result.debugUrl || "",
1538
+ debugUrl,
1539
+ taskId: result.taskId || "",
1540
+ liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
864
1541
  complete: async (pollConfig) => {
865
- if (result.task_id) {
866
- return pollTaskUntilComplete(result.task_id, config, pollConfig);
1542
+ if (result.taskId) {
1543
+ return pollTaskUntilComplete(result.taskId, config, pollConfig);
867
1544
  }
868
- if (result.recording_id) {
1545
+ if (result.recordingId) {
869
1546
  const recording = await waitForRecording(
870
- result.recording_id,
1547
+ result.recordingId,
871
1548
  config,
872
1549
  pollConfig
873
1550
  );
874
1551
  return {
875
1552
  ...result,
876
- recording_status: recording.status
1553
+ recordingStatus: recording.status
877
1554
  };
878
1555
  }
879
- throw new Error("Cannot poll completion: no task_id or recording_id available");
1556
+ throw new Error("Cannot poll completion: no taskId or recordingId available");
880
1557
  },
881
1558
  // Add Steel live session helpers - either functional or error-throwing
882
- getLiveUrl: result.debugUrl ? (options) => buildLiveUrl(result.debugUrl, options) : () => {
1559
+ getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
883
1560
  throw new Error(
884
1561
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
885
1562
  );
886
1563
  },
887
- getLiveIframe: result.debugUrl ? (optionsOrPreset) => {
1564
+ getLiveIframe: debugUrl ? (optionsOrPreset) => {
888
1565
  const options = resolvePreset(optionsOrPreset);
889
- return buildLiveIframe(result.debugUrl, options);
1566
+ return buildLiveIframe(debugUrl, options);
890
1567
  } : () => {
891
1568
  throw new Error(
892
1569
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
893
1570
  );
894
1571
  },
895
- getEmbedCode: result.debugUrl ? () => buildEmbedCode(result.debugUrl) : () => {
1572
+ getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
896
1573
  throw new Error(
897
1574
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
898
1575
  );
@@ -901,44 +1578,46 @@ function wrapTaskResponse(result, config) {
901
1578
  return wrapped;
902
1579
  }
903
1580
  function wrapTaskResponseWithSchema(result, config, schema) {
1581
+ const debugUrl = result.debugUrl ?? "";
904
1582
  const parsed = result.output ? parseStructuredTaskOutput(result, schema) : { ...result, parsed: null };
905
1583
  const wrapped = {
906
1584
  ...parsed,
907
- task_id: result.task_id || "",
908
- liveUrl: result.task_id ? generateLiveUrl(result.task_id, config) : result.debugUrl || "",
1585
+ debugUrl,
1586
+ taskId: result.taskId || "",
1587
+ liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
909
1588
  complete: async (pollConfig) => {
910
- if (result.task_id) {
911
- const finalResult = await pollTaskUntilComplete(result.task_id, config, pollConfig);
1589
+ if (result.taskId) {
1590
+ const finalResult = await pollTaskUntilComplete(result.taskId, config, pollConfig);
912
1591
  return parseStructuredTaskOutput(finalResult, schema);
913
1592
  }
914
- if (result.recording_id) {
1593
+ if (result.recordingId) {
915
1594
  const recording = await waitForRecording(
916
- result.recording_id,
1595
+ result.recordingId,
917
1596
  config,
918
1597
  pollConfig
919
1598
  );
920
1599
  return {
921
1600
  ...parsed,
922
- recording_status: recording.status
1601
+ recordingStatus: recording.status
923
1602
  };
924
1603
  }
925
- throw new Error("Cannot poll completion: no task_id or recording_id available");
1604
+ throw new Error("Cannot poll completion: no taskId or recordingId available");
926
1605
  },
927
1606
  // Add Steel live session helpers - either functional or error-throwing
928
- getLiveUrl: result.debugUrl ? (options) => buildLiveUrl(result.debugUrl, options) : () => {
1607
+ getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
929
1608
  throw new Error(
930
1609
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions. "
931
1610
  );
932
1611
  },
933
- getLiveIframe: result.debugUrl ? (optionsOrPreset) => {
1612
+ getLiveIframe: debugUrl ? (optionsOrPreset) => {
934
1613
  const options = resolvePreset(optionsOrPreset);
935
- return buildLiveIframe(result.debugUrl, options);
1614
+ return buildLiveIframe(debugUrl, options);
936
1615
  } : () => {
937
1616
  throw new Error(
938
1617
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
939
1618
  );
940
1619
  },
941
- getEmbedCode: result.debugUrl ? () => buildEmbedCode(result.debugUrl) : () => {
1620
+ getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
942
1621
  throw new Error(
943
1622
  "Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
944
1623
  );
@@ -953,10 +1632,11 @@ async function getWebp(recordingId, config = {}, options = {}) {
953
1632
  throw new Error("API key required for getWebp");
954
1633
  }
955
1634
  const params = new URLSearchParams();
956
- if (options.max_duration !== void 0) params.set("max_duration", String(options.max_duration));
1635
+ if (options.maxDuration !== void 0) params.set("max_duration", String(options.maxDuration));
957
1636
  if (options.fps !== void 0) params.set("fps", String(options.fps));
958
1637
  if (options.width !== void 0) params.set("width", String(options.width));
959
1638
  if (options.quality !== void 0) params.set("quality", String(options.quality));
1639
+ if (options.maxSizeMb !== void 0) params.set("max_size_mb", String(options.maxSizeMb));
960
1640
  const url = `${apiUrl}/recordings/${recordingId}/webp${params.toString() ? "?" + params.toString() : ""}`;
961
1641
  if (debug) console.log(`[Browser] getWebp: ${url}`);
962
1642
  const response = await fetch(url, {
@@ -968,8 +1648,8 @@ async function getWebp(recordingId, config = {}, options = {}) {
968
1648
  if (debug) console.error(`[Browser] getWebp error: ${response.status} - ${errorText}`);
969
1649
  throw new Error(`HTTP ${response.status}: ${errorText}`);
970
1650
  }
971
- const result = await response.json();
972
- if (debug) console.log(`[Browser] WebP ready: ${result.webp_url} (cached: ${result.cached})`);
1651
+ const result = mapWebpResponse(await response.json());
1652
+ if (debug) console.log(`[Browser] WebP ready: ${result.webpUrl} (cached: ${result.cached})`);
973
1653
  return result;
974
1654
  }
975
1655
  async function checkHealth(config = {}) {
@@ -1865,42 +2545,36 @@ function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS
1865
2545
  }
1866
2546
 
1867
2547
  // tools/warp_grep/agent/runner.ts
2548
+ var import_openai = __toESM(require("openai"), 1);
1868
2549
  var import_path3 = __toESM(require("path"), 1);
1869
2550
  var parser = new LLMResponseParser();
1870
- var DEFAULT_API_URL = "https://api.morphllm.com";
2551
+ var DEFAULT_API_URL2 = "https://api.morphllm.com";
1871
2552
  async function callModel(messages, model, options = {}) {
1872
- const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
2553
+ const baseUrl = options.morphApiUrl || DEFAULT_API_URL2;
1873
2554
  const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
1874
- const fetchPromise = fetchWithRetry(
1875
- `${baseUrl}/v1/chat/completions`,
1876
- {
1877
- method: "POST",
1878
- headers: {
1879
- "Content-Type": "application/json",
1880
- Authorization: `Bearer ${apiKey}`
1881
- },
1882
- body: JSON.stringify({
1883
- model,
1884
- temperature: 0,
1885
- max_tokens: 1024,
1886
- repetition_penalty: 1.05,
1887
- messages
1888
- })
1889
- },
1890
- options.retryConfig
1891
- );
1892
2555
  const timeoutMs = options.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1893
- const resp = await withTimeout(fetchPromise, timeoutMs, "morph-warp-grep request timed out");
1894
- if (!resp.ok) {
1895
- if (resp.status === 404) {
2556
+ const client = new import_openai.default({
2557
+ apiKey,
2558
+ baseURL: baseUrl,
2559
+ maxRetries: options.retryConfig?.maxRetries,
2560
+ timeout: timeoutMs
2561
+ });
2562
+ let data;
2563
+ try {
2564
+ data = await client.chat.completions.create({
2565
+ model,
2566
+ temperature: 0,
2567
+ max_tokens: 1024,
2568
+ messages
2569
+ });
2570
+ } catch (error) {
2571
+ if (error instanceof import_openai.default.APIError && error.status === 404) {
1896
2572
  throw new Error(
1897
2573
  "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"
1898
2574
  );
1899
2575
  }
1900
- const t = await resp.text();
1901
- throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
2576
+ throw error;
1902
2577
  }
1903
- const data = await resp.json();
1904
2578
  const content = data?.choices?.[0]?.message?.content;
1905
2579
  if (!content || typeof content !== "string") {
1906
2580
  throw new Error("Invalid response from model");
@@ -3856,13 +4530,6 @@ function formatResult3(result) {
3856
4530
  changes.linesRemoved && `-${changes.linesRemoved} lines`,
3857
4531
  changes.linesModified && `~${changes.linesModified} lines modified`
3858
4532
  ].filter(Boolean).join(", ");
3859
- if (result.udiff) {
3860
- return `Successfully applied changes to ${result.filepath}:
3861
-
3862
- ${result.udiff}
3863
-
3864
- Summary: ${summary}`;
3865
- }
3866
4533
  return `Successfully applied changes to ${result.filepath}. ${summary}`;
3867
4534
  }
3868
4535
  function createEditFileTool(config = {}) {
@@ -4063,13 +4730,6 @@ function formatResult5(result) {
4063
4730
  changes.linesRemoved && `-${changes.linesRemoved} lines`,
4064
4731
  changes.linesModified && `~${changes.linesModified} lines modified`
4065
4732
  ].filter(Boolean).join(", ");
4066
- if (result.udiff) {
4067
- return `Successfully applied changes to ${result.filepath}:
4068
-
4069
- ${result.udiff}
4070
-
4071
- Summary: ${summary}`;
4072
- }
4073
4733
  return `Successfully applied changes to ${result.filepath}. ${summary}`;
4074
4734
  }
4075
4735
  function createEditFileTool2(config = {}) {