@runcofactor/cli 0.16.0 → 0.17.0

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 (123) hide show
  1. package/dist/{account-ZMV7NEFM.js → account-ZSM2Q3RB.js} +4 -4
  2. package/dist/{api-catalog-6HNOGVPA.js → api-catalog-MMPFWS52.js} +4 -4
  3. package/dist/{attachment-QAHR54HQ.js → attachment-NSYL5X3S.js} +4 -4
  4. package/dist/{augmentor-7T3L6ZR4.js → augmentor-LSJLMJYP.js} +4 -4
  5. package/dist/{auth-DURHUZDW.js → auth-OYF5A4BY.js} +4 -2
  6. package/dist/{chunk-4NRDONSC.js → chunk-FGB6PS2T.js} +4 -4
  7. package/dist/chunk-FGB6PS2T.js.map +1 -0
  8. package/dist/{chunk-D6AVOPOS.js → chunk-GFP4UEUT.js} +3 -3
  9. package/dist/{chunk-G5SXUXSU.js → chunk-H7ZML5QL.js} +2 -2
  10. package/dist/{chunk-CEXV3MOC.js → chunk-HGPZ2QWV.js} +3 -3
  11. package/dist/{chunk-LFFWD5JZ.js → chunk-IHCPMYKU.js} +3 -3
  12. package/dist/{chunk-RGDIVW3O.js → chunk-JZWJFG4M.js} +2 -2
  13. package/dist/{chunk-AI3Y3AZH.js → chunk-MO5G5TP5.js} +404 -96
  14. package/dist/chunk-MO5G5TP5.js.map +1 -0
  15. package/dist/{chunk-UAJUZLDE.js → chunk-PVWG3V62.js} +3 -3
  16. package/dist/{chunk-3CZU7XCS.js → chunk-PWZ4ULCB.js} +20 -14
  17. package/dist/chunk-PWZ4ULCB.js.map +1 -0
  18. package/dist/{chunk-DT2CEH2V.js → chunk-VHVOTGBQ.js} +3 -3
  19. package/dist/{chunk-IGHDIWZU.js → chunk-VYTIJ6HJ.js} +17 -2
  20. package/dist/{chunk-IGHDIWZU.js.map → chunk-VYTIJ6HJ.js.map} +1 -1
  21. package/dist/{credential-X6QKX473.js → credential-WOATCWOU.js} +4 -4
  22. package/dist/{credential-template-GD3D4MBI.js → credential-template-Q3DQE2AH.js} +4 -4
  23. package/dist/{debug-workflow-AAEOAME2.js → debug-workflow-ZNP5XZQF.js} +4 -4
  24. package/dist/{dist-QLTVBUMD.js → dist-WW2S733L.js} +2 -2
  25. package/dist/{docs-BVUEXBY3.js → docs-XVU4LA7G.js} +4 -4
  26. package/dist/{doctor-RHMKD4W5.js → doctor-PGNHD3TI.js} +3 -3
  27. package/dist/{environment-WE7VVE6I.js → environment-MRDNWU47.js} +4 -4
  28. package/dist/{feature-flag-VJO2TJFT.js → feature-flag-D3DQZ6DX.js} +4 -4
  29. package/dist/index.js +51 -48
  30. package/dist/index.js.map +1 -1
  31. package/dist/{init-RQHHKJJD.js → init-DHWKUYIR.js} +4 -4
  32. package/dist/{install-CQLHNZ6U.js → install-DMEFGQHD.js} +4 -4
  33. package/dist/{issues-HK2JA5JS.js → issues-E4YQ4DVC.js} +4 -4
  34. package/dist/{knobs-ETJAV6YM.js → knobs-NA6SPLHX.js} +4 -4
  35. package/dist/{list-F6DGGW3Z.js → list-GJNGFQMC.js} +4 -4
  36. package/dist/{local-CMAY54I5.js → local-NSKOZHHA.js} +6 -6
  37. package/dist/{login-6CKG664C.js → login-AMSRVPXG.js} +4 -4
  38. package/dist/{mcp-GC2WOOZO.js → mcp-V4GKEL2J.js} +5 -5
  39. package/dist/{metrics-IXICURAA.js → metrics-DM4YQQZN.js} +4 -4
  40. package/dist/{product-BUJWPYKB.js → product-WGOLTBRI.js} +4 -4
  41. package/dist/{property-4EXPWRNT.js → property-B7QMGLVW.js} +4 -4
  42. package/dist/{psql-XGLBI6T7.js → psql-4JHPJ2CO.js} +3 -3
  43. package/dist/{record-HL4QA7OX.js → record-LDNKS42Y.js} +5 -5
  44. package/dist/{redis-VJGCUJPV.js → redis-N44ZZ6VB.js} +3 -3
  45. package/dist/{release-2SMK5Y3V.js → release-R7GKLEAI.js} +4 -4
  46. package/dist/{sandbox-ORAPKOH3.js → sandbox-PLYUNOFX.js} +5 -5
  47. package/dist/{scenarios-DM5FDCAF.js → scenarios-WUUUKP2S.js} +4 -4
  48. package/dist/{session-X6IYGWYG.js → session-Y6RO7GFC.js} +6 -6
  49. package/dist/{skill-ZU5V2EE5.js → skill-3SP7MAA4.js} +153 -2
  50. package/dist/skill-3SP7MAA4.js.map +1 -0
  51. package/dist/{spec-KO5QD6AY.js → spec-ESPA3H26.js} +3 -3
  52. package/dist/{src-DSD34JIX.js → src-ECFPDE5X.js} +17 -1
  53. package/dist/src-ECFPDE5X.js.map +1 -0
  54. package/dist/{src-HTGNKBUF.js → src-OWPS5DCK.js} +60 -22
  55. package/dist/{src-HTGNKBUF.js.map → src-OWPS5DCK.js.map} +1 -1
  56. package/dist/{start-2ECN4LX5.js → start-KDDRLUFN.js} +4 -4
  57. package/dist/{tag-R5I72HJ6.js → tag-DAGHQZAR.js} +5 -5
  58. package/dist/{task-V3JMSJIM.js → task-PRR75HI4.js} +4 -4
  59. package/dist/{test-suite-NOYVK6YX.js → test-suite-O3UY7EER.js} +6 -6
  60. package/dist/{trace-NOYEKNO4.js → trace-BCSP7PCX.js} +4 -4
  61. package/dist/{triage-4RFATHBE.js → triage-2BKUBMMX.js} +5 -5
  62. package/dist/{utilities-3WMXE743.js → utilities-O3OO64T5.js} +4 -4
  63. package/dist/{workflow-DC7WOTTW.js → workflow-76EKT3DS.js} +10 -11
  64. package/dist/workflow-76EKT3DS.js.map +1 -0
  65. package/dist/{workflow-cache-SDSI6SMF.js → workflow-cache-UDTTNIHT.js} +7 -7
  66. package/dist/{workflow-debug-6UQYP67Q.js → workflow-debug-WO7D32EI.js} +7 -7
  67. package/dist/{workflow-proposals-67AVGMHX.js → workflow-proposals-PGURJ6ZI.js} +4 -4
  68. package/package.json +3 -3
  69. package/dist/chunk-3CZU7XCS.js.map +0 -1
  70. package/dist/chunk-4NRDONSC.js.map +0 -1
  71. package/dist/chunk-AI3Y3AZH.js.map +0 -1
  72. package/dist/skill-ZU5V2EE5.js.map +0 -1
  73. package/dist/src-DSD34JIX.js.map +0 -1
  74. package/dist/workflow-DC7WOTTW.js.map +0 -1
  75. /package/dist/{account-ZMV7NEFM.js.map → account-ZSM2Q3RB.js.map} +0 -0
  76. /package/dist/{api-catalog-6HNOGVPA.js.map → api-catalog-MMPFWS52.js.map} +0 -0
  77. /package/dist/{attachment-QAHR54HQ.js.map → attachment-NSYL5X3S.js.map} +0 -0
  78. /package/dist/{augmentor-7T3L6ZR4.js.map → augmentor-LSJLMJYP.js.map} +0 -0
  79. /package/dist/{auth-DURHUZDW.js.map → auth-OYF5A4BY.js.map} +0 -0
  80. /package/dist/{chunk-D6AVOPOS.js.map → chunk-GFP4UEUT.js.map} +0 -0
  81. /package/dist/{chunk-G5SXUXSU.js.map → chunk-H7ZML5QL.js.map} +0 -0
  82. /package/dist/{chunk-CEXV3MOC.js.map → chunk-HGPZ2QWV.js.map} +0 -0
  83. /package/dist/{chunk-LFFWD5JZ.js.map → chunk-IHCPMYKU.js.map} +0 -0
  84. /package/dist/{chunk-RGDIVW3O.js.map → chunk-JZWJFG4M.js.map} +0 -0
  85. /package/dist/{chunk-UAJUZLDE.js.map → chunk-PVWG3V62.js.map} +0 -0
  86. /package/dist/{chunk-DT2CEH2V.js.map → chunk-VHVOTGBQ.js.map} +0 -0
  87. /package/dist/{credential-X6QKX473.js.map → credential-WOATCWOU.js.map} +0 -0
  88. /package/dist/{credential-template-GD3D4MBI.js.map → credential-template-Q3DQE2AH.js.map} +0 -0
  89. /package/dist/{debug-workflow-AAEOAME2.js.map → debug-workflow-ZNP5XZQF.js.map} +0 -0
  90. /package/dist/{dist-QLTVBUMD.js.map → dist-WW2S733L.js.map} +0 -0
  91. /package/dist/{docs-BVUEXBY3.js.map → docs-XVU4LA7G.js.map} +0 -0
  92. /package/dist/{doctor-RHMKD4W5.js.map → doctor-PGNHD3TI.js.map} +0 -0
  93. /package/dist/{environment-WE7VVE6I.js.map → environment-MRDNWU47.js.map} +0 -0
  94. /package/dist/{feature-flag-VJO2TJFT.js.map → feature-flag-D3DQZ6DX.js.map} +0 -0
  95. /package/dist/{init-RQHHKJJD.js.map → init-DHWKUYIR.js.map} +0 -0
  96. /package/dist/{install-CQLHNZ6U.js.map → install-DMEFGQHD.js.map} +0 -0
  97. /package/dist/{issues-HK2JA5JS.js.map → issues-E4YQ4DVC.js.map} +0 -0
  98. /package/dist/{knobs-ETJAV6YM.js.map → knobs-NA6SPLHX.js.map} +0 -0
  99. /package/dist/{list-F6DGGW3Z.js.map → list-GJNGFQMC.js.map} +0 -0
  100. /package/dist/{local-CMAY54I5.js.map → local-NSKOZHHA.js.map} +0 -0
  101. /package/dist/{login-6CKG664C.js.map → login-AMSRVPXG.js.map} +0 -0
  102. /package/dist/{mcp-GC2WOOZO.js.map → mcp-V4GKEL2J.js.map} +0 -0
  103. /package/dist/{metrics-IXICURAA.js.map → metrics-DM4YQQZN.js.map} +0 -0
  104. /package/dist/{product-BUJWPYKB.js.map → product-WGOLTBRI.js.map} +0 -0
  105. /package/dist/{property-4EXPWRNT.js.map → property-B7QMGLVW.js.map} +0 -0
  106. /package/dist/{psql-XGLBI6T7.js.map → psql-4JHPJ2CO.js.map} +0 -0
  107. /package/dist/{record-HL4QA7OX.js.map → record-LDNKS42Y.js.map} +0 -0
  108. /package/dist/{redis-VJGCUJPV.js.map → redis-N44ZZ6VB.js.map} +0 -0
  109. /package/dist/{release-2SMK5Y3V.js.map → release-R7GKLEAI.js.map} +0 -0
  110. /package/dist/{sandbox-ORAPKOH3.js.map → sandbox-PLYUNOFX.js.map} +0 -0
  111. /package/dist/{scenarios-DM5FDCAF.js.map → scenarios-WUUUKP2S.js.map} +0 -0
  112. /package/dist/{session-X6IYGWYG.js.map → session-Y6RO7GFC.js.map} +0 -0
  113. /package/dist/{spec-KO5QD6AY.js.map → spec-ESPA3H26.js.map} +0 -0
  114. /package/dist/{start-2ECN4LX5.js.map → start-KDDRLUFN.js.map} +0 -0
  115. /package/dist/{tag-R5I72HJ6.js.map → tag-DAGHQZAR.js.map} +0 -0
  116. /package/dist/{task-V3JMSJIM.js.map → task-PRR75HI4.js.map} +0 -0
  117. /package/dist/{test-suite-NOYVK6YX.js.map → test-suite-O3UY7EER.js.map} +0 -0
  118. /package/dist/{trace-NOYEKNO4.js.map → trace-BCSP7PCX.js.map} +0 -0
  119. /package/dist/{triage-4RFATHBE.js.map → triage-2BKUBMMX.js.map} +0 -0
  120. /package/dist/{utilities-3WMXE743.js.map → utilities-O3OO64T5.js.map} +0 -0
  121. /package/dist/{workflow-cache-SDSI6SMF.js.map → workflow-cache-UDTTNIHT.js.map} +0 -0
  122. /package/dist/{workflow-debug-6UQYP67Q.js.map → workflow-debug-WO7D32EI.js.map} +0 -0
  123. /package/dist/{workflow-proposals-67AVGMHX.js.map → workflow-proposals-PGURJ6ZI.js.map} +0 -0
