@descryy/mcp 0.1.2 → 0.3.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 (207) hide show
  1. package/dist/bin/descry-mcp.js +30 -2
  2. package/dist/bin/descry-mcp.js.map +1 -1
  3. package/dist/browser/driver.d.ts +236 -0
  4. package/dist/browser/driver.d.ts.map +1 -0
  5. package/dist/browser/driver.js +70 -0
  6. package/dist/browser/driver.js.map +1 -0
  7. package/dist/browser/evidence.d.ts +47 -0
  8. package/dist/browser/evidence.d.ts.map +1 -0
  9. package/dist/browser/evidence.js +55 -0
  10. package/dist/browser/evidence.js.map +1 -0
  11. package/dist/browser/fake-driver.d.ts +58 -0
  12. package/dist/browser/fake-driver.d.ts.map +1 -0
  13. package/dist/browser/fake-driver.js +161 -0
  14. package/dist/browser/fake-driver.js.map +1 -0
  15. package/dist/browser/graph-write.d.ts +86 -0
  16. package/dist/browser/graph-write.d.ts.map +1 -0
  17. package/dist/browser/graph-write.js +135 -0
  18. package/dist/browser/graph-write.js.map +1 -0
  19. package/dist/browser/playwright-driver.d.ts +65 -0
  20. package/dist/browser/playwright-driver.d.ts.map +1 -0
  21. package/dist/browser/playwright-driver.js +359 -0
  22. package/dist/browser/playwright-driver.js.map +1 -0
  23. package/dist/browser/provider.d.ts +49 -0
  24. package/dist/browser/provider.d.ts.map +1 -0
  25. package/dist/browser/provider.js +28 -0
  26. package/dist/browser/provider.js.map +1 -0
  27. package/dist/browser/registry.d.ts +182 -0
  28. package/dist/browser/registry.d.ts.map +1 -0
  29. package/dist/browser/registry.js +215 -0
  30. package/dist/browser/registry.js.map +1 -0
  31. package/dist/browser/scenario-resolve.d.ts +62 -0
  32. package/dist/browser/scenario-resolve.d.ts.map +1 -0
  33. package/dist/browser/scenario-resolve.js +127 -0
  34. package/dist/browser/scenario-resolve.js.map +1 -0
  35. package/dist/browser/scenario-runner.d.ts +113 -0
  36. package/dist/browser/scenario-runner.d.ts.map +1 -0
  37. package/dist/browser/scenario-runner.js +315 -0
  38. package/dist/browser/scenario-runner.js.map +1 -0
  39. package/dist/browser/stack-parser.d.ts +43 -0
  40. package/dist/browser/stack-parser.d.ts.map +1 -0
  41. package/dist/browser/stack-parser.js +112 -0
  42. package/dist/browser/stack-parser.js.map +1 -0
  43. package/dist/browser/tool-support.d.ts +113 -0
  44. package/dist/browser/tool-support.d.ts.map +1 -0
  45. package/dist/browser/tool-support.js +181 -0
  46. package/dist/browser/tool-support.js.map +1 -0
  47. package/dist/disclosure-ledger.d.ts +38 -0
  48. package/dist/disclosure-ledger.d.ts.map +1 -0
  49. package/dist/disclosure-ledger.js +40 -0
  50. package/dist/disclosure-ledger.js.map +1 -0
  51. package/dist/index.d.ts +12 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +10 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/protocol.d.ts +12 -3
  56. package/dist/protocol.d.ts.map +1 -1
  57. package/dist/protocol.js +12 -3
  58. package/dist/protocol.js.map +1 -1
  59. package/dist/render.d.ts +121 -10
  60. package/dist/render.d.ts.map +1 -1
  61. package/dist/render.js +96 -15
  62. package/dist/render.js.map +1 -1
  63. package/dist/runtime-registry.d.ts +63 -0
  64. package/dist/runtime-registry.d.ts.map +1 -0
  65. package/dist/runtime-registry.js +131 -0
  66. package/dist/runtime-registry.js.map +1 -0
  67. package/dist/scenarios/index.d.ts +4 -0
  68. package/dist/scenarios/index.d.ts.map +1 -0
  69. package/dist/scenarios/index.js +4 -0
  70. package/dist/scenarios/index.js.map +1 -0
  71. package/dist/scenarios/parse.d.ts +32 -0
  72. package/dist/scenarios/parse.d.ts.map +1 -0
  73. package/dist/scenarios/parse.js +199 -0
  74. package/dist/scenarios/parse.js.map +1 -0
  75. package/dist/scenarios/scenario.d.ts +115 -0
  76. package/dist/scenarios/scenario.d.ts.map +1 -0
  77. package/dist/scenarios/scenario.js +41 -0
  78. package/dist/scenarios/scenario.js.map +1 -0
  79. package/dist/scenarios/storage.d.ts +51 -0
  80. package/dist/scenarios/storage.d.ts.map +1 -0
  81. package/dist/scenarios/storage.js +93 -0
  82. package/dist/scenarios/storage.js.map +1 -0
  83. package/dist/server.d.ts.map +1 -1
  84. package/dist/server.js +11 -3
  85. package/dist/server.js.map +1 -1
  86. package/dist/session.d.ts +142 -2
  87. package/dist/session.d.ts.map +1 -1
  88. package/dist/session.js +266 -4
  89. package/dist/session.js.map +1 -1
  90. package/dist/tools/analyze.d.ts +41 -1
  91. package/dist/tools/analyze.d.ts.map +1 -1
  92. package/dist/tools/analyze.js +111 -9
  93. package/dist/tools/analyze.js.map +1 -1
  94. package/dist/tools/browser-click.d.ts +36 -0
  95. package/dist/tools/browser-click.d.ts.map +1 -0
  96. package/dist/tools/browser-click.js +117 -0
  97. package/dist/tools/browser-click.js.map +1 -0
  98. package/dist/tools/browser-close-session.d.ts +24 -0
  99. package/dist/tools/browser-close-session.d.ts.map +1 -0
  100. package/dist/tools/browser-close-session.js +63 -0
  101. package/dist/tools/browser-close-session.js.map +1 -0
  102. package/dist/tools/browser-fill.d.ts +49 -0
  103. package/dist/tools/browser-fill.d.ts.map +1 -0
  104. package/dist/tools/browser-fill.js +138 -0
  105. package/dist/tools/browser-fill.js.map +1 -0
  106. package/dist/tools/browser-navigate.d.ts +32 -0
  107. package/dist/tools/browser-navigate.d.ts.map +1 -0
  108. package/dist/tools/browser-navigate.js +103 -0
  109. package/dist/tools/browser-navigate.js.map +1 -0
  110. package/dist/tools/browser-run-scenario.d.ts +54 -0
  111. package/dist/tools/browser-run-scenario.d.ts.map +1 -0
  112. package/dist/tools/browser-run-scenario.js +228 -0
  113. package/dist/tools/browser-run-scenario.js.map +1 -0
  114. package/dist/tools/browser-save-scenario.d.ts +44 -0
  115. package/dist/tools/browser-save-scenario.d.ts.map +1 -0
  116. package/dist/tools/browser-save-scenario.js +298 -0
  117. package/dist/tools/browser-save-scenario.js.map +1 -0
  118. package/dist/tools/browser-snapshot.d.ts +60 -0
  119. package/dist/tools/browser-snapshot.d.ts.map +1 -0
  120. package/dist/tools/browser-snapshot.js +139 -0
  121. package/dist/tools/browser-snapshot.js.map +1 -0
  122. package/dist/tools/browser-start-session.d.ts +43 -0
  123. package/dist/tools/browser-start-session.d.ts.map +1 -0
  124. package/dist/tools/browser-start-session.js +272 -0
  125. package/dist/tools/browser-start-session.js.map +1 -0
  126. package/dist/tools/browser-type.d.ts +48 -0
  127. package/dist/tools/browser-type.d.ts.map +1 -0
  128. package/dist/tools/browser-type.js +136 -0
  129. package/dist/tools/browser-type.js.map +1 -0
  130. package/dist/tools/contracts.d.ts +15 -0
  131. package/dist/tools/contracts.d.ts.map +1 -1
  132. package/dist/tools/contracts.js +14 -3
  133. package/dist/tools/contracts.js.map +1 -1
  134. package/dist/tools/cross-pr.d.ts.map +1 -1
  135. package/dist/tools/cross-pr.js +1 -0
  136. package/dist/tools/cross-pr.js.map +1 -1
  137. package/dist/tools/git-diff.d.ts.map +1 -1
  138. package/dist/tools/git-diff.js +104 -4
  139. package/dist/tools/git-diff.js.map +1 -1
  140. package/dist/tools/git-history.d.ts.map +1 -1
  141. package/dist/tools/git-history.js +1 -0
  142. package/dist/tools/git-history.js.map +1 -1
  143. package/dist/tools/history.js +1 -1
  144. package/dist/tools/history.js.map +1 -1
  145. package/dist/tools/impact.d.ts +9 -0
  146. package/dist/tools/impact.d.ts.map +1 -1
  147. package/dist/tools/impact.js +4 -4
  148. package/dist/tools/impact.js.map +1 -1
  149. package/dist/tools/index.d.ts +12 -1
  150. package/dist/tools/index.d.ts.map +1 -1
  151. package/dist/tools/index.js +31 -0
  152. package/dist/tools/index.js.map +1 -1
  153. package/dist/tools/kit.d.ts +11 -1
  154. package/dist/tools/kit.d.ts.map +1 -1
  155. package/dist/tools/kit.js +3 -0
  156. package/dist/tools/kit.js.map +1 -1
  157. package/dist/tools/link-workspace.d.ts.map +1 -1
  158. package/dist/tools/link-workspace.js +13 -1
  159. package/dist/tools/link-workspace.js.map +1 -1
  160. package/dist/tools/mark-incident.d.ts +69 -0
  161. package/dist/tools/mark-incident.d.ts.map +1 -0
  162. package/dist/tools/mark-incident.js +212 -0
  163. package/dist/tools/mark-incident.js.map +1 -0
  164. package/dist/tools/observe-runtime.d.ts +292 -0
  165. package/dist/tools/observe-runtime.d.ts.map +1 -0
  166. package/dist/tools/observe-runtime.js +1192 -0
  167. package/dist/tools/observe-runtime.js.map +1 -0
  168. package/dist/tools/observe-tests.d.ts +125 -0
  169. package/dist/tools/observe-tests.d.ts.map +1 -0
  170. package/dist/tools/observe-tests.js +313 -0
  171. package/dist/tools/observe-tests.js.map +1 -0
  172. package/dist/tools/pr-analysis.d.ts +71 -13
  173. package/dist/tools/pr-analysis.d.ts.map +1 -1
  174. package/dist/tools/pr-analysis.js +55 -12
  175. package/dist/tools/pr-analysis.js.map +1 -1
  176. package/dist/tools/pre-push.d.ts +77 -0
  177. package/dist/tools/pre-push.d.ts.map +1 -0
  178. package/dist/tools/pre-push.js +250 -0
  179. package/dist/tools/pre-push.js.map +1 -0
  180. package/dist/tools/propagation.d.ts.map +1 -1
  181. package/dist/tools/propagation.js +1 -0
  182. package/dist/tools/propagation.js.map +1 -1
  183. package/dist/tools/questions.d.ts.map +1 -1
  184. package/dist/tools/questions.js +101 -14
  185. package/dist/tools/questions.js.map +1 -1
  186. package/dist/tools/refusal-fetch.d.ts.map +1 -1
  187. package/dist/tools/refusal-fetch.js +1 -0
  188. package/dist/tools/refusal-fetch.js.map +1 -1
  189. package/dist/tools/runtime-incident.d.ts +92 -0
  190. package/dist/tools/runtime-incident.d.ts.map +1 -0
  191. package/dist/tools/runtime-incident.js +144 -0
  192. package/dist/tools/runtime-incident.js.map +1 -0
  193. package/dist/tools/scope.d.ts.map +1 -1
  194. package/dist/tools/scope.js +1 -0
  195. package/dist/tools/scope.js.map +1 -1
  196. package/dist/tools/similar-incidents.d.ts +13 -0
  197. package/dist/tools/similar-incidents.d.ts.map +1 -1
  198. package/dist/tools/similar-incidents.js +22 -8
  199. package/dist/tools/similar-incidents.js.map +1 -1
  200. package/dist/tools/verification-status.d.ts +22 -0
  201. package/dist/tools/verification-status.d.ts.map +1 -1
  202. package/dist/tools/verification-status.js +52 -5
  203. package/dist/tools/verification-status.js.map +1 -1
  204. package/dist/tools/verify-claim.d.ts.map +1 -1
  205. package/dist/tools/verify-claim.js +5 -0
  206. package/dist/tools/verify-claim.js.map +1 -1
  207. package/package.json +16 -4
