@descryy/mcp 0.6.0 → 0.7.1

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 (264) hide show
  1. package/dist/action-handshake.d.ts +8 -75
  2. package/dist/action-handshake.d.ts.map +1 -1
  3. package/dist/action-handshake.js +9 -79
  4. package/dist/action-handshake.js.map +1 -1
  5. package/dist/bin/descry-mcp.d.ts +4 -15
  6. package/dist/bin/descry-mcp.d.ts.map +1 -1
  7. package/dist/bin/descry-mcp.js +12 -42
  8. package/dist/bin/descry-mcp.js.map +1 -1
  9. package/dist/browser/driver.d.ts +121 -224
  10. package/dist/browser/driver.d.ts.map +1 -1
  11. package/dist/browser/driver.js +9 -55
  12. package/dist/browser/driver.js.map +1 -1
  13. package/dist/browser/evidence.d.ts +10 -43
  14. package/dist/browser/evidence.d.ts.map +1 -1
  15. package/dist/browser/evidence.js +20 -54
  16. package/dist/browser/evidence.js.map +1 -1
  17. package/dist/browser/fake-driver.d.ts +12 -19
  18. package/dist/browser/fake-driver.d.ts.map +1 -1
  19. package/dist/browser/fake-driver.js +31 -26
  20. package/dist/browser/fake-driver.js.map +1 -1
  21. package/dist/browser/graph-write.d.ts +4 -45
  22. package/dist/browser/graph-write.d.ts.map +1 -1
  23. package/dist/browser/graph-write.js +8 -53
  24. package/dist/browser/graph-write.js.map +1 -1
  25. package/dist/browser/playwright-driver.d.ts +7 -91
  26. package/dist/browser/playwright-driver.d.ts.map +1 -1
  27. package/dist/browser/playwright-driver.js +59 -197
  28. package/dist/browser/playwright-driver.js.map +1 -1
  29. package/dist/browser/provider.d.ts +5 -34
  30. package/dist/browser/provider.d.ts.map +1 -1
  31. package/dist/browser/provider.js +4 -24
  32. package/dist/browser/provider.js.map +1 -1
  33. package/dist/browser/registry.d.ts +26 -106
  34. package/dist/browser/registry.d.ts.map +1 -1
  35. package/dist/browser/registry.js +20 -77
  36. package/dist/browser/registry.js.map +1 -1
  37. package/dist/browser/scenario-provenance.d.ts +23 -0
  38. package/dist/browser/scenario-provenance.d.ts.map +1 -0
  39. package/dist/browser/scenario-provenance.js +59 -0
  40. package/dist/browser/scenario-provenance.js.map +1 -0
  41. package/dist/browser/scenario-resolve.d.ts +10 -44
  42. package/dist/browser/scenario-resolve.d.ts.map +1 -1
  43. package/dist/browser/scenario-resolve.js +10 -41
  44. package/dist/browser/scenario-resolve.js.map +1 -1
  45. package/dist/browser/scenario-runner.d.ts +12 -70
  46. package/dist/browser/scenario-runner.d.ts.map +1 -1
  47. package/dist/browser/scenario-runner.js +30 -90
  48. package/dist/browser/scenario-runner.js.map +1 -1
  49. package/dist/browser/stack-parser.d.ts +4 -28
  50. package/dist/browser/stack-parser.d.ts.map +1 -1
  51. package/dist/browser/stack-parser.js +12 -39
  52. package/dist/browser/stack-parser.js.map +1 -1
  53. package/dist/browser/tool-support.d.ts +20 -67
  54. package/dist/browser/tool-support.d.ts.map +1 -1
  55. package/dist/browser/tool-support.js +21 -65
  56. package/dist/browser/tool-support.js.map +1 -1
  57. package/dist/browser/url-scheme.d.ts +14 -0
  58. package/dist/browser/url-scheme.d.ts.map +1 -0
  59. package/dist/browser/url-scheme.js +38 -0
  60. package/dist/browser/url-scheme.js.map +1 -0
  61. package/dist/cancellation.d.ts +11 -46
  62. package/dist/cancellation.d.ts.map +1 -1
  63. package/dist/cancellation.js +11 -46
  64. package/dist/cancellation.js.map +1 -1
  65. package/dist/capped.d.ts +9 -33
  66. package/dist/capped.d.ts.map +1 -1
  67. package/dist/capped.js +7 -27
  68. package/dist/capped.js.map +1 -1
  69. package/dist/disclosure-ledger.d.ts +6 -30
  70. package/dist/disclosure-ledger.d.ts.map +1 -1
  71. package/dist/disclosure-ledger.js +4 -26
  72. package/dist/disclosure-ledger.js.map +1 -1
  73. package/dist/index.d.ts +10 -26
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +7 -17
  76. package/dist/index.js.map +1 -1
  77. package/dist/module-trust.d.ts +24 -0
  78. package/dist/module-trust.d.ts.map +1 -0
  79. package/dist/module-trust.js +60 -0
  80. package/dist/module-trust.js.map +1 -0
  81. package/dist/path-confinement.d.ts +31 -0
  82. package/dist/path-confinement.d.ts.map +1 -0
  83. package/dist/path-confinement.js +44 -0
  84. package/dist/path-confinement.js.map +1 -0
  85. package/dist/protocol.d.ts +10 -53
  86. package/dist/protocol.d.ts.map +1 -1
  87. package/dist/protocol.js +14 -60
  88. package/dist/protocol.js.map +1 -1
  89. package/dist/registry.d.ts +37 -58
  90. package/dist/registry.d.ts.map +1 -1
  91. package/dist/registry.js +88 -96
  92. package/dist/registry.js.map +1 -1
  93. package/dist/render.d.ts +89 -13
  94. package/dist/render.d.ts.map +1 -1
  95. package/dist/render.js +111 -16
  96. package/dist/render.js.map +1 -1
  97. package/dist/runtime-registry.d.ts +7 -66
  98. package/dist/runtime-registry.d.ts.map +1 -1
  99. package/dist/runtime-registry.js +39 -114
  100. package/dist/runtime-registry.js.map +1 -1
  101. package/dist/scenarios/index.d.ts +1 -1
  102. package/dist/scenarios/index.d.ts.map +1 -1
  103. package/dist/scenarios/index.js +1 -1
  104. package/dist/scenarios/index.js.map +1 -1
  105. package/dist/scenarios/parse.d.ts +4 -18
  106. package/dist/scenarios/parse.d.ts.map +1 -1
  107. package/dist/scenarios/parse.js +14 -34
  108. package/dist/scenarios/parse.js.map +1 -1
  109. package/dist/scenarios/scenario.d.ts +18 -74
  110. package/dist/scenarios/scenario.d.ts.map +1 -1
  111. package/dist/scenarios/scenario.js +7 -34
  112. package/dist/scenarios/scenario.js.map +1 -1
  113. package/dist/scenarios/storage.d.ts +11 -41
  114. package/dist/scenarios/storage.d.ts.map +1 -1
  115. package/dist/scenarios/storage.js +57 -47
  116. package/dist/scenarios/storage.js.map +1 -1
  117. package/dist/server.d.ts.map +1 -1
  118. package/dist/server.js +69 -19
  119. package/dist/server.js.map +1 -1
  120. package/dist/session.d.ts +90 -259
  121. package/dist/session.d.ts.map +1 -1
  122. package/dist/session.js +136 -250
  123. package/dist/session.js.map +1 -1
  124. package/dist/tools/analyze.d.ts +32 -118
  125. package/dist/tools/analyze.d.ts.map +1 -1
  126. package/dist/tools/analyze.js +65 -186
  127. package/dist/tools/analyze.js.map +1 -1
  128. package/dist/tools/browser-click.d.ts +3 -21
  129. package/dist/tools/browser-click.d.ts.map +1 -1
  130. package/dist/tools/browser-click.js +10 -31
  131. package/dist/tools/browser-click.js.map +1 -1
  132. package/dist/tools/browser-close-session.d.ts +4 -13
  133. package/dist/tools/browser-close-session.d.ts.map +1 -1
  134. package/dist/tools/browser-close-session.js +4 -13
  135. package/dist/tools/browser-close-session.js.map +1 -1
  136. package/dist/tools/browser-fill.d.ts +5 -36
  137. package/dist/tools/browser-fill.d.ts.map +1 -1
  138. package/dist/tools/browser-fill.js +9 -44
  139. package/dist/tools/browser-fill.js.map +1 -1
  140. package/dist/tools/browser-navigate.d.ts +6 -27
  141. package/dist/tools/browser-navigate.d.ts.map +1 -1
  142. package/dist/tools/browser-navigate.js +6 -23
  143. package/dist/tools/browser-navigate.js.map +1 -1
  144. package/dist/tools/browser-run-scenario.d.ts +5 -31
  145. package/dist/tools/browser-run-scenario.d.ts.map +1 -1
  146. package/dist/tools/browser-run-scenario.js +10 -46
  147. package/dist/tools/browser-run-scenario.js.map +1 -1
  148. package/dist/tools/browser-save-scenario.d.ts +3 -28
  149. package/dist/tools/browser-save-scenario.d.ts.map +1 -1
  150. package/dist/tools/browser-save-scenario.js +47 -84
  151. package/dist/tools/browser-save-scenario.js.map +1 -1
  152. package/dist/tools/browser-snapshot.d.ts +6 -39
  153. package/dist/tools/browser-snapshot.d.ts.map +1 -1
  154. package/dist/tools/browser-snapshot.js +4 -31
  155. package/dist/tools/browser-snapshot.js.map +1 -1
  156. package/dist/tools/browser-start-session.d.ts +4 -23
  157. package/dist/tools/browser-start-session.d.ts.map +1 -1
  158. package/dist/tools/browser-start-session.js +145 -56
  159. package/dist/tools/browser-start-session.js.map +1 -1
  160. package/dist/tools/browser-type.d.ts +5 -35
  161. package/dist/tools/browser-type.d.ts.map +1 -1
  162. package/dist/tools/browser-type.js +10 -45
  163. package/dist/tools/browser-type.js.map +1 -1
  164. package/dist/tools/contracts.d.ts +9 -86
  165. package/dist/tools/contracts.d.ts.map +1 -1
  166. package/dist/tools/contracts.js +24 -80
  167. package/dist/tools/contracts.js.map +1 -1
  168. package/dist/tools/cross-pr.d.ts +42 -30
  169. package/dist/tools/cross-pr.d.ts.map +1 -1
  170. package/dist/tools/cross-pr.js +81 -62
  171. package/dist/tools/cross-pr.js.map +1 -1
  172. package/dist/tools/git-diff.d.ts +18 -2
  173. package/dist/tools/git-diff.d.ts.map +1 -1
  174. package/dist/tools/git-diff.js +118 -26
  175. package/dist/tools/git-diff.js.map +1 -1
  176. package/dist/tools/git-history.d.ts +5 -16
  177. package/dist/tools/git-history.d.ts.map +1 -1
  178. package/dist/tools/git-history.js +3 -10
  179. package/dist/tools/git-history.js.map +1 -1
  180. package/dist/tools/history.d.ts +4 -33
  181. package/dist/tools/history.d.ts.map +1 -1
  182. package/dist/tools/history.js +6 -31
  183. package/dist/tools/history.js.map +1 -1
  184. package/dist/tools/impact.d.ts +7 -51
  185. package/dist/tools/impact.d.ts.map +1 -1
  186. package/dist/tools/impact.js +15 -67
  187. package/dist/tools/impact.js.map +1 -1
  188. package/dist/tools/index.d.ts +3 -8
  189. package/dist/tools/index.d.ts.map +1 -1
  190. package/dist/tools/index.js +3 -8
  191. package/dist/tools/index.js.map +1 -1
  192. package/dist/tools/kit.d.ts +66 -113
  193. package/dist/tools/kit.d.ts.map +1 -1
  194. package/dist/tools/kit.js +60 -28
  195. package/dist/tools/kit.js.map +1 -1
  196. package/dist/tools/link-workspace.d.ts +7 -45
  197. package/dist/tools/link-workspace.d.ts.map +1 -1
  198. package/dist/tools/link-workspace.js +10 -48
  199. package/dist/tools/link-workspace.js.map +1 -1
  200. package/dist/tools/lookup.d.ts +4 -14
  201. package/dist/tools/lookup.d.ts.map +1 -1
  202. package/dist/tools/lookup.js +4 -14
  203. package/dist/tools/lookup.js.map +1 -1
  204. package/dist/tools/mark-incident.d.ts +7 -54
  205. package/dist/tools/mark-incident.d.ts.map +1 -1
  206. package/dist/tools/mark-incident.js +15 -68
  207. package/dist/tools/mark-incident.js.map +1 -1
  208. package/dist/tools/observe-runtime.d.ts +27 -210
  209. package/dist/tools/observe-runtime.d.ts.map +1 -1
  210. package/dist/tools/observe-runtime.js +273 -434
  211. package/dist/tools/observe-runtime.js.map +1 -1
  212. package/dist/tools/observe-tests.d.ts +9 -100
  213. package/dist/tools/observe-tests.d.ts.map +1 -1
  214. package/dist/tools/observe-tests.js +74 -152
  215. package/dist/tools/observe-tests.js.map +1 -1
  216. package/dist/tools/pr-analysis.d.ts +18 -2
  217. package/dist/tools/pr-analysis.d.ts.map +1 -1
  218. package/dist/tools/pr-analysis.js +73 -23
  219. package/dist/tools/pr-analysis.js.map +1 -1
  220. package/dist/tools/pre-push.d.ts +74 -16
  221. package/dist/tools/pre-push.d.ts.map +1 -1
  222. package/dist/tools/pre-push.js +152 -51
  223. package/dist/tools/pre-push.js.map +1 -1
  224. package/dist/tools/propagation.d.ts +11 -53
  225. package/dist/tools/propagation.d.ts.map +1 -1
  226. package/dist/tools/propagation.js +17 -58
  227. package/dist/tools/propagation.js.map +1 -1
  228. package/dist/tools/questions.d.ts +13 -63
  229. package/dist/tools/questions.d.ts.map +1 -1
  230. package/dist/tools/questions.js +33 -105
  231. package/dist/tools/questions.js.map +1 -1
  232. package/dist/tools/refusal-fetch.d.ts +4 -40
  233. package/dist/tools/refusal-fetch.d.ts.map +1 -1
  234. package/dist/tools/refusal-fetch.js +4 -40
  235. package/dist/tools/refusal-fetch.js.map +1 -1
  236. package/dist/tools/runtime-incident.d.ts +4 -63
  237. package/dist/tools/runtime-incident.d.ts.map +1 -1
  238. package/dist/tools/runtime-incident.js +10 -87
  239. package/dist/tools/runtime-incident.js.map +1 -1
  240. package/dist/tools/scope.d.ts +7 -25
  241. package/dist/tools/scope.d.ts.map +1 -1
  242. package/dist/tools/scope.js +10 -29
  243. package/dist/tools/scope.js.map +1 -1
  244. package/dist/tools/similar-incidents.d.ts +11 -86
  245. package/dist/tools/similar-incidents.d.ts.map +1 -1
  246. package/dist/tools/similar-incidents.js +7 -71
  247. package/dist/tools/similar-incidents.js.map +1 -1
  248. package/dist/tools/validate.d.ts +29 -70
  249. package/dist/tools/validate.d.ts.map +1 -1
  250. package/dist/tools/validate.js +141 -85
  251. package/dist/tools/validate.js.map +1 -1
  252. package/dist/tools/verification-status.d.ts +9 -64
  253. package/dist/tools/verification-status.d.ts.map +1 -1
  254. package/dist/tools/verification-status.js +9 -62
  255. package/dist/tools/verification-status.js.map +1 -1
  256. package/dist/tools/verify-claim.d.ts +5 -52
  257. package/dist/tools/verify-claim.d.ts.map +1 -1
  258. package/dist/tools/verify-claim.js +6 -56
  259. package/dist/tools/verify-claim.js.map +1 -1
  260. package/dist/transport.d.ts +15 -52
  261. package/dist/transport.d.ts.map +1 -1
  262. package/dist/transport.js +16 -60
  263. package/dist/transport.js.map +1 -1
  264. package/package.json +40 -22