@@ -50122,6 +50122,52 @@ var ToolAbortedError = class extends Error {
50122
50122
  this.name = "ToolAbortedError";
50123
50123
  }
50124
50124
  };
50125
+ function labelForContext(context) {
50126
+ return context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
50127
+ }
50128
+ function elementDescription(context) {
50129
+ const role = context.role ? ` role=${context.role}` : "";
50130
+ const type = context.inputType ? ` type=${context.inputType}` : "";
50131
+ return `${context.tagName}${type}${role}`;
50132
+ }
50133
+ function formatFillTargetNotEditableMessage(context) {
50134
+ if (context.state === "not-fillable") {
50135
+ return `Cannot fill target ${labelForContext(context)}: ${elementDescription(context)} cannot accept text.`;
50136
+ }
50137
+ return `Cannot fill ${context.state} field ${labelForContext(context)}: ${elementDescription(context)} is not editable.`;
50138
+ }
50139
+ var FillTargetNotEditableError = class extends Error {
50140
+ fillTarget;
50141
+ constructor(context) {
50142
+ super(formatFillTargetNotEditableMessage(context));
50143
+ this.name = "FillTargetNotEditableError";
50144
+ this.fillTarget = context;
50145
+ }
50146
+ };
50147
+ function isRecord(value) {
50148
+ return typeof value === "object" && value !== null;
50149
+ }
50150
+ function isFillTargetNotEditableError(error) {
50151
+ if (error instanceof FillTargetNotEditableError) return true;
50152
+ if (!isRecord(error) || error.name !== "FillTargetNotEditableError") return false;
50153
+ if (!isRecord(error.fillTarget)) return false;
50154
+ const context = error.fillTarget;
50155
+ return typeof context.ref === "string" && typeof context.tagName === "string" && (context.state === "readonly" || context.state === "disabled" || context.state === "not-fillable");
50156
+ }
50157
+ function formatFillTargetNotEditableRecovery(error, toolName = "browser_fill_form") {
50158
+ const context = error.fillTarget;
50159
+ const action = toolName === "browser_type" ? "type into" : "fill";
50160
+ if (context.state === "not-fillable") {
50161
+ return [
50162
+ `Error: ${toolName} cannot ${action} target ${labelForContext(context)}: ${elementDescription(context)} cannot accept typed text.`,
50163
+ `Do not retry ${toolName} for this ref. Call browser_snapshot and choose the actual editable input or textarea. If the target is a dropdown, use browser_select_option or click the dropdown and select an option.`
50164
+ ].join(" ");
50165
+ }
50166
+ return [
50167
+ `Error: ${toolName} cannot ${action} ${context.state} field ${labelForContext(context)}: ${elementDescription(context)} is not editable.`,
50168
+ `Do not retry ${toolName} for this ref. Call browser_snapshot and choose an editable input or textarea for the same requirement, or complete the prerequisite that unlocks this field first.`
50169
+ ].join(" ");
50170
+ }
50125
50171
  function isMCPContentWithImages(result) {
50126
50172
  return typeof result === "object" && result !== null && "text" in result && "images" in result && Array.isArray(result.images) && result.images.length > 0;
50127
50173
  }
