@descryy/mcp 0.2.0 → 0.4.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 (160) hide show
  1. package/dist/bin/descry-mcp.js +6 -1
  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/index.d.ts +11 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +10 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/render.d.ts +112 -10
  52. package/dist/render.d.ts.map +1 -1
  53. package/dist/render.js +79 -13
  54. package/dist/render.js.map +1 -1
  55. package/dist/scenarios/index.d.ts +4 -0
  56. package/dist/scenarios/index.d.ts.map +1 -0
  57. package/dist/scenarios/index.js +4 -0
  58. package/dist/scenarios/index.js.map +1 -0
  59. package/dist/scenarios/parse.d.ts +32 -0
  60. package/dist/scenarios/parse.d.ts.map +1 -0
  61. package/dist/scenarios/parse.js +199 -0
  62. package/dist/scenarios/parse.js.map +1 -0
  63. package/dist/scenarios/scenario.d.ts +115 -0
  64. package/dist/scenarios/scenario.d.ts.map +1 -0
  65. package/dist/scenarios/scenario.js +41 -0
  66. package/dist/scenarios/scenario.js.map +1 -0
  67. package/dist/scenarios/storage.d.ts +51 -0
  68. package/dist/scenarios/storage.d.ts.map +1 -0
  69. package/dist/scenarios/storage.js +93 -0
  70. package/dist/scenarios/storage.js.map +1 -0
  71. package/dist/server.d.ts.map +1 -1
  72. package/dist/server.js +6 -3
  73. package/dist/server.js.map +1 -1
  74. package/dist/session.d.ts +68 -0
  75. package/dist/session.d.ts.map +1 -1
  76. package/dist/session.js +89 -0
  77. package/dist/session.js.map +1 -1
  78. package/dist/tools/analyze.d.ts +41 -1
  79. package/dist/tools/analyze.d.ts.map +1 -1
  80. package/dist/tools/analyze.js +81 -8
  81. package/dist/tools/analyze.js.map +1 -1
  82. package/dist/tools/browser-click.d.ts +36 -0
  83. package/dist/tools/browser-click.d.ts.map +1 -0
  84. package/dist/tools/browser-click.js +117 -0
  85. package/dist/tools/browser-click.js.map +1 -0
  86. package/dist/tools/browser-close-session.d.ts +24 -0
  87. package/dist/tools/browser-close-session.d.ts.map +1 -0
  88. package/dist/tools/browser-close-session.js +63 -0
  89. package/dist/tools/browser-close-session.js.map +1 -0
  90. package/dist/tools/browser-fill.d.ts +49 -0
  91. package/dist/tools/browser-fill.d.ts.map +1 -0
  92. package/dist/tools/browser-fill.js +138 -0
  93. package/dist/tools/browser-fill.js.map +1 -0
  94. package/dist/tools/browser-navigate.d.ts +32 -0
  95. package/dist/tools/browser-navigate.d.ts.map +1 -0
  96. package/dist/tools/browser-navigate.js +103 -0
  97. package/dist/tools/browser-navigate.js.map +1 -0
  98. package/dist/tools/browser-run-scenario.d.ts +54 -0
  99. package/dist/tools/browser-run-scenario.d.ts.map +1 -0
  100. package/dist/tools/browser-run-scenario.js +228 -0
  101. package/dist/tools/browser-run-scenario.js.map +1 -0
  102. package/dist/tools/browser-save-scenario.d.ts +44 -0
  103. package/dist/tools/browser-save-scenario.d.ts.map +1 -0
  104. package/dist/tools/browser-save-scenario.js +298 -0
  105. package/dist/tools/browser-save-scenario.js.map +1 -0
  106. package/dist/tools/browser-snapshot.d.ts +60 -0
  107. package/dist/tools/browser-snapshot.d.ts.map +1 -0
  108. package/dist/tools/browser-snapshot.js +139 -0
  109. package/dist/tools/browser-snapshot.js.map +1 -0
  110. package/dist/tools/browser-start-session.d.ts +43 -0
  111. package/dist/tools/browser-start-session.d.ts.map +1 -0
  112. package/dist/tools/browser-start-session.js +272 -0
  113. package/dist/tools/browser-start-session.js.map +1 -0
  114. package/dist/tools/browser-type.d.ts +48 -0
  115. package/dist/tools/browser-type.d.ts.map +1 -0
  116. package/dist/tools/browser-type.js +136 -0
  117. package/dist/tools/browser-type.js.map +1 -0
  118. package/dist/tools/contracts.d.ts +15 -0
  119. package/dist/tools/contracts.d.ts.map +1 -1
  120. package/dist/tools/contracts.js +14 -3
  121. package/dist/tools/contracts.js.map +1 -1
  122. package/dist/tools/git-diff.d.ts.map +1 -1
  123. package/dist/tools/git-diff.js +85 -6
  124. package/dist/tools/git-diff.js.map +1 -1
  125. package/dist/tools/impact.d.ts +9 -0
  126. package/dist/tools/impact.d.ts.map +1 -1
  127. package/dist/tools/impact.js +3 -3
  128. package/dist/tools/impact.js.map +1 -1
  129. package/dist/tools/index.d.ts +9 -0
  130. package/dist/tools/index.d.ts.map +1 -1
  131. package/dist/tools/index.js +27 -0
  132. package/dist/tools/index.js.map +1 -1
  133. package/dist/tools/kit.d.ts +10 -1
  134. package/dist/tools/kit.d.ts.map +1 -1
  135. package/dist/tools/kit.js +2 -0
  136. package/dist/tools/kit.js.map +1 -1
  137. package/dist/tools/observe-runtime.d.ts +113 -19
  138. package/dist/tools/observe-runtime.d.ts.map +1 -1
  139. package/dist/tools/observe-runtime.js +324 -53
  140. package/dist/tools/observe-runtime.js.map +1 -1
  141. package/dist/tools/observe-tests.d.ts +125 -0
  142. package/dist/tools/observe-tests.d.ts.map +1 -0
  143. package/dist/tools/observe-tests.js +313 -0
  144. package/dist/tools/observe-tests.js.map +1 -0
  145. package/dist/tools/pr-analysis.d.ts +11 -0
  146. package/dist/tools/pr-analysis.d.ts.map +1 -1
  147. package/dist/tools/pr-analysis.js +3 -3
  148. package/dist/tools/pr-analysis.js.map +1 -1
  149. package/dist/tools/pre-push.d.ts +77 -0
  150. package/dist/tools/pre-push.d.ts.map +1 -0
  151. package/dist/tools/pre-push.js +250 -0
  152. package/dist/tools/pre-push.js.map +1 -0
  153. package/dist/tools/questions.d.ts.map +1 -1
  154. package/dist/tools/questions.js +41 -16
  155. package/dist/tools/questions.js.map +1 -1
  156. package/dist/tools/verification-status.d.ts +22 -0
  157. package/dist/tools/verification-status.d.ts.map +1 -1
  158. package/dist/tools/verification-status.js +51 -5
  159. package/dist/tools/verification-status.js.map +1 -1
  160. package/package.json +14 -13