@@ -1,99 +1,26 @@
1
1
  /**
2
- * The real `BrowserDriver`, over `@descryy/runtime-browser`.
3
- *
4
- * ## Why the import is still dynamic
5
- *
6
- * `@descryy/runtime-browser` is now a declared dependency of `@descryy/mcp`
7
- * (`mcp-browser-tools.md` §3.1's publish landed) — so the reason this module
8
- * used to give for a call-time specifier import, "not on the registry yet,"
9
- * no longer holds. It stays dynamic anyway, for a reason that outgrew that
10
- * one: `loadRuntimeModule` (`PlaywrightDriverOptions`, below) is a real
11
- * injection seam that `browser-action-settle.test.ts` uses today to drive
12
- * this file's settle logic against a fake module shaped like this one,
13
- * deterministically, with no real browser involved. A static
14
- * `import … from "@descryy/runtime-browser"` at the top of this file would
15
- * remove that seam along with the indirection, and nothing here needs
16
- * removing it — the package being on the registry made the *dependency*
17
- * declarable, not the import path mandatory. Absence (package missing,
18
- * import throws, or Chromium missing) is still a **named refusal with a
19
- * remedy**, exactly as before — see `provider.ts`.
20
- *
21
- * ## What this adds over the runtime package, and what it deliberately does not
22
- *
23
- * It adds exactly one thing the runtime has no API for: **element refs.** The
24
- * port's contract is that an action names an element the agent has seen, and
25
- * `BrowserActionCollector` acts on raw selectors. So this module enumerates
26
- * the page's interactive elements, holds a Playwright `ElementHandle` per ref,
27
- * and drops the whole map on navigation. Handles rather than injected `data-`
28
- * attributes on purpose: stamping the DOM would make the act of observing
29
- * change the page being observed, and a detached element then fails naturally
30
- * instead of resolving to whatever took its place.
31
- *
32
- * Everything else is composition. Launch, click, type, fill, console capture,
33
- * network capture, the `fetch()` initiator stack and its source-map rewrite
34
- * are all the runtime package's, called rather than reimplemented.
35
- *
36
- * ## Rule 1
37
- *
38
- * No language is named here. The stack-trace parser that turns a raw browser
39
- * stack into resolved frames is handed in by the caller as a module specifier
40
- * — the same mechanism `runtime-registry.ts` uses for runtime adapters, for
41
- * the same reason. Without one, requests are still observed and no caller
42
- * edge is written; the reply says so.
2
+ * The real `BrowserDriver`, over `@descryy/runtime-browser`. Import stays
3
+ * dynamic — `loadRuntimeModule` is a test injection seam (fake module, no
4
+ * browser needed). Adds only element refs over the runtime; stack-trace parsing is caller-supplied (rule 1: no language named here).
43
5
  */