@@ -0,0 +1,138 @@
1
+ /**
2
+ * `browser_fill` — set a field's value directly.
3
+ *
4
+ * ## Consent, and why this tool asks for none
5
+ *
6
+ * `DEC-380` grants consent once, at
7
+ * `browser_start_session`, bound to that call's origin. So this tool declares
8
+ * `class: "action"` — it does change a running application, and pretending
9
+ * otherwise would be the lie — while its `describeAction` returns `undefined`,
10
+ * the existing "this particular call has nothing to confirm" path that
11
+ * `questions` uses when asked to read rather than write.
12
+ *
13
+ * The property that makes that safe rather than merely cheap: **a verb cannot
14
+ * mint consent for itself.** Called against a session with no consent record,
15
+ * it refuses. There is deliberately no path from a click to a token.
16
+ *
17
+ * ## A ref, never a selector
18
+ *
19
+ * The element is named by a ref from a snapshot the calling agent actually
20
+ * saw. A raw selector would let an agent act on an element it never looked
21
+ * at, and a ref from a superseded snapshot refuses rather than resolving
22
+ * against whatever now matches — that is the same mistake, arriving later.
23
+ */
24
+ /**
25
+ * ## The trap this tool sets, stated where a reader meets it
26
+ *
27
+ * `fill` is faster and more reliable than typing, and for most fields it is
28
+ * the right verb. It also fires **no key events whatsoever** — the value is
29
+ * set directly. Any behaviour the page attaches to typing simply does not
30
+ * happen: autocompletes stay closed, per-character validation never runs, a
31
+ * debounced search never fires. The field ends up holding the right string
32
+ * and the page never learns it was touched.
33
+ *
34
+ * That is a correct tool producing a wrong answer, so the distinction is in
35
+ * the schema prose and not only here.
36
+ */
37
+ import { browserAnswer, emptyEvidence, mutationRefusal, observeAndCorrelate, useSession } from "../browser/tool-support.js";
38
+ import { NO_GRAPH_WRITE } from "../browser/graph-write.js";
39
+ import { UnknownElementRefError } from "../browser/driver.js";
40
+ import { requiredString } from "./kit.js";
41
+ const SCHEMA = {
42
+ type: "object",
43
+ properties: {
44
+ sessionId: {
45
+ type: "string",
46
+ description: "REQUIRED. The id returned by browser_start_session.",
47
+ },
48
+ ref: {
49
+ type: "string",
50
+ description: "REQUIRED. An element ref from a browser_snapshot reply. Refs are issued per snapshot and are " +
51
+ "invalidated by navigation — a stale ref is refused rather than resolved against whatever now " +
52
+ "matches it, so take a fresh snapshot after the page changes.",
53
+ },
54
+ text: {
55
+ type: "string",
56
+ description: "REQUIRED. The value to put in the field, REPLACING whatever is there. Set directly, so the page " +
57
+ "sees no keydown, keypress or keyup at all — if the field has an autocomplete, per-character " +
58
+ "validation or a debounced search, none of it will fire. Use browser_type for those.",
59
+ },
60
+ confirmToken: {
61
+ type: "string",
62
+ description: "Not normally needed. Consent is granted once, for the whole session, by browser_start_session, " +
63
+ "so this call performs its action directly. The field is declared because the handshake reads it " +
64
+ "for every action-class tool.",
65
+ },
66
+ },
67
+ required: ["sessionId", "ref", "text"],
68
+ additionalProperties: false,
69
+ };
70
+ async function run(args, ctx) {
71
+ const sessionId = requiredString(args, "sessionId");
72
+ const ref = requiredString(args, "ref");
73
+ const text = requiredString(args, "text");
74
+ const found = await useSession(ctx.session.browserSessions(), sessionId);
75
+ if (!found.ok) {
76
+ return browserAnswer({
77
+ ctx,
78
+ state: found.dead.state,
79
+ headline: found.dead.headline,
80
+ notes: found.dead.notes,
81
+ data: { sessionId, ref, url: "", evidence: found.dead.evidence, graph: NO_GRAPH_WRITE },
82
+ });
83
+ }
84
+ const { entry } = found;
85
+ // Consent and safetyLevel are independent and both must pass. Consent was
86
+ // granted at session start; this is the other half.
87
+ const refused = mutationRefusal(entry);
88
+ if (refused !== null) {
89
+ return browserAnswer({
90
+ ctx,
91
+ state: "refused",
92
+ headline: `${entry.profile?.name ?? "This environment"} is read-only.`,
93
+ notes: [refused],
94
+ data: { sessionId, ref, url: entry.page.currentUrl(), evidence: emptyEvidence(), graph: NO_GRAPH_WRITE },
95
+ });
96
+ }
97
+ try {
98
+ ctx.progress(`Filling`);
99
+ await entry.page.fill(ref, text);
100
+ }
101
+ catch (error) {
102
+ if (!(error instanceof UnknownElementRefError))
103
+ throw error;
104
+ return browserAnswer({
105
+ ctx,
106
+ state: "refused",
107
+ headline: `"${ref}" is not an element in this session's current snapshot.`,
108
+ notes: [error.message],
109
+ data: { sessionId, ref, url: entry.page.currentUrl(), evidence: emptyEvidence(), graph: NO_GRAPH_WRITE },
110
+ });
111
+ }
112
+ const observed = await observeAndCorrelate(ctx, entry);
113
+ return browserAnswer({
114
+ ctx,
115
+ headline: `Filling on ${entry.page.currentUrl()}.`,
116
+ notes: observed.notes,
117
+ data: { sessionId, ref, url: entry.page.currentUrl(), evidence: observed.evidence, graph: observed.graph },
118
+ });
119
+ }
120
+ export const browserFillTool = {
121
+ name: "browser_fill",
122
+ title: "set a field's value directly",
123
+ description: "Set a field's value in an open browser session, replacing what is there. Fires no key events at all: " +
124
+ "the value is set directly, so an autocomplete, per-character validation or debounced search attached " +
125
+ "to that field will not run. Use browser_type when the page reacts to typing. Refused when the " +
126
+ "session's environment profile declares safetyLevel \"readOnly\".",
127
+ class: "action",
128
+ tier: "evidence",
129
+ version: "1.0.0",
130
+ inputSchema: SCHEMA,
131
+ run,
132
+ // Undefined, always: the session this runs in was confirmed at
133
+ // browser_start_session, and a verb that could describe — and therefore
134
+ // mint — its own consent would be a gate that exists only for callers who
135
+ // were not going to abuse it.
136
+ describeAction: () => undefined,
137
+ };
138
+ //# sourceMappingURL=browser-fill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-fill.js","sourceRoot":"","sources":["../../src/tools/browser-fill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `browser_navigate` — move an open session to another page.
3
+ *
4
+ * ## The one rule that is not obvious
5
+ *
6
+ * Navigation is where a session-scoped consent could quietly become a
7
+ * general-purpose browser. The grant names an origin; this tool is the only
8
+ * thing that can leave it, so this is where the bound is enforced. A
9
+ * cross-origin navigate **refuses** — it does not ask again, because a second
10
+ * prompt at this point is exactly the reflexive confirmation the consent
11
+ * ruling rejected.
12
+ *
13
+ * ## Class
14
+ *
15
+ * `stateful-read`. It changes what a later call sees, so it is not a pure
16
+ * read; but the fact a developer approves — *drive a browser against this
17
+ * application* — was stated and confirmed at `browser_start_session`, and
18
+ * re-gating each hop would make the session grant meaningless.
19
+ */
20
+ import { type ToolDefinition } from "./kit.ts";
21
+ import type { BrowserPageData } from "./browser-snapshot.ts";
22
+ /**
23
+ * Deliberately the same shape `browser_snapshot` returns.
24
+ *
25
+ * A navigate answers the question a snapshot answers — *what is on the page
26
+ * now* — so returning a different shape would make a caller write two readers
27
+ * for one fact. It is an alias rather than a re-declaration so the two cannot
28
+ * drift.
29
+ */
30
+ export type BrowserNavigateData = BrowserPageData;
31
+ export declare const browserNavigateTool: ToolDefinition;
32
+ //# sourceMappingURL=browser-navigate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-navigate.d.ts","sourceRoot":"","sources":["../../src/tools/browser-navigate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAEjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,eAAe,CAAC;AA8ElD,eAAO,MAAM,mBAAmB,EAAE,cAYjC,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `browser_navigate` — move an open session to another page.
3
+ *
4
+ * ## The one rule that is not obvious
5
+ *
6
+ * Navigation is where a session-scoped consent could quietly become a
7
+ * general-purpose browser. The grant names an origin; this tool is the only
8
+ * thing that can leave it, so this is where the bound is enforced. A
9
+ * cross-origin navigate **refuses** — it does not ask again, because a second
10
+ * prompt at this point is exactly the reflexive confirmation the consent
11
+ * ruling rejected.
12
+ *
13
+ * ## Class
14
+ *
15
+ * `stateful-read`. It changes what a later call sees, so it is not a pure
16
+ * read; but the fact a developer approves — *drive a browser against this
17
+ * application* — was stated and confirmed at `browser_start_session`, and
18
+ * re-gating each hop would make the session grant meaningless.
19
+ */
20
+ import { browserAnswer, emptyEvidence, observeAndCorrelate, useSession, withinConsent } from "../browser/tool-support.js";
21
+ import { NO_GRAPH_WRITE } from "../browser/graph-write.js";
22
+ import { requiredString } from "./kit.js";
23
+ const SCHEMA = {
24
+ type: "object",
25
+ properties: {
26
+ sessionId: {
27
+ type: "string",
28
+ description: "REQUIRED. The id returned by browser_start_session. Sessions belong to this connection and do not survive a reconnect.",
29
+ },
30
+ url: {
31
+ type: "string",
32
+ description: "REQUIRED. Where to navigate. Must be on the same origin the session was started against — a URL " +
33
+ "on any other origin is refused rather than confirmed again, because consent was granted for one " +
34
+ "application, not for the browser generally.",
35
+ },
36
+ },
37
+ required: ["sessionId", "url"],
38
+ additionalProperties: false,
39
+ };
40
+ function blank(url, evidence) {
41
+ return { sessionId: "", url, title: "", elements: [], totalElements: 0, evidence, graph: NO_GRAPH_WRITE };
42
+ }
43
+ async function run(args, ctx) {
44
+ const sessionId = requiredString(args, "sessionId");
45
+ const url = requiredString(args, "url");
46
+ const found = await useSession(ctx.session.browserSessions(), sessionId);
47
+ if (!found.ok) {
48
+ return browserAnswer({
49
+ ctx,
50
+ state: found.dead.state,
51
+ headline: found.dead.headline,
52
+ notes: found.dead.notes,
53
+ data: blank(url, found.dead.evidence),
54
+ });
55
+ }
56
+ const { entry } = found;
57
+ if (!withinConsent(entry, url)) {
58
+ return browserAnswer({
59
+ ctx,
60
+ state: "refused",
61
+ headline: `${url} is outside this session's consented origin.`,
62
+ notes: [
63
+ `This session was started against ${entry.consentedOrigin} and consent is bound to that origin. ` +
64
+ `Navigating to ${url} would take the browser somewhere the developer did not approve, so it is ` +
65
+ "refused rather than confirmed again — asking a second time here is how a gate becomes a habit. " +
66
+ "Start a new session if you genuinely mean to drive that origin.",
67
+ ],
68
+ data: blank(url, emptyEvidence()),
69
+ });
70
+ }
71
+ ctx.progress(`Navigating to ${url}`);
72
+ await entry.page.navigate(url);
73
+ const snapshot = await entry.page.snapshot();
74
+ const observed = await observeAndCorrelate(ctx, entry);
75
+ const evidence = observed.evidence;
76
+ return browserAnswer({
77
+ ctx,
78
+ headline: `${snapshot.url} — ${snapshot.title || "untitled"} (${snapshot.totalElements} elements).`,
79
+ notes: observed.notes,
80
+ data: {
81
+ sessionId: entry.sessionId,
82
+ url: snapshot.url,
83
+ title: snapshot.title,
84
+ elements: snapshot.elements,
85
+ totalElements: snapshot.totalElements,
86
+ evidence,
87
+ graph: observed.graph,
88
+ },
89
+ });
90
+ }
91
+ export const browserNavigateTool = {
92
+ name: "browser_navigate",
93
+ title: "Navigate an open browser session",
94
+ description: "Move an open browser session to another page on the same application, and return what is there plus " +
95
+ "anything the page produced on the way — console errors and failed requests, raw and untriaged. " +
96
+ "Refuses to leave the origin the session was started against.",
97
+ class: "stateful-read",
98
+ tier: "evidence",
99
+ version: "1.0.0",
100
+ inputSchema: SCHEMA,
101
+ run,
102
+ };
103
+ //# sourceMappingURL=browser-navigate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-navigate.js","sourceRoot":"","sources":["../../src/tools/browser-navigate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `browser_run_scenario` — replay a saved scenario in one batch call.
3
+ *
4
+ * ## One reply, not N
5
+ *
6
+ * The point of this tool over "call browser_click six times" is that a
7
+ * scenario is a *check*, and a check has one outcome. Six replies make the
8
+ * calling agent reassemble a verdict from fragments and give it six chances to
9
+ * stop early with a partial picture; one reply carries the whole run — every
10
+ * step, every assertion, and one aggregated evidence summary drained a step at
11
+ * a time.
12
+ *
13
+ * ## Class
14
+ *
15
+ * `action`, on the same test as `browser_click`: it changes a running
16
+ * application, and declaring it anything softer would be the lie. Like the
17
+ * verbs, it mints nothing — consent was granted once at
18
+ * `browser_start_session` and bound to that origin, and a scenario cannot
19
+ * widen it: a stored `navigate` step that would leave the consented origin
20
+ * stops the replay rather than following the file.
21
+ *
22
+ * ## What it will not do
23
+ *
24
+ * Report a verdict about the application beyond what the scenario's own author
25
+ * wrote down. `DEC-381` keeps classification
26
+ * in the desktop pipeline, so a console error observed during a replay is
27
+ * reported and is not blocking — while a console error the author *asserted*
28
+ * would not happen is a failed assertion and is. Same evidence, different
29
+ * status, and the difference is entirely whether a person claimed something
30
+ * about it.
31
+ */
32
+ import { type BrowserGraphWrite } from "../browser/graph-write.ts";
33
+ import { type ScenarioAssertionResult, type ScenarioCounts, type ScenarioStepResult } from "../browser/scenario-runner.ts";
34
+ import type { BrowserEvidenceReport } from "../browser/evidence.ts";
35
+ import { type ToolDefinition } from "./kit.ts";
36
+ export interface BrowserRunScenarioData {
37
+ readonly sessionId: string;
38
+ readonly scenario: string;
39
+ /** `passed` · `failed` · `unresolved` · `unverified` — never `passed` for a scenario that asserted nothing. */
40
+ readonly outcome: "passed" | "failed" | "unresolved" | "unverified" | "notRun";
41
+ /** Three counts that are never summed, plus how many assertions held. */
42
+ readonly counts: ScenarioCounts;
43
+ /** Failed assertions alone. An unresolvable step is not a defect claim, so it does not join this. */
44
+ readonly blockingTotal: number;
45
+ /** 0 clean · 1 blocking findings · 2 the check could not run. `descry check`'s existing vocabulary. */
46
+ readonly exitCode: 0 | 1 | 2;
47
+ readonly steps: readonly ScenarioStepResult[];
48
+ readonly assertions: readonly ScenarioAssertionResult[];
49
+ /** One summary for the whole batch, drained a step at a time. */
50
+ readonly evidence: BrowserEvidenceReport;
51
+ readonly graph: BrowserGraphWrite;
52
+ }
53
+ export declare const browserRunScenarioTool: ToolDefinition;
54
+ //# sourceMappingURL=browser-run-scenario.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-run-scenario.d.ts","sourceRoot":"","sources":["../../src/tools/browser-run-scenario.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAA2B,KAAK,uBAAuB,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEpJ,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGjF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+GAA+G;IAC/G,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,YAAY,GAAG,QAAQ,CAAC;IAC/E,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,qGAAqG;IACrG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,uGAAuG;IACvG,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACxD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;CACnC;AA2LD,eAAO,MAAM,sBAAsB,EAAE,cAoBpC,CAAC"}
@@ -0,0 +1,228 @@
1
+ /**
2
+ * `browser_run_scenario` — replay a saved scenario in one batch call.
3
+ *
4
+ * ## One reply, not N
5
+ *
6
+ * The point of this tool over "call browser_click six times" is that a
7
+ * scenario is a *check*, and a check has one outcome. Six replies make the
8
+ * calling agent reassemble a verdict from fragments and give it six chances to
9
+ * stop early with a partial picture; one reply carries the whole run — every
10
+ * step, every assertion, and one aggregated evidence summary drained a step at
11
+ * a time.
12
+ *
13
+ * ## Class
14
+ *
15
+ * `action`, on the same test as `browser_click`: it changes a running
16
+ * application, and declaring it anything softer would be the lie. Like the
17
+ * verbs, it mints nothing — consent was granted once at
18
+ * `browser_start_session` and bound to that origin, and a scenario cannot
19
+ * widen it: a stored `navigate` step that would leave the consented origin
20
+ * stops the replay rather than following the file.
21
+ *
22
+ * ## What it will not do
23
+ *
24
+ * Report a verdict about the application beyond what the scenario's own author
25
+ * wrote down. `DEC-381` keeps classification
26
+ * in the desktop pipeline, so a console error observed during a replay is
27
+ * reported and is not blocking — while a console error the author *asserted*
28
+ * would not happen is a failed assertion and is. Same evidence, different
29
+ * status, and the difference is entirely whether a person claimed something
30
+ * about it.
31
+ */
32
+ import { readScenario } from "../scenarios/index.js";
33
+ import { browserAnswer, emptyEvidence, mutationRefusal, useSession, withinConsent } from "../browser/tool-support.js";
34
+ import { NO_GRAPH_WRITE } from "../browser/graph-write.js";
35
+ import { mutates, replayScenario } from "../browser/scenario-runner.js";
36
+ import { scenarioPath } from "../scenarios/index.js";
37
+ import { requiredString } from "./kit.js";
38
+ const SCHEMA = {
39
+ type: "object",
40
+ properties: {
41
+ sessionId: {
42
+ type: "string",
43
+ description: "REQUIRED. The id returned by browser_start_session. The replay runs in that session, under the consent it was granted.",
44
+ },
45
+ name: {
46
+ type: "string",
47
+ description: "REQUIRED. The scenario's name, without the file extension — a scenario saved as " +
48
+ "descry/scenarios/checkout.scenario.json is named \"checkout\". Saved scenarios are committed files; " +
49
+ "browser_save_scenario writes them.",
50
+ },
51
+ confirmToken: {
52
+ type: "string",
53
+ description: "Not normally needed. Consent is granted once, for the whole session, by browser_start_session, so " +
54
+ "this call replays directly. The field is declared because the handshake reads it for every " +
55
+ "action-class tool.",
56
+ },
57
+ },
58
+ required: ["sessionId", "name"],
59
+ additionalProperties: false,
60
+ };
61
+ function empty(sessionId, name) {
62
+ return {
63
+ sessionId,
64
+ scenario: name,
65
+ outcome: "notRun",
66
+ counts: { passed: 0, failed: 0, unresolved: 0, unverified: 0 },
67
+ blockingTotal: 0,
68
+ // 2 rather than 0: this is "the check could not run", and a refusal that
69
+ // reported clean would let a scenario nobody could replay keep claiming
70
+ // coverage.
71
+ exitCode: 2,
72
+ steps: [],
73
+ assertions: [],
74
+ evidence: emptyEvidence(),
75
+ graph: NO_GRAPH_WRITE,
76
+ };
77
+ }
78
+ async function run(args, ctx) {
79
+ const sessionId = requiredString(args, "sessionId");
80
+ const name = requiredString(args, "name");
81
+ const stored = await readScenario(ctx.session.repoPath, name);
82
+ if (!stored.valid) {
83
+ return browserAnswer({
84
+ ctx,
85
+ state: "refused",
86
+ headline: `Scenario "${name}" cannot be replayed.`,
87
+ notes: [
88
+ ...stored.problems,
89
+ `Scenarios live in ${scenarioPath(ctx.session.repoPath, name)} and are committed like source. ` +
90
+ "browser_save_scenario writes one from a session you have explored.",
91
+ ],
92
+ data: empty(sessionId, name),
93
+ });
94
+ }
95
+ const scenario = stored.scenario;
96
+ const found = await useSession(ctx.session.browserSessions(), sessionId);
97
+ if (!found.ok) {
98
+ return browserAnswer({
99
+ ctx,
100
+ state: found.dead.state,
101
+ headline: found.dead.headline,
102
+ notes: found.dead.notes,
103
+ data: { ...empty(sessionId, name), evidence: found.dead.evidence },
104
+ });
105
+ }
106
+ const { entry } = found;
107
+ // Consent is bound to one origin and a stored file cannot widen it. Checked
108
+ // before anything runs, because a replay that got three steps in and then
109
+ // refused would have already changed the application.
110
+ if (!withinConsent(entry, scenario.targetUrl)) {
111
+ return browserAnswer({
112
+ ctx,
113
+ state: "refused",
114
+ headline: `Scenario "${name}" targets another origin.`,
115
+ notes: [
116
+ `It opens ${scenario.targetUrl}, and this session's consent covers ${entry.consentedOrigin} only. ` +
117
+ "Consent is granted per session and bound to the origin it was granted for, so a saved file cannot " +
118
+ "extend it. Start a session against that origin to replay this scenario.",
119
+ ],
120
+ data: empty(sessionId, name),
121
+ });
122
+ }
123
+ const refusedForSafety = mutationRefusal(entry);
124
+ if (refusedForSafety !== null && mutates(scenario)) {
125
+ return browserAnswer({
126
+ ctx,
127
+ state: "refused",
128
+ headline: `${entry.profile?.name ?? "This environment"} is read-only.`,
129
+ notes: [
130
+ refusedForSafety,
131
+ `Scenario "${name}" clicks, types or fills, so replaying it would change the application. A ` +
132
+ "navigate-only scenario would still run here.",
133
+ ],
134
+ data: empty(sessionId, name),
135
+ });
136
+ }
137
+ // Start where the scenario says it starts, rather than wherever the session
138
+ // happened to be left. A replay that began from an arbitrary page is not a
139
+ // repeat of the run that was saved.
140
+ if (entry.page.currentUrl() !== scenario.targetUrl) {
141
+ await entry.page.navigate(scenario.targetUrl);
142
+ }
143
+ const replay = await replayScenario(ctx, ctx.session.browserSessions(), sessionId, scenario);
144
+ const notes = [
145
+ ...(replay.sessionEnded === null ? [] : replay.sessionEnded.notes),
146
+ ...replay.notes,
147
+ ...outcomeNotes(replay.outcome, replay.counts, name),
148
+ ...replay.steps.filter((s) => s.resolution !== null).map((s) => s.resolution.message),
149
+ ...replay.assertions.filter((a) => a.status === "failed" || a.status === "unevaluable").map((a) => a.detail),
150
+ ];
151
+ return browserAnswer({
152
+ ctx,
153
+ ...(replay.sessionEnded === null ? {} : { state: replay.sessionEnded.state }),
154
+ headline: `Scenario "${name}": ${replay.outcome} — ${replay.counts.passed} assertion(s) held, ` +
155
+ `${replay.counts.failed} failed, ${replay.counts.unresolved} could not run.`,
156
+ notes,
157
+ data: {
158
+ sessionId,
159
+ scenario: name,
160
+ outcome: replay.outcome,
161
+ counts: replay.counts,
162
+ blockingTotal: replay.blockingTotal,
163
+ exitCode: replay.exitCode,
164
+ steps: replay.steps,
165
+ assertions: replay.assertions,
166
+ evidence: replay.evidence,
167
+ graph: replay.graph,
168
+ },
169
+ });
170
+ }
171
+ /**
172
+ * The disclosure the three counts exist for.
173
+ *
174
+ * Emitted per outcome rather than always, because a note that appears on every
175
+ * reply is a note nobody reads by the third one — the same reasoning
176
+ * `evidenceNotes` uses for the triage disclaimer.
177
+ */
178
+ function outcomeNotes(outcome, counts, name) {
179
+ switch (outcome) {
180
+ case "unverified":
181
+ return [
182
+ `Scenario "${name}" stores no assertions, so this replay verified nothing. Its steps still resolved ` +
183
+ "and still ran — real information — but that is not the same statement as the application behaving, " +
184
+ "and it is deliberately never reported as passed.",
185
+ ];
186
+ case "unresolved":
187
+ return [
188
+ `${counts.unresolved} step or assertion could not run, so this replay neither passed nor failed. ` +
189
+ "Descry cannot tell a deliberate rename from a vanished element — both look identical from here — " +
190
+ "so it claims neither. The remedy is in the message below: re-save the step if the change was " +
191
+ "intended, or fix the application if it was not.",
192
+ ];
193
+ case "failed":
194
+ return [
195
+ `${counts.failed} assertion(s) the scenario's author wrote down did not hold. Only those count as ` +
196
+ "blocking; console errors and failed requests observed during the replay are reported below and are " +
197
+ "not, because they are frequently true of a working page." +
198
+ (counts.unresolved > 0
199
+ ? ` ${counts.unresolved} check(s) also could not run, counted separately and never added to the ` +
200
+ "blocking total."
201
+ : ""),
202
+ ];
203
+ case "passed":
204
+ case "notRun":
205
+ return [];
206
+ }
207
+ }
208
+ export const browserRunScenarioTool = {
209
+ name: "browser_run_scenario",
210
+ title: "Replay a saved scenario",
211
+ description: "Replay a saved scenario — a named step sequence with the assertions its author wrote down — in one " +
212
+ "call, and return a single evidence summary rather than one reply per step. Each step re-resolves its " +
213
+ "element against a fresh page: exactly one match or the replay stops and says which of role, name and " +
214
+ "selector disagreed and what the page holds now. Reports three counts that are never added together — " +
215
+ "failed assertions, checks that could not run, and whether the scenario asserted anything at all. A " +
216
+ "scenario with no assertions replays as unverified, never as passed.",
217
+ class: "action",
218
+ tier: "evidence",
219
+ version: "1.0.0",
220
+ inputSchema: SCHEMA,
221
+ run,
222
+ // Undefined, always — the same property the verbs have. The session was
223
+ // confirmed at browser_start_session; a replay that could mint its own
224
+ // consent would be a gate that exists only for callers who were not going
225
+ // to abuse it.
226
+ describeAction: () => undefined,
227
+ };
228
+ //# sourceMappingURL=browser-run-scenario.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-run-scenario.js","sourceRoot":"","sources":["../../src/tools/browser-run-scenario.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `browser_save_scenario` — an explored session becomes a durable, repeatable
3
+ * scenario.
4
+ *
5
+ * ## Everything goes out through `writeScenario`
6
+ *
7
+ * There is deliberately no private path to disk here. `writeScenario`
8
+ * validates on the way *out* as well as in, and that is not redundant: a
9
+ * scenario is written once and read on every push afterwards, so a fault saved
10
+ * today surfaces during a check somebody is waiting on, long after the session
11
+ * that could have explained it is gone. A tool that serialized its own JSON
12
+ * would be a second writer with a first writer's rules, and the second one is
13
+ * the one that ships a file nothing can read.
14
+ *
15
+ * ## A step carries no ref, and this schema offers no way to send one
16
+ *
17
+ * The ref refusal is enforced twice on purpose. The format module rejects a
18
+ * step carrying a `ref` **by name**, with the reason in the message; this
19
+ * schema does not declare the field at all. A caller working from a
20
+ * `browser_snapshot` reply has the three durable fields already — every
21
+ * snapshot element carries `role`, `name` and `selector` — so the natural
22
+ * thing to copy is the right thing to store.
23
+ *
24
+ * ## What the session supplies
25
+ *
26
+ * The consented origin (a scenario may not be saved pointing somewhere this
27
+ * session was never allowed to drive), the URL to start from, and the
28
+ * provenance pair. The steps are the caller's: this server has no exploration
29
+ * planner and does not pretend to have recorded intent it never saw.
30
+ */
31
+ import { type ToolDefinition } from "./kit.ts";
32
+ export interface BrowserSaveScenarioData {
33
+ readonly sessionId: string;
34
+ readonly name: string;
35
+ /** Where it was written, or "" when nothing was written. */
36
+ readonly path: string;
37
+ readonly targetUrl: string;
38
+ readonly stepCount: number;
39
+ readonly assertionCount: number;
40
+ readonly savedAt: string;
41
+ readonly savedAtCommit: string;
42
+ }
43
+ export declare const browserSaveScenarioTool: ToolDefinition;
44
+ //# sourceMappingURL=browser-save-scenario.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-save-scenario.d.ts","sourceRoot":"","sources":["../../src/tools/browser-save-scenario.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,OAAO,EAAoE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGjH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AA2QD,eAAO,MAAM,uBAAuB,EAAE,cAoBrC,CAAC"}