@@ -0,0 +1,117 @@
1
+ /**
2
+ * `browser_click` — click an element the agent has seen.
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
+ import { browserAnswer, emptyEvidence, mutationRefusal, observeAndCorrelate, useSession } from "../browser/tool-support.js";
25
+ import { NO_GRAPH_WRITE } from "../browser/graph-write.js";
26
+ import { UnknownElementRefError } from "../browser/driver.js";
27
+ import { requiredString } from "./kit.js";
28
+ const SCHEMA = {
29
+ type: "object",
30
+ properties: {
31
+ sessionId: {
32
+ type: "string",
33
+ description: "REQUIRED. The id returned by browser_start_session.",
34
+ },
35
+ ref: {
36
+ type: "string",
37
+ description: "REQUIRED. An element ref from a browser_snapshot reply. Refs are issued per snapshot and are " +
38
+ "invalidated by navigation — a stale ref is refused rather than resolved against whatever now " +
39
+ "matches it, so take a fresh snapshot after the page changes.",
40
+ },
41
+ confirmToken: {
42
+ type: "string",
43
+ description: "Not normally needed. Consent is granted once, for the whole session, by browser_start_session, " +
44
+ "so this call performs its action directly. The field is declared because the handshake reads it " +
45
+ "for every action-class tool.",
46
+ },
47
+ },
48
+ required: ["sessionId", "ref"],
49
+ additionalProperties: false,
50
+ };
51
+ async function run(args, ctx) {
52
+ const sessionId = requiredString(args, "sessionId");
53
+ const ref = requiredString(args, "ref");
54
+ const found = await useSession(ctx.session.browserSessions(), sessionId);
55
+ if (!found.ok) {
56
+ return browserAnswer({
57
+ ctx,
58
+ state: found.dead.state,
59
+ headline: found.dead.headline,
60
+ notes: found.dead.notes,
61
+ data: { sessionId, ref, url: "", evidence: found.dead.evidence, graph: NO_GRAPH_WRITE },
62
+ });
63
+ }
64
+ const { entry } = found;
65
+ // Consent and safetyLevel are independent and both must pass. Consent was
66
+ // granted at session start; this is the other half.
67
+ const refused = mutationRefusal(entry);
68
+ if (refused !== null) {
69
+ return browserAnswer({
70
+ ctx,
71
+ state: "refused",
72
+ headline: `${entry.profile?.name ?? "This environment"} is read-only.`,
73
+ notes: [refused],
74
+ data: { sessionId, ref, url: entry.page.currentUrl(), evidence: emptyEvidence(), graph: NO_GRAPH_WRITE },
75
+ });
76
+ }
77
+ try {
78
+ ctx.progress(`Clicking`);
79
+ await entry.page.click(ref);
80
+ }
81
+ catch (error) {
82
+ if (!(error instanceof UnknownElementRefError))
83
+ throw error;
84
+ return browserAnswer({
85
+ ctx,
86
+ state: "refused",
87
+ headline: `"${ref}" is not an element in this session's current snapshot.`,
88
+ notes: [error.message],
89
+ data: { sessionId, ref, url: entry.page.currentUrl(), evidence: emptyEvidence(), graph: NO_GRAPH_WRITE },
90
+ });
91
+ }
92
+ const observed = await observeAndCorrelate(ctx, entry);
93
+ return browserAnswer({
94
+ ctx,
95
+ headline: `Clicking on ${entry.page.currentUrl()}.`,
96
+ notes: observed.notes,
97
+ data: { sessionId, ref, url: entry.page.currentUrl(), evidence: observed.evidence, graph: observed.graph },
98
+ });
99
+ }
100
+ export const browserClickTool = {
101
+ name: "browser_click",
102
+ title: "click an element the agent has seen",
103
+ description: "Click an element in an open browser session, named by a ref from a browser_snapshot reply. Returns " +
104
+ "whatever the page produced as a result — console errors and failed requests, raw and untriaged. " +
105
+ "Refused when the session's environment profile declares safetyLevel \"readOnly\".",
106
+ class: "action",
107
+ tier: "evidence",
108
+ version: "1.0.0",
109
+ inputSchema: SCHEMA,
110
+ run,
111
+ // Undefined, always: the session this runs in was confirmed at
112
+ // browser_start_session, and a verb that could describe — and therefore
113
+ // mint — its own consent would be a gate that exists only for callers who
114
+ // were not going to abuse it.
115
+ describeAction: () => undefined,
116
+ };
117
+ //# sourceMappingURL=browser-click.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-click.js","sourceRoot":"","sources":["../../src/tools/browser-click.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `browser_close_session` — tear down and hand back what is left.
3
+ *
4
+ * Returns the final flush rather than an acknowledgement. Evidence captured
5
+ * and then dropped is worse than evidence never captured, because nothing
6
+ * discloses it — so the last thing a session does is report what it was
7
+ * holding.
8
+ *
9
+ * Closing a session that is already gone is **not an error**. An agent whose
10
+ * session timed out while it was thinking should get "here is why it went,
11
+ * and here is what it saw", not a failure it has to handle. That is the whole
12
+ * reason the registry keeps a record of a reaped session rather than
13
+ * forgetting the id.
14
+ */
15
+ import type { BrowserEvidenceReport } from "../browser/evidence.ts";
16
+ import { type ToolDefinition } from "./kit.ts";
17
+ export interface BrowserCloseSessionData {
18
+ readonly sessionId: string;
19
+ /** How the session ended — closed here, or already gone and why. */
20
+ readonly endedBy: "thisCall" | "idleTimeout" | "clientDisconnect" | "crashed" | "alreadyClosed";
21
+ readonly evidence: BrowserEvidenceReport;
22
+ }
23
+ export declare const browserCloseSessionTool: ToolDefinition;
24
+ //# sourceMappingURL=browser-close-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-close-session.d.ts","sourceRoot":"","sources":["../../src/tools/browser-close-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGjF,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,aAAa,GAAG,kBAAkB,GAAG,SAAS,GAAG,eAAe,CAAC;IAChG,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;CAC1C;AA+CD,eAAO,MAAM,uBAAuB,EAAE,cAWrC,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `browser_close_session` — tear down and hand back what is left.
3
+ *
4
+ * Returns the final flush rather than an acknowledgement. Evidence captured
5
+ * and then dropped is worse than evidence never captured, because nothing
6
+ * discloses it — so the last thing a session does is report what it was
7
+ * holding.
8
+ *
9
+ * Closing a session that is already gone is **not an error**. An agent whose
10
+ * session timed out while it was thinking should get "here is why it went,
11
+ * and here is what it saw", not a failure it has to handle. That is the whole
12
+ * reason the registry keeps a record of a reaped session rather than
13
+ * forgetting the id.
14
+ */
15
+ import { evidenceNotes } from "../browser/evidence.js";
16
+ import { browserAnswer, reportOf } from "../browser/tool-support.js";
17
+ import { requiredString } from "./kit.js";
18
+ const SCHEMA = {
19
+ type: "object",
20
+ properties: {
21
+ sessionId: {
22
+ type: "string",
23
+ description: "REQUIRED. The id returned by browser_start_session. Closing a session that has already ended is " +
24
+ "safe and returns what it observed, so there is no need to check first.",
25
+ },
26
+ },
27
+ required: ["sessionId"],
28
+ additionalProperties: false,
29
+ };
30
+ async function run(args, ctx) {
31
+ const sessionId = requiredString(args, "sessionId");
32
+ const registry = ctx.session.browserSessions();
33
+ const alreadyReaped = registry.reaped(sessionId);
34
+ ctx.progress(`Closing browser session ${sessionId}`);
35
+ const observation = await registry.close(sessionId);
36
+ const evidence = reportOf(observation);
37
+ const endedBy = alreadyReaped === undefined ? "thisCall" : alreadyReaped.reason === "closed" ? "alreadyClosed" : alreadyReaped.reason;
38
+ const notes = [...evidenceNotes(evidence)];
39
+ if (alreadyReaped !== undefined) {
40
+ notes.push(`This session had already ended (${endedBy}). Anything it observed before it went is included here ` +
41
+ "rather than discarded.");
42
+ }
43
+ return browserAnswer({
44
+ ctx,
45
+ headline: alreadyReaped === undefined
46
+ ? `Closed browser session ${sessionId}.`
47
+ : `Browser session ${sessionId} had already ended (${endedBy}).`,
48
+ notes,
49
+ data: { sessionId, endedBy, evidence },
50
+ });
51
+ }
52
+ export const browserCloseSessionTool = {
53
+ name: "browser_close_session",
54
+ title: "Close a browser session",
55
+ description: "Tear down an open browser session and return everything it observed but had not yet reported. Safe to " +
56
+ "call on a session that has already ended — it tells you how it ended and hands over what it saw.",
57
+ class: "stateful-read",
58
+ tier: "evidence",
59
+ version: "1.0.0",
60
+ inputSchema: SCHEMA,
61
+ run,
62
+ };
63
+ //# sourceMappingURL=browser-close-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-close-session.js","sourceRoot":"","sources":["../../src/tools/browser-close-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAyC,MAAM,UAAU,CAAC;AAUjF,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,SAAS,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,kGAAkG;gBAClG,wEAAwE;SAC3E;KACF;IACD,QAAQ,EAAE,CAAC,WAAW,CAAC;IACvB,oBAAoB,EAAE,KAAK;CACnB,CAAC;AAEX,KAAK,UAAU,GAAG,CAAC,IAA6B,EAAE,GAAgB;IAChE,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;IAE/C,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjD,GAAG,CAAC,QAAQ,CAAC,2BAA2B,SAAS,EAAE,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAEvC,MAAM,OAAO,GACX,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC;IAExH,MAAM,KAAK,GAAG,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3C,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CACR,mCAAmC,OAAO,0DAA0D;YAClG,wBAAwB,CAC3B,CAAC;IACJ,CAAC;IAED,OAAO,aAAa,CAA0B;QAC5C,GAAG;QACH,QAAQ,EACN,aAAa,KAAK,SAAS;YACzB,CAAC,CAAC,0BAA0B,SAAS,GAAG;YACxC,CAAC,CAAC,mBAAmB,SAAS,uBAAuB,OAAO,IAAI;QACpE,KAAK;QACL,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE;KACvC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAmB;IACrD,IAAI,EAAE,uBAAuB;IAC7B,KAAK,EAAE,yBAAyB;IAChC,WAAW,EACT,wGAAwG;QACxG,kGAAkG;IACpG,KAAK,EAAE,eAAe;IACtB,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,MAAM;IACnB,GAAG;CACJ,CAAC"}
@@ -0,0 +1,49 @@
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 type { BrowserEvidenceReport } from "../browser/evidence.ts";
38
+ import { type BrowserGraphWrite } from "../browser/graph-write.ts";
39
+ import { type ToolDefinition } from "./kit.ts";
40
+ export interface BrowserFillData {
41
+ readonly sessionId: string;
42
+ readonly ref: string;
43
+ readonly url: string;
44
+ readonly evidence: BrowserEvidenceReport;
45
+ /** Graph nodes this action's own requests reached, and any R4 edges it witnessed. */
46
+ readonly graph: BrowserGraphWrite;
47
+ }
48
+ export declare const browserFillTool: ToolDefinition;
49
+ //# sourceMappingURL=browser-fill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-fill.d.ts","sourceRoot":"","sources":["../../src/tools/browser-fill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnF,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGjF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,qFAAqF;IACrF,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;CACnC;AA0FD,eAAO,MAAM,eAAe,EAAE,cAiB7B,CAAC"}
@@ -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"}