44
6
  import type { StackTrace, ServiceRootLookup } from "@descryy/runtime-contracts";
45
7
  import type { StackTraceParser } from "@descryy/runtime-backend-observation";
46
8
  import type { BrowserDriver, BrowserObservation, ObservedAction, UnsettledAction } from "./driver.ts";
47
9
  export interface PlaywrightDriverOptions {
48
- /**
49
- * Turns a raw browser stack into resolved frames. Supplied by the caller;
50
- * this package cannot construct one without naming a language.
51
- *
52
- * Absent, the initiator capture is not installed at all — the runtime's own
53
- * two-halves-or-neither rule — so no request carries a call site and no R4
54
- * caller edge can be written.
55
- */
10
+ /** Turns a raw stack into resolved frames; caller-supplied (rule 1: no language named here). Absent, initiator capture isn't installed and no R4 caller edge is written. */
56
11
  readonly stackParser?: StackTraceParser;
57
12
  /** Maps a page origin to the on-disk root that served it. Absent means script URLs stay unresolved. */
58
13
  readonly resolveSourceRoot?: SourceRootResolver;
59
14
  /** Which service the evidence is attributed to. */
60
15
  readonly service?: string;
61
- /**
62
- * How the runtime browser module is loaded. Defaults to importing
63
- * `@descryy/runtime-browser` by specifier.
64
- *
65
- * It exists because the settle logic below is timing-dependent and this
66
- * build has no browser backend to run it against — the conformance test
67
- * that would exercise it skips on every machine without
68
- * `@descryy/runtime-browser` and Chromium. A driver-level fake page is the
69
- * only way that logic is executable at all here — and a sleep that dropped
70
- * evidence shipped in this file with no executable test over it at all,
71
- * which is the argument for the seam rather than against it.
72
- *
73
- * Keep it. Ruled on when it was introduced: exporting `openSession` instead
74
- * would widen the public surface to an internal lifecycle, where this
75
- * narrows to one injection point with a stated purpose.
76
- */
16
+ /** Loads the runtime browser module; overridable because the settle logic below is timing-dependent and this build has no browser backend to test it against otherwise. Keep narrow — do not export `openSession` instead. */
77
17
  readonly loadRuntimeModule?: () => Promise<unknown>;
78
18
  }