@@ -50212,6 +50258,31 @@ function formatEmptyPressKeyRecovery() {
50212
50258
  'Do not retry with an empty string. Use a Playwright key such as "Enter", "Escape", "Tab", "ArrowDown", or use browser_type when you need to enter text.'
50213
50259
  ].join(" ");
50214
50260
  }
50261
+ function normalizeRef(ref2) {
50262
+ const trimmed = ref2?.trim();
50263
+ return trimmed ? trimmed : void 0;
50264
+ }
50265
+ function formatEmptyRefRecovery(toolName) {
50266
+ return [
50267
+ `Error: ${toolName} requires a non-empty element ref from the latest browser_snapshot.`,
50268
+ 'Do not retry with an empty string. Call browser_snapshot and use a concrete ref such as "e42", or use the coordinate path when that tool supports coordinates.'
50269
+ ].join(" ");
50270
+ }
50271
+ function extractWaitingAriaRef(error) {
50272
+ const message = error instanceof Error ? error.message : String(error);
50273
+ return message.match(/waiting for locator\('aria-ref=([^']+)'\)/)?.[1];
50274
+ }
50275
+ function isStaleAriaRefActionTimeout(error, action) {
50276
+ const message = error instanceof Error ? error.message : String(error);
50277
+ return message.includes(`${action}: Timeout`) && message.includes("waiting for locator('aria-ref=") && !message.includes("locator resolved to <");
50278
+ }
50279
+ function formatStaleRefActionRecovery(toolName, ref2, element) {
50280
+ const label = element ? ` for "${element}"` : "";
50281
+ return [
50282
+ `Error: ${toolName} could not find ref ${ref2}${label} in the current page snapshot.`,
50283
+ "Do not retry the same ref. Call browser_snapshot and choose a fresh ref, or use the coordinate path when that tool supports coordinates."
50284
+ ].join(" ");
50285
+ }
50215
50286
  function isSelectOptionNonSelectError(error) {
50216
50287
  const message = error instanceof Error ? error.message : String(error);
50217
50288
  return message.includes("selectOption:") && message.includes("Element is not a <select>");
@@ -50219,6 +50290,35 @@ function isSelectOptionNonSelectError(error) {
50219
50290
  function isMissingDownloadTempFileError(message) {
50220
50291
  return /\bENOENT:\s+no such file or directory,\s+open\b/.test(message) && message.includes("/playwright-downloads-");
50221
50292
  }
50293
+ function isNavigateDownloadStartingError(error) {
50294
+ const message = error instanceof Error ? error.message : String(error);
50295
+ return /\bgoto:\s+Download is starting\b/.test(message);
50296
+ }
50297
+ function formatNavigateDownloadStartingRecovery() {
50298
+ return [
50299
+ "Error: browser_navigate started a file download instead of loading a page.",
50300
+ "Do not retry browser_navigate for the same URL. Call browser_list_downloads to find the captured file, then use browser_read_download or browser_pdf_read to inspect it."
50301
+ ].join(" ");
50302
+ }
50303
+ function isRelativeNavigateTarget(url) {
50304
+ const trimmed = url.trim();
50305
+ return trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("?") || trimmed.startsWith("#");
50306
+ }
50307
+ function isHttpUrl(url) {
50308
+ try {
50309
+ const parsed = new URL(url);
50310
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
50311
+ } catch {
50312
+ return false;
50313
+ }
50314
+ }
50315
+ function formatRelativeNavigateRecovery(url, currentUrl) {
50316
+ const baseDescription = currentUrl ? `current page URL is "${currentUrl}"` : "current page URL is unavailable";
50317
+ return [
50318
+ `Error: browser_navigate received relative URL "${url}", but the ${baseDescription}.`,
50319
+ "Use an absolute http(s) URL, or call browser_snapshot first if the current page needs to be inspected before navigating."
50320
+ ].join(" ");
50321
+ }
50222
50322
  function formatSelectOptionNonSelectRecovery(ref2, value, element) {
50223
50323
  return [
50224
50324
  `Error: browser_select_option can only operate on native <select> elements; ref ${ref2} for "${element}" resolved to a custom dropdown or combobox.`,
@@ -50676,11 +50776,38 @@ var BrowserToolExecutor = class {
50676
50776
  }
50677
50777
  // ==================== Navigation ====================
50678
50778
  async navigate(url) {
50679
- ensureAllowedHost(url, this.allowedHosts);
50680
- const rawResult = await this.client.navigate(url, { signal: this.currentSignal });
50779
+ const navigationUrl = await this.resolveNavigateUrl(url);
50780
+ if (navigationUrl.kind === "recovery") {
50781
+ return navigationUrl.message;
50782
+ }
50783
+ ensureAllowedHost(navigationUrl.url, this.allowedHosts);
50784
+ let rawResult;
50785
+ try {
50786
+ rawResult = await this.client.navigate(navigationUrl.url, { signal: this.currentSignal });
50787
+ } catch (error) {
50788
+ if (isNavigateDownloadStartingError(error)) {
50789
+ return formatNavigateDownloadStartingRecovery();
50790
+ }
50791
+ throw error;
50792
+ }
50681
50793
  const result = withRecoveryNotice(this.client, rawResult, this.logger);
50682
50794
  return this.formatNavigateResult(result);
50683
50795
  }
50796
+ async resolveNavigateUrl(url) {
50797
+ const trimmedUrl = url.trim();
50798
+ if (!isRelativeNavigateTarget(trimmedUrl)) {
50799
+ return { kind: "url", url: trimmedUrl };
50800
+ }
50801
+ const currentUrl = await this.client.getCurrentUrl({ signal: this.currentSignal }).catch(() => void 0);
50802
+ if (!currentUrl || !isHttpUrl(currentUrl)) {
50803
+ return { kind: "recovery", message: formatRelativeNavigateRecovery(trimmedUrl, currentUrl) };
50804
+ }
50805
+ try {
50806
+ return { kind: "url", url: new URL(trimmedUrl, currentUrl).toString() };
50807
+ } catch {
50808
+ return { kind: "recovery", message: formatRelativeNavigateRecovery(trimmedUrl, currentUrl) };
50809
+ }
50810
+ }
50684
50811
  async navigateBack() {
50685
50812
  const result = await this.client.navigateBack({ signal: this.currentSignal });
50686
50813
  return this.formatNavigateResult(result);
@@ -50985,6 +51112,10 @@ ${body}
50985
51112
  * Returns a structured result with the message and optional created hint metadata.
50986
51113
  */
50987
51114
  async hint(opts) {
51115
+ const ref2 = normalizeRef(opts.ref);
51116
+ if (!ref2) {
51117
+ return { message: formatEmptyRefRecovery("browser_hint") };
51118
+ }
50988
51119
  if (!this.hintProvider) {
50989
51120
  return { message: "Element hints are not available in this environment." };
50990
51121
  }
@@ -50998,9 +51129,9 @@ ${body}
50998
51129
  return { message: "Cannot create hint: no recent snapshot available. Take a snapshot first." };
50999
51130
  }
51000
51131
  const elements = parseSnapshot(lastYaml);
51001
- const fpResult = fingerprintForHint(elements, opts.ref);
51132
+ const fpResult = fingerprintForHint(elements, ref2);
51002
51133
  if (!fpResult) {
51003
- return { message: `Cannot create hint: element ref "${opts.ref}" not found in the current snapshot.` };
51134
+ return { message: `Cannot create hint: element ref "${ref2}" not found in the current snapshot.` };
51004
51135
  }
51005
51136
  if (opts.invalidate) {
51006
51137
  if (!opts.reason) {
@@ -51019,7 +51150,7 @@ ${body}
51019
51150
  fingerprintHash: fpResult.hash,
51020
51151
  reason: opts.reason
51021
51152
  });
51022
- return { message: `Hint on [${opts.ref}] invalidated: ${opts.reason}` };
51153
+ return { message: `Hint on [${ref2}] invalidated: ${opts.reason}` };
51023
51154
  }
51024
51155
  if (!opts.frontmatter) {
51025
51156
  return { message: "Cannot create hint: frontmatter is required." };
@@ -51064,7 +51195,7 @@ ${body}
51064
51195
  });
51065
51196
  const anchorNote = fpResult.anchoredToParent ? ` (anchored to ${fpResult.anchor.role} "${fpResult.anchor.text ?? ""}")` : "";
51066
51197
  return {
51067
- message: `Hint created on [${opts.ref}]${anchorNote}: (${category}: ${opts.frontmatter})`,
51198
+ message: `Hint created on [${ref2}]${anchorNote}: (${category}: ${opts.frontmatter})`,
51068
51199
  created: {
51069
51200
  id: created.id,
51070
51201
  frontmatter: opts.frontmatter,
@@ -51075,10 +51206,11 @@ ${body}
51075
51206
  };
51076
51207
  }
51077
51208
  async screenshot(opts) {
51209
+ const ref2 = normalizeRef(opts?.ref);
51078
51210
  const img = await this.client.takeScreenshot({
51079
51211
  fullPage: opts?.fullPage,
51080
51212
  element: opts?.element,
51081
- ref: opts?.ref,
51213
+ ref: ref2,
51082
51214
  type: opts?.type ?? "jpeg",
51083
51215
  label: opts?.label,
51084
51216
  signal: this.currentSignal
@@ -51099,9 +51231,10 @@ ${body}
51099
51231
  return `Screenshot captured${opts?.label ? ` (${opts.label})` : ""}: ${img ? "success" : "no screenshot"}`;
51100
51232
  }
51101
51233
  async evaluate(opts) {
51234
+ const ref2 = normalizeRef(opts.ref);
51102
51235
  const result = await this.client.evaluate(opts.expression, {
51103
51236
  label: opts.label,
51104
- ref: opts.ref,
51237
+ ref: ref2,
51105
51238
  signal: this.currentSignal
51106
51239
  });
51107
51240
  return typeof result === "string" ? result : JSON.stringify(result, null, 2);
@@ -51119,7 +51252,7 @@ ${body}
51119
51252
  }
51120
51253
  // ==================== Interaction ====================
51121
51254
  async click(opts) {
51122
- const { ref: ref2 } = opts;
51255
+ const ref2 = normalizeRef(opts.ref);
51123
51256
  let x2 = opts.x;
51124
51257
  let y2 = opts.y;
51125
51258
  if (x2 === 0 && y2 === 0) {
@@ -51135,29 +51268,40 @@ ${body}
51135
51268
  const useCoordinates = !ref2 && x2 !== void 0 && y2 !== void 0;
51136
51269
  const clickVerb = opts.doubleClick ? "Double-clicked" : "Clicked";
51137
51270
  const actionDesc = useCoordinates ? `${clickVerb} "${opts.element}" at (${x2}, ${y2})` : `${clickVerb} "${opts.element}" via ref ${ref2}`;
51138
- this.lastReplayableAction = { description: actionDesc, thunk: () => this.click(opts) };
51271
+ this.lastReplayableAction = {
51272
+ description: actionDesc,
51273
+ thunk: () => this.click({ ...opts, ref: ref2 })
51274
+ };
51139
51275
  let clickedElement;
51140
51276
  let fallbackUsed = false;
51141
51277
  const preClickUrl = useCoordinates && this.augmentorProvider ? await this.client.getCurrentUrl({ signal: this.currentSignal }).catch(() => void 0) : void 0;
51142
- const { diffText } = await this.withSemanticDiff(actionDesc, async () => {
51143
- if (useCoordinates) {
51144
- const coordResult = await this.client.clickAtCoordinates(x2, y2, opts.element, {
51145
- doubleClick: opts.doubleClick,
51146
- modifiers: opts.modifiers,
51147
- signal: this.currentSignal
51148
- });
51149
- clickedElement = coordResult.clickedElement;
51150
- } else {
51151
- const refResult = await this.client.click(ref2, opts.element, {
51152
- doubleClick: opts.doubleClick,
51153
- button: opts.button,
51154
- modifiers: opts.modifiers,
51155
- signal: this.currentSignal
51156
- });
51157
- clickedElement = refResult.clickedElement;
51158
- fallbackUsed = refResult.fallbackUsed;
51278
+ let diffText;
51279
+ try {
51280
+ ({ diffText } = await this.withSemanticDiff(actionDesc, async () => {
51281
+ if (useCoordinates) {
51282
+ const coordResult = await this.client.clickAtCoordinates(x2, y2, opts.element, {
51283
+ doubleClick: opts.doubleClick,
51284
+ modifiers: opts.modifiers,
51285
+ signal: this.currentSignal
51286
+ });
51287
+ clickedElement = coordResult.clickedElement;
51288
+ } else {
51289
+ const refResult = await this.client.click(ref2, opts.element, {
51290
+ doubleClick: opts.doubleClick,
51291
+ button: opts.button,
51292
+ modifiers: opts.modifiers,
51293
+ signal: this.currentSignal
51294
+ });
51295
+ clickedElement = refResult.clickedElement;
51296
+ fallbackUsed = refResult.fallbackUsed;
51297
+ }
51298
+ }));
51299
+ } catch (error) {
51300
+ if (!useCoordinates && ref2 && isStaleAriaRefActionTimeout(error, "click")) {
51301
+ return formatStaleRefActionRecovery("browser_click", ref2, opts.element);
51159
51302
  }
51160
- });
51303
+ throw error;
51304
+ }
51161
51305
  if (useCoordinates) {
51162
51306
  await this.emitAugmentorSignal("coordinate_click", x2, y2, preClickUrl ?? "");
51163
51307
  } else {
@@ -51173,36 +51317,56 @@ ${body}
51173
51317
  return withRecoveryNoticeMCP(this.client, result, this.logger);
51174
51318
  }
51175
51319
  async type(opts) {
51320
+ const ref2 = normalizeRef(opts.ref);
51321
+ if (!ref2) {
51322
+ return formatEmptyRefRecovery("browser_type");
51323
+ }
51176
51324
  const submitNote = opts.submit ? " and submitted" : "";
51177
51325
  const actionDesc = `Typed "${opts.text}" into "${opts.element}"${submitNote}`;
51178
- this.lastReplayableAction = { description: actionDesc, thunk: () => this.type(opts) };
51179
- if (opts.submit) {
51180
- const { diffText } = await this.withSemanticDiff(actionDesc, async () => {
51181
- await this.client.type(opts.ref, opts.text, opts.element, opts.submit, {
51182
- delay: opts.delay,
51183
- signal: this.currentSignal
51326
+ this.lastReplayableAction = {
51327
+ description: actionDesc,
51328
+ thunk: () => this.type({ ...opts, ref: ref2 })
51329
+ };
51330
+ try {
51331
+ if (opts.submit) {
51332
+ const { diffText } = await this.withSemanticDiff(actionDesc, async () => {
51333
+ await this.client.type(ref2, opts.text, opts.element, opts.submit, {
51334
+ delay: opts.delay,
51335
+ signal: this.currentSignal
51336
+ });
51184
51337
  });
51338
+ return withRecoveryNoticeMCP(this.client, diffText, this.logger);
51339
+ }
51340
+ await this.client.type(ref2, opts.text, opts.element, opts.submit, {
51341
+ delay: opts.delay,
51342
+ signal: this.currentSignal
51185
51343
  });
51186
- return withRecoveryNoticeMCP(this.client, diffText, this.logger);
51344
+ return withRecoveryNotice(this.client, actionDesc, this.logger);
51345
+ } catch (error) {
51346
+ if (isFillTargetNotEditableError(error)) {
51347
+ return formatFillTargetNotEditableRecovery(error, "browser_type");
51348
+ }
51349
+ throw error;
51187
51350
  }
51188
- await this.client.type(opts.ref, opts.text, opts.element, opts.submit, {
51189
- delay: opts.delay,
51190
- signal: this.currentSignal
51191
- });
51192
- return withRecoveryNotice(this.client, actionDesc, this.logger);
51193
51351
  }
51194
51352
  async hover(opts) {
51353
+ const ref2 = normalizeRef(opts.ref);
51354
+ if (!ref2) {
51355
+ return formatEmptyRefRecovery("browser_hover");
51356
+ }
51195
51357
  const { diffText } = await this.withSemanticDiff(
51196
- `Hovered over "${opts.element}" via ref ${opts.ref}`,
51358
+ `Hovered over "${opts.element}" via ref ${ref2}`,
51197
51359
  async () => {
51198
- await this.client.hover(opts.ref, opts.element, { signal: this.currentSignal });
51360
+ await this.client.hover(ref2, opts.element, { signal: this.currentSignal });
51199
51361
  }
51200
51362
  );
51201
51363
  return diffText;
51202
51364
  }
51203
51365
  async drag(opts) {
51366
+ const startRef = normalizeRef(opts.startRef);
51367
+ const endRef = normalizeRef(opts.endRef);
51204
51368
  const useCoordinates = opts.startX !== void 0 && opts.startY !== void 0 && opts.endX !== void 0 && opts.endY !== void 0;
51205
- const useRefs = opts.startRef !== void 0 && opts.endRef !== void 0;
51369
+ const useRefs = startRef !== void 0 && endRef !== void 0;
51206
51370
  if (!useCoordinates && !useRefs) {
51207
51371
  return "Error: Provide either startRef + endRef, or all four coordinates (startX, startY, endX, endY)";
51208
51372
  }
@@ -51221,7 +51385,7 @@ ${body}
51221
51385
  { signal: this.currentSignal }
51222
51386
  );
51223
51387
  } else {
51224
- await this.client.drag(opts.startRef, opts.startElement, opts.endRef, opts.endElement, {
51388
+ await this.client.drag(startRef, opts.startElement, endRef, opts.endElement, {
51225
51389
  signal: this.currentSignal
51226
51390
  });
51227
51391
  }
@@ -51247,35 +51411,63 @@ ${body}
51247
51411
  return withRecoveryNotice(this.client, `Pressed ${key}`, this.logger);
51248
51412
  }
51249
51413
  async fillForm(fields) {
51250
- const { diffText } = await this.withSemanticDiff(
51251
- `Filled ${fields.length} form field(s)`,
51252
- async () => {
51253
- await this.client.fillForm(fields, { signal: this.currentSignal });
51414
+ const normalizedFields = fields.map((field) => ({
51415
+ ...field,
51416
+ ref: normalizeRef(field.ref) ?? ""
51417
+ }));
51418
+ if (normalizedFields.some((field) => field.ref.length === 0)) {
51419
+ return formatEmptyRefRecovery("browser_fill_form");
51420
+ }
51421
+ try {
51422
+ const { diffText } = await this.withSemanticDiff(
51423
+ `Filled ${fields.length} form field(s)`,
51424
+ async () => {
51425
+ await this.client.fillForm(normalizedFields, { signal: this.currentSignal });
51426
+ }
51427
+ );
51428
+ return diffText;
51429
+ } catch (error) {
51430
+ if (isFillTargetNotEditableError(error)) {
51431
+ return formatFillTargetNotEditableRecovery(error);
51432
+ }
51433
+ if (isStaleAriaRefActionTimeout(error, "fill")) {
51434
+ const staleRef = extractWaitingAriaRef(error);
51435
+ const field = normalizedFields.find((candidate) => candidate.ref === staleRef) ?? normalizedFields[0];
51436
+ return formatStaleRefActionRecovery(
51437
+ "browser_fill_form",
51438
+ staleRef ?? field?.ref ?? "unknown",
51439
+ field?.element
51440
+ );
51254
51441
  }
51255
- );
51256
- return diffText;
51442
+ throw error;
51443
+ }
51257
51444
  }
51258
51445
  async selectOption(opts) {
51446
+ const ref2 = normalizeRef(opts.ref);
51447
+ if (!ref2) {
51448
+ return formatEmptyRefRecovery("browser_select_option");
51449
+ }
51259
51450
  try {
51260
51451
  const { diffText } = await this.withSemanticDiff(
51261
51452
  `Selected "${opts.value}" in "${opts.element}"`,
51262
51453
  async () => {
51263
- await this.client.selectOption(opts.ref, opts.value, opts.element, { signal: this.currentSignal });
51454
+ await this.client.selectOption(ref2, opts.value, opts.element, { signal: this.currentSignal });
51264
51455
  }
51265
51456
  );
51266
51457
  return diffText;
51267
51458
  } catch (error) {
51268
51459
  if (isSelectOptionNonSelectError(error)) {
51269
- return formatSelectOptionNonSelectRecovery(opts.ref, opts.value, opts.element);
51460
+ return formatSelectOptionNonSelectRecovery(ref2, opts.value, opts.element);
51270
51461
  }
51271
51462
  throw error;
51272
51463
  }
51273
51464
  }
51274
51465
  async fileUpload(paths, ref2) {
51466
+ const normalizedRef = normalizeRef(ref2);
51275
51467
  const { diffText } = await this.withSemanticDiff(
51276
51468
  `Uploaded ${paths.length} file(s)`,
51277
51469
  async () => {
51278
- await this.client.fileUpload(paths, { ref: ref2, signal: this.currentSignal });
51470
+ await this.client.fileUpload(paths, { ref: normalizedRef, signal: this.currentSignal });
51279
51471
  }
51280
51472
  );
51281
51473
  return diffText;
@@ -51284,15 +51476,17 @@ ${body}
51284
51476
  async scroll(opts) {
51285
51477
  let result;
51286
51478
  let label;
51287
- if (opts.toRef) {
51479
+ const withinRef = normalizeRef(opts.withinRef);
51480
+ const toRef = normalizeRef(opts.toRef);
51481
+ if (toRef) {
51288
51482
  result = await this.client.scroll(
51289
51483
  opts.direction,
51290
51484
  opts.amount,
51291
- opts.withinRef,
51292
- opts.toRef,
51485
+ withinRef,
51486
+ toRef,
51293
51487
  { signal: this.currentSignal }
51294
51488
  );
51295
- label = `toRef=${opts.toRef}`;
51489
+ label = `toRef=${toRef}`;
51296
51490
  } else if (opts.x !== void 0 && opts.y !== void 0) {
51297
51491
  if (opts.x === 0 && opts.y === 0) {
51298
51492
  return "Error: Coordinate scroll at (0, 0) is invalid. The coordinates were likely defaulted instead of intentionally chosen. Use direction-based scroll, scroll to a ref, or provide real coordinates before retrying.";
@@ -51309,11 +51503,11 @@ ${body}
51309
51503
  result = await this.client.scroll(
51310
51504
  opts.direction,
51311
51505
  opts.amount,
51312
- opts.withinRef,
51506
+ withinRef,
51313
51507
  void 0,
51314
51508
  { signal: this.currentSignal }
51315
51509
  );
51316
- label = `direction=${opts.direction}${opts.amount ? `, amount=${opts.amount}` : ""}${opts.withinRef ? `, withinRef=${opts.withinRef}` : ""}`;
51510
+ label = `direction=${opts.direction}${opts.amount ? `, amount=${opts.amount}` : ""}${withinRef ? `, withinRef=${withinRef}` : ""}`;
51317
51511
  }
51318
51512
  const scrollInfo = typeof result === "object" && result !== null ? result.scrollInfo : void 0;
51319
51513
  if (scrollInfo && !scrollInfo.moved) {
@@ -51467,6 +51661,9 @@ ${header}
51467
51661
  if (message === "No downloads captured during this session.") {
51468
51662
  return `${message} Use browser_list_downloads after triggering a download, or try browser_pdf_read if the file opened inline instead of downloading.`;
51469
51663
  }
51664
+ if (downloadId === 0 && message === "Download #0 not found. Use browser_list_downloads to see available downloads.") {
51665
+ return "Download #0 was not found. Download ids start at 1; call browser_list_downloads and use the listed id, or omit downloadId to read the most recent download.";
51666
+ }
51470
51667
  if (isMissingDownloadTempFileError(message)) {
51471
51668
  const label = downloadId !== null && downloadId !== void 0 ? `Download #${downloadId}` : "The most recent download";
51472
51669
  return `${label} was captured, but its temporary file is no longer available. Trigger the download again, then call browser_list_downloads and browser_read_download with the new id.`;
@@ -54653,6 +54850,42 @@ function resolveBrowserContextRequestUrl(url, pageUrl) {
54653
54850
  return url;
54654
54851
  }
54655
54852
  }
54853
+ function isRouteAlreadyHandledError(error) {
54854
+ const message = error instanceof Error ? error.message : String(error);
54855
+ return message.includes("Route is already handled");
54856
+ }
54857
+ function createSingleUseContextRouteHandle(route, logger2) {
54858
+ let handled = false;
54859
+ const runOnce = async (action, fn) => {
54860
+ if (handled) {
54861
+ logger2.debug("[ContextRoute] Ignoring duplicate route action", { action });
54862
+ return;
54863
+ }
54864
+ handled = true;
54865
+ try {
54866
+ await fn();
54867
+ } catch (error) {
54868
+ if (isRouteAlreadyHandledError(error)) {
54869
+ logger2.debug("[ContextRoute] Route was already handled by Playwright", {
54870
+ action,
54871
+ error: error instanceof Error ? error.message : String(error)
54872
+ });
54873
+ return;
54874
+ }
54875
+ throw error;
54876
+ }
54877
+ };
54878
+ return {
54879
+ fulfill: (opts) => runOnce("fulfill", () => route.fulfill(opts)),
54880
+ continue: (opts) => runOnce("continue", () => route.continue(opts)),
54881
+ request: () => ({
54882
+ url: () => route.request().url(),
54883
+ method: () => route.request().method(),
54884
+ postData: () => route.request().postData(),
54885
+ headers: () => route.request().headers()
54886
+ })
54887
+ };
54888
+ }
54656
54889
  var TOAST_SNAPSHOT_TTL = 5;
54657
54890
  function formatMcpResponse(input) {
54658
54891
  const lines = [];
@@ -54868,6 +55101,17 @@ function logLateRunCodeWaitRejection(logger2, kind, promise) {
54868
55101
  });
54869
55102
  });
54870
55103
  }
55104
+ function guardRunCodeFetch(logger2) {
55105
+ return (...args) => {
55106
+ const promise = globalThis.fetch(...args);
55107
+ promise.catch((err) => {
55108
+ logger2.debug("[DirectPlaywright] runCode fetch rejected after call", {
55109
+ error: err instanceof Error ? err.message : String(err)
55110
+ });
55111
+ });
55112
+ return promise;
55113
+ };
55114
+ }
54871
55115
  function guardFileChooserWaitForEvent(page, logger2) {
54872
55116
  const originalWaitForEvent = page.waitForEvent;
54873
55117
  let guarded = 0;
@@ -56469,9 +56713,10 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
56469
56713
  let threwError = null;
56470
56714
  let aborted = runController.signal.aborted;
56471
56715
  let guardedWaits = { fileChooser: 0, request: 0, response: 0 };
56716
+ const guardedFetch = guardRunCodeFetch(this.logger);
56472
56717
  try {
56473
56718
  if (!aborted) {
56474
- const fn = new Function("page", `return (async () => { ${code} })()`);
56719
+ const fn = new Function("page", "fetch", `return (async () => { ${code} })()`);
56475
56720
  const abortPromise = new Promise((_, reject) => {
56476
56721
  runController.signal.addEventListener(
56477
56722
  "abort",
@@ -56479,7 +56724,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
56479
56724
  { once: true }
56480
56725
  );
56481
56726
  });
56482
- const inner = fn(page);
56727
+ const inner = fn(page, guardedFetch);
56483
56728
  inner.catch((err) => {
56484
56729
  this.logger.debug("[DirectPlaywright] runCode orphan rejected after race", {
56485
56730
  error: err instanceof Error ? err.message : String(err)
@@ -56698,6 +56943,16 @@ ${warning}` : ""}`,
56698
56943
  }
56699
56944
  } catch (err) {
56700
56945
  const message = err instanceof Error ? err.message : String(err);
56946
+ if (isPlaywrightTargetClosedError(err)) {
56947
+ throw toBrowserActionTargetClosedError(err, {
56948
+ operation: "click",
56949
+ ref: ref2,
56950
+ elementDesc,
56951
+ lastKnownUrl: this.lastKnownUrl,
56952
+ pendingCrashType: this._pendingCrashType,
56953
+ pendingCrashUrl: this._pendingCrashUrl
56954
+ });
56955
+ }
56701
56956
  const isTimeout = message.includes("Timeout") || message.includes("timeout");
56702
56957
  if (isTimeout && preClickBox) {
56703
56958
  const centerX = Math.round(preClickBox.x + preClickBox.width / 2);
@@ -56849,6 +57104,7 @@ ${warning}` : ""}`,
56849
57104
  const dialogMsg = this.getPendingDialogMessage();
56850
57105
  if (dialogMsg) throw new Error(dialogMsg);
56851
57106
  const locator = await this.resolveRef(ref2);
57107
+ await assertFillTargetEditable(locator, ref2, elementDesc);
56852
57108
  const timeout = opts?.timeoutMs ?? TIMEOUTS.TYPE;
56853
57109
  const inputType = await locator.evaluate((node) => node instanceof HTMLInputElement ? node.type : null).catch((err) => {
56854
57110
  this.logger.debug("[DirectPlaywright] Failed to inspect input type before typing", {
@@ -59477,15 +59733,7 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59477
59733
  const context = await this.getContext();
59478
59734
  for (const route of routes) {
59479
59735
  await context.route(route.urlPattern, async (pwRoute) => {
59480
- await route.handler({
59481
- fulfill: (opts) => pwRoute.fulfill(opts),
59482
- continue: () => pwRoute.continue(),
59483
- request: () => ({
59484
- url: () => pwRoute.request().url(),
59485
- method: () => pwRoute.request().method(),
59486
- postData: () => pwRoute.request().postData()
59487
- })
59488
- });
59736
+ await route.handler(createSingleUseContextRouteHandle(pwRoute, this.logger));
59489
59737
  });
59490
59738
  }
59491
59739
  }
@@ -59497,33 +59745,93 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59497
59745
  return guessMimeType(filename);
59498
59746
  }
59499
59747
  };
59500
- async function assertFillTargetEditable(locator, ref2, elementDesc) {
59501
- const probe = await locator.evaluate((el) => {
59502
- const element = el;
59503
- const htmlInput = element instanceof HTMLInputElement ? element : null;
59504
- const htmlTextArea = element instanceof HTMLTextAreaElement ? element : null;
59505
- const htmlSelect = element instanceof HTMLSelectElement ? element : null;
59506
- const htmlButton = element instanceof HTMLButtonElement ? element : null;
59507
- const ariaReadonly = element.getAttribute("aria-readonly") === "true";
59508
- const ariaDisabled = element.getAttribute("aria-disabled") === "true";
59509
- const contentEditableFalse = element.getAttribute("contenteditable") === "false";
59510
- return {
59511
- readonly: Boolean(
59512
- htmlInput?.readOnly || htmlTextArea?.readOnly || ariaReadonly || contentEditableFalse
59513
- ),
59514
- disabled: Boolean(
59515
- htmlInput?.disabled || htmlTextArea?.disabled || htmlSelect?.disabled || htmlButton?.disabled || ariaDisabled
59516
- ),
59517
- tagName: element.tagName.toLowerCase(),
59518
- role: element.getAttribute("role")
59519
- };
59748
+ function isPlaywrightTargetClosedError(error) {
59749
+ const message = error instanceof Error ? error.message : String(error);
59750
+ return /Target page, context or browser has been closed/i.test(message);
59751
+ }
59752
+ function toBrowserActionTargetClosedError(error, context) {
59753
+ const label = context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
59754
+ const message = error instanceof Error ? error.message : String(error);
59755
+ const wrapped = new Error(`Browser click target closed for ${label}: ${message}`, {
59756
+ cause: error instanceof Error ? error : void 0
59520
59757
  });
59521
- if (!probe.readonly && !probe.disabled) return;
59522
- const label = elementDesc ? `${elementDesc} (${ref2})` : ref2;
59523
- const state = probe.disabled ? "disabled" : "readonly";
59758
+ wrapped.name = "BrowserActionTargetClosedError";
59759
+ Object.assign(wrapped, { browserActionTargetClosed: context });
59760
+ return wrapped;
59761
+ }
59762
+ function toFillTargetProbeError(error, context) {
59763
+ const label = context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
59764
+ const message = error instanceof Error ? error.message : String(error);
59765
+ const wrapped = new Error(`Fill target editability probe failed for ${label}: ${message}`, {
59766
+ cause: error instanceof Error ? error : void 0
59767
+ });
59768
+ wrapped.name = "FillTargetProbeError";
59769
+ Object.assign(wrapped, { fillTargetProbe: context });
59770
+ return wrapped;
59771
+ }
59772
+ async function assertFillTargetEditable(locator, ref2, elementDesc) {
59773
+ let probe;
59774
+ try {
59775
+ probe = await locator.evaluate((el) => {
59776
+ const element = el;
59777
+ const htmlInput = element instanceof HTMLInputElement ? element : null;
59778
+ const htmlTextArea = element instanceof HTMLTextAreaElement ? element : null;
59779
+ const htmlSelect = element instanceof HTMLSelectElement ? element : null;
59780
+ const htmlButton = element instanceof HTMLButtonElement ? element : null;
59781
+ const ariaReadonly = element.getAttribute("aria-readonly") === "true";
59782
+ const ariaDisabled = element.getAttribute("aria-disabled") === "true";
59783
+ const contentEditableFalse = element.getAttribute("contenteditable") === "false";
59784
+ const inputType = htmlInput?.type.toLowerCase() ?? null;
59785
+ const fillableInputTypes = /* @__PURE__ */ new Set([
59786
+ "",
59787
+ "color",
59788
+ "date",
59789
+ "datetime-local",
59790
+ "email",
59791
+ "month",
59792
+ "number",
59793
+ "password",
59794
+ "range",
59795
+ "search",
59796
+ "tel",
59797
+ "text",
59798
+ "time",
59799
+ "url",
59800
+ "week"
59801
+ ]);
59802
+ return {
59803
+ readonly: Boolean(
59804
+ htmlInput?.readOnly || htmlTextArea?.readOnly || ariaReadonly || contentEditableFalse
59805
+ ),
59806
+ disabled: Boolean(
59807
+ htmlInput?.disabled || htmlTextArea?.disabled || htmlSelect?.disabled || htmlButton?.disabled || ariaDisabled
59808
+ ),
59809
+ fillable: Boolean(
59810
+ htmlInput && fillableInputTypes.has(inputType ?? "") || htmlTextArea || element.isContentEditable
59811
+ ),
59812
+ tagName: element.tagName.toLowerCase(),
59813
+ role: element.getAttribute("role"),
59814
+ inputType
59815
+ };
59816
+ });
59817
+ } catch (error) {
59818
+ throw toFillTargetProbeError(error, {
59819
+ ref: ref2,
59820
+ elementDesc,
59821
+ stage: "editability_probe"
59822
+ });
59823
+ }
59824
+ if (!probe.readonly && !probe.disabled && probe.fillable) return;
59825
+ const state = probe.disabled ? "disabled" : probe.readonly ? "readonly" : "not-fillable";
59524
59826
  const tag = probe.tagName ?? "element";
59525
- const role = probe.role ? ` role=${probe.role}` : "";
59526
- throw new Error(`Cannot fill ${state} field ${label}: ${tag}${role} is not editable.`);
59827
+ throw new FillTargetNotEditableError({
59828
+ ref: ref2,
59829
+ elementDesc,
59830
+ state,
59831
+ tagName: tag,
59832
+ role: probe.role,
59833
+ ...probe.inputType ? { inputType: probe.inputType } : {}
59834
+ });
59527
59835
  }
59528
59836
 
59529
59837
  export {
@@ -59921,4 +60229,4 @@ playwright-extra/dist/index.esm.js:
59921
60229
  * @license MIT
59922
60230
  *)
59923
60231
  */
59924
- //# sourceMappingURL=chunk-AI3Y3AZH.js.map
60232
+ //# sourceMappingURL=chunk-MO5G5TP5.js.map