79
19
  type SourceRootResolver = (origin: string) => ServiceRootLookup;
80
20
  export declare class BrowserBackendMissingError extends Error {
81
21
  constructor(cause: unknown);
82
22
  }
83
- /**
84
- * An `Evidence`-shaped record, as the collectors emit it.
85
- *
86
- * Typed structurally rather than imported wholesale because only four fields
87
- * are read: everything else the collectors attach travels to nobody here.
88
- */
89
- /**
90
- * Exported for `browser-request-outcome-mapping.test.ts`, which drives
91
- * `collect()` directly with synthetic evidence records — the real
92
- * evidence-to-`ObservedRequest` mapping, not the fake driver's fixtures. That
93
- * test is the regression test for B3: the fake driver lets a fixture declare
94
- * `outcome` directly, which let this mapping function ship never populating
95
- * the field at all while every test still passed.
96
- */
23
+ /** `Evidence`-shaped record (typed structurally; only 4 fields are read). Exported for `browser-request-outcome-mapping.test.ts` — regression test for B3, where `outcome` shipped never populated despite passing tests. */
97
24
  export interface EmittedEvidence {
98
25
  readonly eventType: string;
99
26
  readonly payload: Record<string, unknown>;
@@ -102,18 +29,7 @@ export interface EmittedEvidence {
102
29
  readonly timestamp: string;
103
30
  }
104
31
  export declare function createPlaywrightBrowserDriver(options?: PlaywrightDriverOptions): Promise<BrowserDriver>;
105
- /**
106
- * Turn what the collectors emitted into the port's vocabulary.
107
- *
108
- * The request/response join is on `requestId`, because a `NETWORK_REQUEST`
109
- * carries the method, the URL and the call-site stack while only the matching
110
- * `NETWORK_RESPONSE` knows the status. A request with no response is kept with
111
- * `status: null` — "never answered" is a different fact from a 500, and the
112
- * port says so.
113
- *
114
- * Exported for `browser-request-outcome-mapping.test.ts` — see
115
- * `EmittedEvidence`'s doc comment for why.
116
- */
32
+ /** Turns collector evidence into the port's vocabulary. Joins request/response on `requestId`, keeping `status: null` (not guessed) when unanswered. Exported for `browser-request-outcome-mapping.test.ts`. */
117
33
  export declare function collect(emitted: readonly EmittedEvidence[], actions: readonly ObservedAction[], unsettled: readonly UnsettledAction[]): BrowserObservation;
118
34
  export {};
119
35
  //# sourceMappingURL=playwright-driver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-driver.d.ts","sourceRoot":"","sources":["../../src/browser/playwright-driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AA+B7E,OAAO,KAAK,EACV,aAAa,EAGb,kBAAkB,EAElB,cAAc,EAOd,eAAe,EAChB,MAAM,aAAa,CAAC;AAgDrB,MAAM,WAAW,uBAAuB;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IACxC,uGAAuG;IACvG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;IAChD,mDAAmD;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED,KAAK,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;AAsDhE,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,KAAK,EAAE,OAAO;CAO3B;AAED;;;;;GAKG;AACH;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,6BAA6B,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBjH;AAgXD;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,kBAAkB,CA6FpB"}
1
+ {"version":3,"file":"playwright-driver.d.ts","sourceRoot":"","sources":["../../src/browser/playwright-driver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAgB7E,OAAO,KAAK,EACV,aAAa,EAGb,kBAAkB,EAElB,cAAc,EAQd,eAAe,EAChB,MAAM,aAAa,CAAC;AAuBrB,MAAM,WAAW,uBAAuB;IACtC,4KAA4K;IAC5K,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IACxC,uGAAuG;IACvG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;IAChD,mDAAmD;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,8NAA8N;IAC9N,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED,KAAK,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;AAgDhE,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,KAAK,EAAE,OAAO;CAO3B;AAED,6NAA6N;AAC7N,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,6BAA6B,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBjH;AAgWD,gNAAgN;AAChN,wBAAgB,OAAO,CACrB,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,kBAAkB,CAgFpB"}
@@ -1,45 +1,7 @@
1
1
  /**
2
- * The real `BrowserDriver`, over `@descryy/runtime-browser`.
3
- *
4
- * ## Why the import is still dynamic
5
- *
6
- * `@descryy/runtime-browser` is now a declared dependency of `@descryy/mcp`
7
- * (`mcp-browser-tools.md` §3.1's publish landed) — so the reason this module
8
- * used to give for a call-time specifier import, "not on the registry yet,"
9
- * no longer holds. It stays dynamic anyway, for a reason that outgrew that
10
- * one: `loadRuntimeModule` (`PlaywrightDriverOptions`, below) is a real
11
- * injection seam that `browser-action-settle.test.ts` uses today to drive
12
- * this file's settle logic against a fake module shaped like this one,
13
- * deterministically, with no real browser involved. A static
14
- * `import … from "@descryy/runtime-browser"` at the top of this file would
15
- * remove that seam along with the indirection, and nothing here needs
16
- * removing it — the package being on the registry made the *dependency*
17
- * declarable, not the import path mandatory. Absence (package missing,
18
- * import throws, or Chromium missing) is still a **named refusal with a
19
- * remedy**, exactly as before — see `provider.ts`.
20
- *
21
- * ## What this adds over the runtime package, and what it deliberately does not
22
- *
23
- * It adds exactly one thing the runtime has no API for: **element refs.** The
24
- * port's contract is that an action names an element the agent has seen, and
25
- * `BrowserActionCollector` acts on raw selectors. So this module enumerates
26
- * the page's interactive elements, holds a Playwright `ElementHandle` per ref,
27
- * and drops the whole map on navigation. Handles rather than injected `data-`
28
- * attributes on purpose: stamping the DOM would make the act of observing
29
- * change the page being observed, and a detached element then fails naturally
30
- * instead of resolving to whatever took its place.
31
- *
32
- * Everything else is composition. Launch, click, type, fill, console capture,
33
- * network capture, the `fetch()` initiator stack and its source-map rewrite
34
- * are all the runtime package's, called rather than reimplemented.
35
- *
36
- * ## Rule 1
37
- *
38
- * No language is named here. The stack-trace parser that turns a raw browser
39
- * stack into resolved frames is handed in by the caller as a module specifier
40
- * — the same mechanism `runtime-registry.ts` uses for runtime adapters, for
41
- * the same reason. Without one, requests are still observed and no caller
42
- * edge is written; the reply says so.
2
+ * The real `BrowserDriver`, over `@descryy/runtime-browser`. Import stays
3
+ * dynamic — `loadRuntimeModule` is a test injection seam (fake module, no
4
+ * browser needed). Adds only element refs over the runtime; stack-trace parsing is caller-supplied (rule 1: no language named here).
43
5
  */
44
6
  var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
45
7
  if (typeof path === "string" && /^\.\.?\//.test(path)) {
@@ -53,40 +15,15 @@ import { BrowserSessionCrashedError, UnknownElementRefError } from "./driver.js"
53
15
  /** The module the driver composes. Named once, imported at call time. */
54
16
  const RUNTIME_BROWSER_MODULE = "@descryy/runtime-browser";
55
17
  const defaultRuntimeModuleLoader = () => import(__rewriteRelativeImportExtension(RUNTIME_BROWSER_MODULE));
56
- /**
57
- * How long an action waits for the page to *start* doing something.
58
- *
59
- * This is the one half of the wait that cannot be made conditional: there is
60
- * no signal for "this click will never issue a request", and proving that
61
- * negative is proving a negative. It is the old fixed 300ms, kept at its old
62
- * value so an action that causes nothing observes exactly what it observed
63
- * before — and it is now a *ceiling* rather than a duration, since any
64
- * request that starts inside it moves the wait onto the settle condition
65
- * below.
66
- */
18
+ /** Ceiling on waiting for network activity to *start* — no signal proves "never will". Kept at the old fixed 300ms so a no-op action observes the same as before. */
67
19
  const ACTIVITY_GRACE_MS = 300;
68
- /**
69
- * After the last request lifecycle event, how long of nothing counts as
70
- * settled. Short on purpose: this fires only once activity has been seen, so
71
- * it measures a gap between requests, not a guess about whether any will come.
72
- */
20
+ /** How long after the last request event counts as settled; fires only once activity is seen, so it measures a gap, not a guess about more coming. */
73
21
  const NETWORK_QUIET_MS = 120;
74
- /**
75
- * The ceiling on the whole wait. A page that never stops requesting must not
76
- * hold a tool call open forever; reaching this is recorded on the observation
77
- * and disclosed, never swallowed.
78
- */
22
+ /** Ceiling on the whole wait; reaching it is recorded on the observation and disclosed, never swallowed. */
79
23
  const SETTLE_TIMEOUT_MS = 5_000;
80
24
  /** How often the settle loop re-checks. Small enough not to add latency of its own. */
81
25
  const POLL_INTERVAL_MS = 10;
82
- /**
83
- * Elements an agent can act on.
84
- *
85
- * Deliberately narrow. A snapshot of every node on the page would be
86
- * enormous, mostly unactionable, and would push the reply budget into
87
- * dropping the elements that matter. `[role]` is included so an application
88
- * that builds its own controls is not invisible.
89
- */
26
+ /** Elements an agent can act on — deliberately narrow; a full-page snapshot would blow the reply budget. `[role]` included for custom controls. */
90
27
  const INTERACTIVE = "a, button, input, select, textarea, [role], [onclick], summary";
91
28
  export class BrowserBackendMissingError extends Error {
92
29
  constructor(cause) {
@@ -119,9 +56,11 @@ async function openSession(runtime, session, launchOptions, options) {
119
56
  const emitted = [];
120
57
  const actions = [];
121
58
  const unsettled = [];
122
- // The request lifecycle, counted rather than slept through. `inFlight` is
123
- // what "the page is still doing something" means, and `lastEventAt` is what
124
- // separates a gap between two requests from the end of them.
59
+ // Never cleared, unlike `actions` — the session's whole-lifetime provenance record for
60
+ // `browser_save_scenario`, not per-drain evidence. Goes out of reach the moment this closure
61
+ // does (session closed/reaped) — there is no other path back to it.
62
+ const performed = [];
63
+ // inFlight/lastEventAt track request activity: counted, not slept through.
125
64
  let inFlight = 0;
126
65
  let lastEventAt = 0;
127
66
  session.page.on("request", () => {
@@ -160,12 +99,9 @@ async function openSession(runtime, session, launchOptions, options) {
160
99
  ...(options.service === undefined ? {} : { service: options.service }),
161
100
  ...(options.stackParser === undefined ? {} : { stackTraceParser: options.stackParser }),
162
101
  });
163
- // Started before the first navigation, deliberately. The initiator capture
164
- // wraps `fetch` via an init script, and Playwright's own contract is that
165
- // an init script does not reach a document already loaded — so a collector
166
- // started after navigating would observe a page it could never have
167
- // instrumented, and would report an honest-looking empty stack for every
168
- // request.
102
+ // Started before first navigation: Playwright's init-script contract means
103
+ // a collector started later misses instrumentation, reporting an
104
+ // honest-looking but empty stack.
169
105
  await actionCollector.start(context);
170
106
  await consoleCollector.start(context);
171
107
  await networkCollector.start(context);
@@ -183,26 +119,10 @@ async function openSession(runtime, session, launchOptions, options) {
183
119
  function recordAction(action, ref, mode) {
184
120
  actions.push({ action, ref, mode, url: session.page.url(), at: new Date().toISOString() });
185
121
  }
186
- /**
187
- * Waits for the network activity this action caused, and records it when it
188
- * does not arrive.
189
- *
190
- * Two conditions, because there are two different questions:
191
- *
192
- * - **Did anything start?** There is no signal for "nothing will", so this
193
- * half is a bounded wait — `ACTIVITY_GRACE_MS`, the old fixed sleep's own
194
- * value, so an action that causes nothing observes exactly what it did
195
- * before. It is a ceiling now rather than a duration: the moment a
196
- * request starts, the wait moves onto the second condition.
197
- * - **Has it finished?** Nothing in flight, and nothing new for
198
- * `NETWORK_QUIET_MS`. This is the half the fixed sleep got wrong: a
199
- * request that took longer than 300ms was not waited for and its
200
- * evidence was silently absent from the reply.
201
- *
202
- * Whichever condition is running, the whole wait is capped at
203
- * `SETTLE_TIMEOUT_MS` — and reaching that cap is recorded on the
204
- * observation, never swallowed.
205
- */
122
+ function recordPerformed(action, target, url, text) {
123
+ performed.push({ action, target, url, text, at: new Date().toISOString() });
124
+ }
125
+ /** Waits for the action's network effect (started within `ACTIVITY_GRACE_MS`, then quiet for `NETWORK_QUIET_MS`), capped at `SETTLE_TIMEOUT_MS` — reaching the cap is recorded, never swallowed. */
206
126
  async function settleAfterAction(action, ref) {
207
127
  const startedAt = Date.now();
208
128
  lastEventAt = startedAt;
@@ -241,6 +161,7 @@ async function openSession(runtime, session, launchOptions, options) {
241
161
  await dropRefs();
242
162
  await actionCollector.navigate(url);
243
163
  recordAction("navigate", null, null);
164
+ recordPerformed("navigate", null, url, null);
244
165
  },
245
166
  async snapshot() {
246
167
  assertUsable();
@@ -266,25 +187,18 @@ async function openSession(runtime, session, launchOptions, options) {
266
187
  : tag === "input" && type === "checkbox"
267
188
  ? "checkbox"
268
189
  : tag);
269
- // A pragmatic accessible name, not a full AccName implementation:
270
- // the label a person would read, in the order a person would find
271
- // it. Named as an approximation rather than presented as the
272
- // accessibility tree's own answer.
190
+ // A pragmatic accessible-name approximation, not a full AccName implementation.
273
191
  const name = el.getAttribute("aria-label") ??
274
192
  el.innerText?.trim() ??
275
193
  el.textContent?.trim() ??
276
194
  el.getAttribute("placeholder") ??
277
195
  el.getAttribute("name") ??
278
196
  "";
279
- // --- a real, re-resolvable selector, computed in this same
280
- // round trip so role/name/selector are all read off one live
281
- // DOM state. Everything below runs *inside the page* — it is
282
- // serialized by Playwright, so it must be entirely self
283
- // contained (no closures over the outer module).
284
- // An id that looks generated rather than authored: a hex run
285
- // (a hashed/uuid-derived id), React's `:r0:`-style useId
286
- // output, or blank. Rejected because "stable across a reload"
287
- // is exactly the property a generated id does not have.
197
+ // Computed in this same round trip so role/name/selector read off
198
+ // one live DOM state; runs inside the page (serialized by
199
+ // Playwright), so it must not close over the outer module.
200
+ // Rejects ids that look generated (hex run, React's `:r0:` useId
201
+ // style, or blank) — "stable across reload" is what a generated id lacks.
288
202
  const GENERATED_ID = /[0-9a-f]{8}|:r[0-9a-z]+:|^\s*$/i;
289
203
  const FORM_CONTROLS = ["input", "select", "textarea", "button"];
290
204
  function escapeAttrValue(value) {
@@ -295,9 +209,7 @@ async function openSession(runtime, session, launchOptions, options) {
295
209
  return el.ownerDocument.querySelectorAll(candidate).length === 1;
296
210
  }
297
211
  catch {
298
- // An attribute value that produces an invalid selector (rare,
299
- // but not impossible) is treated as "no candidate" rather
300
- // than thrown — the caller falls through to the next tier.
212
+ // Invalid selector (rare) is treated as "no candidate", not thrown.
301
213
  return false;
302
214
  }
303
215
  }
@@ -344,12 +256,9 @@ async function openSession(runtime, session, launchOptions, options) {
344
256
  return null;
345
257
  }
346
258
  function computeSelector(target) {
347
- // Priority order: first candidate that is unique in the whole
348
- // document wins. Each tier is checked for uniqueness on its
349
- // own — a data-testid shared by two elements is not
350
- // addressable either, and the next tier gets its turn rather
351
- // than giving up at the first attribute that happens to be
352
- // present.
259
+ // Priority order: first unique candidate wins. Each tier is checked
260
+ // independently — a shared data-testid isn't addressable either,
261
+ // so the next tier gets a turn instead of stopping there.
353
262
  const testId = target.getAttribute("data-testid");
354
263
  if (testId !== null && testId.trim() !== "") {
355
264
  const candidate = `[data-testid="${escapeAttrValue(testId)}"]`;
@@ -405,16 +314,16 @@ async function openSession(runtime, session, launchOptions, options) {
405
314
  },
406
315
  async click(ref) {
407
316
  assertUsable();
408
- await resolve(ref).handle.click();
317
+ const { element, handle } = resolve(ref);
318
+ await handle.click();
409
319
  recordAction("click", ref, null);
410
- // `requestfinished` fires after the page's own handler resolves, so a
411
- // drain immediately after a click would miss the request the click
412
- // caused — which is the request this whole feature exists to witness.
320
+ recordPerformed("click", { role: element.role, name: element.name, selector: element.selector }, null, null);
321
+ // requestfinished fires after the page's handler resolves; draining immediately would miss the very request being watched for.
413
322
  await settleAfterAction("click", ref);
414
323
  },
415
324
  async type(ref, text) {
416
325
  assertUsable();
417
- const { handle } = resolve(ref);
326
+ const { element, handle } = resolve(ref);
418
327
  if (handle.pressSequentially !== undefined)
419
328
  await handle.pressSequentially(text);
420
329
  else if (handle.type !== undefined)
@@ -422,12 +331,15 @@ async function openSession(runtime, session, launchOptions, options) {
422
331
  else
423
332
  throw new Error("this Playwright build exposes no keystroke-level typing method");
424
333
  recordAction("type", ref, "append");
334
+ recordPerformed("type", { role: element.role, name: element.name, selector: element.selector }, null, text);
425
335
  await settleAfterAction("type", ref);
426
336
  },
427
337
  async fill(ref, text) {
428
338
  assertUsable();
429
- await resolve(ref).handle.fill(text);
339
+ const { element, handle } = resolve(ref);
340
+ await handle.fill(text);
430
341
  recordAction("fill", ref, "replace");
342
+ recordPerformed("fill", { role: element.role, name: element.name, selector: element.selector }, null, text);
431
343
  await settleAfterAction("fill", ref);
432
344
  },
433
345
  async drainEvidence() {
@@ -437,13 +349,15 @@ async function openSession(runtime, session, launchOptions, options) {
437
349
  unsettled.length = 0;
438
350
  return observation;
439
351
  },
352
+ performedSteps() {
353
+ return [...performed];
354
+ },
440
355
  async close() {
441
356
  if (finalObservation !== null)
442
357
  return finalObservation;
443
358
  closed = true;
444
- // Collectors are stopped before the observation is taken: a stop
445
- // flushes whatever the collector was still holding, and taking the
446
- // observation first would drop exactly that.
359
+ // Stopped before observing: a stop flushes what each collector was
360
+ // still holding, which observing first would drop.
447
361
  await actionCollector.stop().catch(() => { });
448
362
  await consoleCollector.stop().catch(() => { });
449
363
  await networkCollector.stop().catch(() => { });
@@ -458,33 +372,16 @@ async function openSession(runtime, session, launchOptions, options) {
458
372
  },
459
373
  };
460
374
  }
461
- /**
462
- * Turn what the collectors emitted into the port's vocabulary.
463
- *
464
- * The request/response join is on `requestId`, because a `NETWORK_REQUEST`
465
- * carries the method, the URL and the call-site stack while only the matching
466
- * `NETWORK_RESPONSE` knows the status. A request with no response is kept with
467
- * `status: null` — "never answered" is a different fact from a 500, and the
468
- * port says so.
469
- *
470
- * Exported for `browser-request-outcome-mapping.test.ts` — see
471
- * `EmittedEvidence`'s doc comment for why.
472
- */
375
+ /** Turns collector evidence into the port's vocabulary. Joins request/response on `requestId`, keeping `status: null` (not guessed) when unanswered. Exported for `browser-request-outcome-mapping.test.ts`. */
473
376
  export function collect(emitted, actions, unsettled) {
474
- // Two joins, in priority order, because `requestId` is a *correlation
475
- // header* and not a transport id: it exists only when the application
476
- // itself propagates one. The fixture's own delete sets `x-request-id`, so
477
- // an id-only join looked correct while silently reporting `status: null`
478
- // for every request the page issued without one — which is most of them.
377
+ // Two joins, by priority: `requestId` is a correlation header the app must
378
+ // itself propagate, not a transport id — an id-only join silently reported
379
+ // `status: null` for every request without one, i.e. most of them.
479
380
  const statusByRequestId = new Map();
480
381
  const statusByUrl = new Map();
481
- // B3: the same two joins, for `outcome`. `descry-runtime` stamps
482
- // `outcome: "answered"` on every `NETWORK_RESPONSE` and `"unanswered"` on a
483
- // paired `HTTP_ERROR` (a real `requestfailed`, a collector timeout, or a
484
- // WebSocket `socketerror`); it never emits `"pending"` itself — a request
485
- // still open when the drain runs simply has no terminal event at all,
486
- // which is why `pairedRequestIds`/`pairedUrls` below track "a terminal
487
- // event existed" separately from "and it named a recognisable outcome".
382
+ // B3: same two joins, for `outcome`. `descry-runtime` stamps "answered" or
383
+ // "unanswered", never "pending" itself — a still-open request at drain time
384
+ // has no terminal event, which pairedRequestIds/pairedUrls track separately.
488
385
  const outcomeByRequestId = new Map();
489
386
  const outcomeByUrl = new Map();
490
387
  const pairedRequestIds = new Set();
@@ -502,9 +399,7 @@ export function collect(emitted, actions, unsettled) {
502
399
  if (typeof status === "number") {
503
400
  if (evidence.requestId !== null)
504
401
  statusByRequestId.set(evidence.requestId, status);
505
- // Last response wins: a page that requests the same URL twice reports
506
- // the most recent outcome, which is the one a caller reading this
507
- // reply is asking about.
402
+ // Last response wins: repeat requests report the most recent outcome.
508
403
  if (typeof url === "string")
509
404
  statusByUrl.set(url, status);
510
405
  }
@@ -528,12 +423,9 @@ export function collect(emitted, actions, unsettled) {
528
423
  const requestId = evidence.requestId;
529
424
  const pairedOutcome = (requestId === null ? undefined : outcomeByRequestId.get(requestId)) ?? outcomeByUrl.get(url);
530
425
  const wasPaired = (requestId !== null && pairedRequestIds.has(requestId)) || pairedUrls.has(url);
531
- // A recognised outcome from the paired terminal event wins; no terminal
532
- // event at all means the request was still in flight when the drain
533
- // happened, so "pending"; a terminal event that exists but names no
534
- // recognisable outcome (an older `descry-runtime` build) leaves the
535
- // field undefined rather than guessing — `isFailedRequest`'s documented
536
- // fallback is what handles that case, not this function.
426
+ // Paired outcome wins; no terminal event means still in-flight
427
+ // ("pending"); an unrecognised outcome (older runtime build) leaves the
428
+ // field undefined — `isFailedRequest`'s fallback handles that, not this.
537
429
  const outcome = pairedOutcome ?? (wasPaired ? undefined : "pending");
538
430
  requests.push({
539
431
  method,
@@ -547,9 +439,7 @@ export function collect(emitted, actions, unsettled) {
547
439
  });
548
440
  continue;
549
441
  }
550
- // An uncaught page error and a console error are both "the page reported
551
- // something went wrong"; a caller triaging them does not need to know
552
- // which listener fired.
442
+ // Uncaught page error and console error both mean "something went wrong"; callers don't need to know which listener fired.
553
443
  if (evidence.eventType === "EXCEPTION" || evidence.eventType === "CONSOLE_MESSAGE") {
554
444
  const level = evidence.payload["level"] ?? evidence.payload["type"];
555
445
  if (evidence.eventType === "CONSOLE_MESSAGE" && level !== "error")
@@ -565,30 +455,14 @@ export function collect(emitted, actions, unsettled) {
565
455
  }
566
456
  return { actions: [...actions], consoleErrors, requests, unsettled: [...unsettled] };
567
457
  }
568
- /**
569
- * Reads a terminal event's `outcome` off an untyped payload.
570
- *
571
- * Validated rather than cast, exactly like `scriptUrlOutcomesOf` below: this
572
- * crosses a package boundary — the field is `@descryy/runtime-browser`'s to
573
- * set, and this build does not depend on that package's types — so an
574
- * absent or unrecognised value must degrade to "no outcome reported", never
575
- * be cast through as one of the three known literals it might not actually
576
- * be.
577
- */
458
+ /** Reads `outcome` off an untyped cross-package payload; validated not cast, since this build doesn't depend on `@descryy/runtime-browser`'s types — unrecognised values degrade to undefined, never a guessed literal. */
578
459
  function requestOutcomeOf(payload) {
579
460
  const raw = payload["outcome"];
580
461
  if (raw === "answered" || raw === "unanswered" || raw === "pending")
581
462
  return raw;
582
463
  return undefined;
583
464
  }
584
- /**
585
- * Reads the collector's `scriptUrlMapping` off an untyped payload.
586
- *
587
- * Validated rather than cast. This crosses a package boundary — the shape is
588
- * `@descryy/runtime-browser`'s and this build does not even depend on it — so
589
- * a malformed or absent field must degrade to "no mapping was reported",
590
- * never to a half-populated outcome that reads like a real one.
591
- */
465
+ /** Reads `scriptUrlMapping` off an untyped cross-package payload; validated not cast, so a malformed/absent field degrades to "no mapping reported" rather than a half-populated result. */
592
466
  function scriptUrlOutcomesOf(payload) {
593
467
  const raw = payload["scriptUrlMapping"];
594
468
  if (!Array.isArray(raw))
@@ -611,19 +485,7 @@ function scriptUrlOutcomesOf(payload) {
611
485
  }
612
486
  return outcomes;
613
487
  }
614
- /**
615
- * A short, readable rendering of a parsed stack.
616
- *
617
- * `StackTrace` carries frames, not the original text — the raw string is
618
- * consumed by the parser and deliberately not kept, since a parsed frame with
619
- * a resolved on-disk path is strictly more useful than the `http://` line it
620
- * came from. So the port's `stackText` is rendered from the frames rather
621
- * than passed through, and it is for a human reading the reply: the R4 write
622
- * uses `stackTrace`, never this.
623
- *
624
- * The primary frame is marked, because it is the one a developer should be
625
- * sent to and it is not always the innermost.
626
- */
488
+ /** Human-readable rendering of parsed stack frames, for the reply; the R4 write uses `stackTrace` itself, never this. Primary frame marked since it isn't always the innermost. */
627
489
  function renderStack(stack) {
628
490
  if (stack === null || stack.frames.length === 0)
629
491
  return null;