@descryy/mcp 0.2.0 → 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.
- package/dist/bin/descry-mcp.js +6 -1
- package/dist/bin/descry-mcp.js.map +1 -1
- package/dist/browser/driver.d.ts +236 -0
- package/dist/browser/driver.d.ts.map +1 -0
- package/dist/browser/driver.js +70 -0
- package/dist/browser/driver.js.map +1 -0
- package/dist/browser/evidence.d.ts +47 -0
- package/dist/browser/evidence.d.ts.map +1 -0
- package/dist/browser/evidence.js +55 -0
- package/dist/browser/evidence.js.map +1 -0
- package/dist/browser/fake-driver.d.ts +58 -0
- package/dist/browser/fake-driver.d.ts.map +1 -0
- package/dist/browser/fake-driver.js +161 -0
- package/dist/browser/fake-driver.js.map +1 -0
- package/dist/browser/graph-write.d.ts +86 -0
- package/dist/browser/graph-write.d.ts.map +1 -0
- package/dist/browser/graph-write.js +135 -0
- package/dist/browser/graph-write.js.map +1 -0
- package/dist/browser/playwright-driver.d.ts +65 -0
- package/dist/browser/playwright-driver.d.ts.map +1 -0
- package/dist/browser/playwright-driver.js +359 -0
- package/dist/browser/playwright-driver.js.map +1 -0
- package/dist/browser/provider.d.ts +49 -0
- package/dist/browser/provider.d.ts.map +1 -0
- package/dist/browser/provider.js +28 -0
- package/dist/browser/provider.js.map +1 -0
- package/dist/browser/registry.d.ts +182 -0
- package/dist/browser/registry.d.ts.map +1 -0
- package/dist/browser/registry.js +215 -0
- package/dist/browser/registry.js.map +1 -0
- package/dist/browser/scenario-resolve.d.ts +62 -0
- package/dist/browser/scenario-resolve.d.ts.map +1 -0
- package/dist/browser/scenario-resolve.js +127 -0
- package/dist/browser/scenario-resolve.js.map +1 -0
- package/dist/browser/scenario-runner.d.ts +113 -0
- package/dist/browser/scenario-runner.d.ts.map +1 -0
- package/dist/browser/scenario-runner.js +315 -0
- package/dist/browser/scenario-runner.js.map +1 -0
- package/dist/browser/stack-parser.d.ts +43 -0
- package/dist/browser/stack-parser.d.ts.map +1 -0
- package/dist/browser/stack-parser.js +112 -0
- package/dist/browser/stack-parser.js.map +1 -0
- package/dist/browser/tool-support.d.ts +113 -0
- package/dist/browser/tool-support.d.ts.map +1 -0
- package/dist/browser/tool-support.js +181 -0
- package/dist/browser/tool-support.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/render.d.ts +112 -10
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +79 -13
- package/dist/render.js.map +1 -1
- package/dist/scenarios/index.d.ts +4 -0
- package/dist/scenarios/index.d.ts.map +1 -0
- package/dist/scenarios/index.js +4 -0
- package/dist/scenarios/index.js.map +1 -0
- package/dist/scenarios/parse.d.ts +32 -0
- package/dist/scenarios/parse.d.ts.map +1 -0
- package/dist/scenarios/parse.js +199 -0
- package/dist/scenarios/parse.js.map +1 -0
- package/dist/scenarios/scenario.d.ts +115 -0
- package/dist/scenarios/scenario.d.ts.map +1 -0
- package/dist/scenarios/scenario.js +41 -0
- package/dist/scenarios/scenario.js.map +1 -0
- package/dist/scenarios/storage.d.ts +51 -0
- package/dist/scenarios/storage.d.ts.map +1 -0
- package/dist/scenarios/storage.js +93 -0
- package/dist/scenarios/storage.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +6 -3
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +68 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +89 -0
- package/dist/session.js.map +1 -1
- package/dist/tools/analyze.d.ts +41 -1
- package/dist/tools/analyze.d.ts.map +1 -1
- package/dist/tools/analyze.js +81 -8
- package/dist/tools/analyze.js.map +1 -1
- package/dist/tools/browser-click.d.ts +36 -0
- package/dist/tools/browser-click.d.ts.map +1 -0
- package/dist/tools/browser-click.js +117 -0
- package/dist/tools/browser-click.js.map +1 -0
- package/dist/tools/browser-close-session.d.ts +24 -0
- package/dist/tools/browser-close-session.d.ts.map +1 -0
- package/dist/tools/browser-close-session.js +63 -0
- package/dist/tools/browser-close-session.js.map +1 -0
- package/dist/tools/browser-fill.d.ts +49 -0
- package/dist/tools/browser-fill.d.ts.map +1 -0
- package/dist/tools/browser-fill.js +138 -0
- package/dist/tools/browser-fill.js.map +1 -0
- package/dist/tools/browser-navigate.d.ts +32 -0
- package/dist/tools/browser-navigate.d.ts.map +1 -0
- package/dist/tools/browser-navigate.js +103 -0
- package/dist/tools/browser-navigate.js.map +1 -0
- package/dist/tools/browser-run-scenario.d.ts +54 -0
- package/dist/tools/browser-run-scenario.d.ts.map +1 -0
- package/dist/tools/browser-run-scenario.js +228 -0
- package/dist/tools/browser-run-scenario.js.map +1 -0
- package/dist/tools/browser-save-scenario.d.ts +44 -0
- package/dist/tools/browser-save-scenario.d.ts.map +1 -0
- package/dist/tools/browser-save-scenario.js +298 -0
- package/dist/tools/browser-save-scenario.js.map +1 -0
- package/dist/tools/browser-snapshot.d.ts +60 -0
- package/dist/tools/browser-snapshot.d.ts.map +1 -0
- package/dist/tools/browser-snapshot.js +139 -0
- package/dist/tools/browser-snapshot.js.map +1 -0
- package/dist/tools/browser-start-session.d.ts +43 -0
- package/dist/tools/browser-start-session.d.ts.map +1 -0
- package/dist/tools/browser-start-session.js +272 -0
- package/dist/tools/browser-start-session.js.map +1 -0
- package/dist/tools/browser-type.d.ts +48 -0
- package/dist/tools/browser-type.d.ts.map +1 -0
- package/dist/tools/browser-type.js +136 -0
- package/dist/tools/browser-type.js.map +1 -0
- package/dist/tools/contracts.d.ts +15 -0
- package/dist/tools/contracts.d.ts.map +1 -1
- package/dist/tools/contracts.js +14 -3
- package/dist/tools/contracts.js.map +1 -1
- package/dist/tools/git-diff.d.ts.map +1 -1
- package/dist/tools/git-diff.js +85 -6
- package/dist/tools/git-diff.js.map +1 -1
- package/dist/tools/impact.d.ts +9 -0
- package/dist/tools/impact.d.ts.map +1 -1
- package/dist/tools/impact.js +3 -3
- package/dist/tools/impact.js.map +1 -1
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +27 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kit.d.ts +10 -1
- package/dist/tools/kit.d.ts.map +1 -1
- package/dist/tools/kit.js +2 -0
- package/dist/tools/kit.js.map +1 -1
- package/dist/tools/observe-runtime.d.ts +113 -19
- package/dist/tools/observe-runtime.d.ts.map +1 -1
- package/dist/tools/observe-runtime.js +293 -52
- package/dist/tools/observe-runtime.js.map +1 -1
- package/dist/tools/observe-tests.d.ts +125 -0
- package/dist/tools/observe-tests.d.ts.map +1 -0
- package/dist/tools/observe-tests.js +313 -0
- package/dist/tools/observe-tests.js.map +1 -0
- package/dist/tools/pr-analysis.d.ts +11 -0
- package/dist/tools/pr-analysis.d.ts.map +1 -1
- package/dist/tools/pr-analysis.js +3 -3
- package/dist/tools/pr-analysis.js.map +1 -1
- package/dist/tools/pre-push.d.ts +77 -0
- package/dist/tools/pre-push.d.ts.map +1 -0
- package/dist/tools/pre-push.js +250 -0
- package/dist/tools/pre-push.js.map +1 -0
- package/dist/tools/questions.d.ts.map +1 -1
- package/dist/tools/questions.js +41 -16
- package/dist/tools/questions.js.map +1 -1
- package/dist/tools/verification-status.d.ts +22 -0
- package/dist/tools/verification-status.d.ts.map +1 -1
- package/dist/tools/verification-status.js +51 -5
- package/dist/tools/verification-status.js.map +1 -1
- package/package.json +14 -13
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-snapshot.d.ts","sourceRoot":"","sources":["../../src/tools/browser-snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAqD,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAClG,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,6IAA6I;IAC7I,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,eAAe,CAAC;AA6ElD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,GAAG,QAAQ,CAAC,mBAAmB,CAAC,CAwBzG;AAED,eAAO,MAAM,mBAAmB,EAAE,cAajC,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `browser_snapshot` — what is on the page, as refs an action can take.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this tool implements `fitToBudget` from its first commit
|
|
5
|
+
*
|
|
6
|
+
* A real accessibility tree is tens of thousands of bytes. `get_git_diff`
|
|
7
|
+
* shipped without an aggregate reply ceiling, returned 228,860 characters on
|
|
8
|
+
* an ordinary branch diff, and was rejected by the host before the caller saw
|
|
9
|
+
* any of it; the fix that followed bounded patch text and still failed at
|
|
10
|
+
* 84,516, because a reply is not only patch text. That lesson was paid for
|
|
11
|
+
* twice, and a brand-new tool whose payload grows with the user's application
|
|
12
|
+
* is precisely where it would be paid for a third time.
|
|
13
|
+
*
|
|
14
|
+
* So the budget hook is here from day one, and the test asserts the
|
|
15
|
+
* serialized `CallToolResult` rather than asserting that a cap ran.
|
|
16
|
+
*
|
|
17
|
+
* ## `totalElements` is never the capped number
|
|
18
|
+
*
|
|
19
|
+
* A snapshot that quietly returned the first forty elements would teach a
|
|
20
|
+
* calling agent that the page has forty elements. It would then conclude the
|
|
21
|
+
* button it needs is absent, which is a wrong answer produced by a correct
|
|
22
|
+
* tool. So the uncapped count is always present and always the real one, and
|
|
23
|
+
* a shortened reply says so in a note.
|
|
24
|
+
*/
|
|
25
|
+
import { browserAnswer, observeAndCorrelate, useSession } from "../browser/tool-support.js";
|
|
26
|
+
import { NO_GRAPH_WRITE } from "../browser/graph-write.js";
|
|
27
|
+
import { optionalInteger, 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
|
+
startAt: {
|
|
36
|
+
type: "integer",
|
|
37
|
+
minimum: 0,
|
|
38
|
+
description: "Skip this many elements before listing. Use it to page through a large page: compare the number " +
|
|
39
|
+
"of elements you received against totalElements, which is always the real count, and ask again " +
|
|
40
|
+
"with startAt set past what you have seen.",
|
|
41
|
+
},
|
|
42
|
+
limit: {
|
|
43
|
+
type: "integer",
|
|
44
|
+
minimum: 1,
|
|
45
|
+
description: "At most this many elements. A reply too large for the host is shortened further regardless, and says so.",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ["sessionId"],
|
|
49
|
+
additionalProperties: false,
|
|
50
|
+
};
|
|
51
|
+
async function run(args, ctx) {
|
|
52
|
+
const sessionId = requiredString(args, "sessionId");
|
|
53
|
+
const startAt = optionalInteger(args, "startAt", 0) ?? 0;
|
|
54
|
+
const limit = optionalInteger(args, "limit", 1);
|
|
55
|
+
const found = await useSession(ctx.session.browserSessions(), sessionId);
|
|
56
|
+
if (!found.ok) {
|
|
57
|
+
return browserAnswer({
|
|
58
|
+
ctx,
|
|
59
|
+
state: found.dead.state,
|
|
60
|
+
headline: found.dead.headline,
|
|
61
|
+
notes: found.dead.notes,
|
|
62
|
+
data: { sessionId: "", url: "", title: "", elements: [], totalElements: 0, evidence: found.dead.evidence, graph: NO_GRAPH_WRITE },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const { entry } = found;
|
|
66
|
+
ctx.progress("Reading the page");
|
|
67
|
+
const snapshot = await entry.page.snapshot();
|
|
68
|
+
const observed = await observeAndCorrelate(ctx, entry);
|
|
69
|
+
const evidence = observed.evidence;
|
|
70
|
+
const windowed = snapshot.elements.slice(startAt, limit === undefined ? undefined : startAt + limit);
|
|
71
|
+
const notes = [...observed.notes];
|
|
72
|
+
if (windowed.length < snapshot.totalElements) {
|
|
73
|
+
notes.push(`Showing ${windowed.length} of ${snapshot.totalElements} elements, from index ${startAt}. ` +
|
|
74
|
+
"totalElements is always the real count — ask again with startAt past what you have seen.");
|
|
75
|
+
}
|
|
76
|
+
const data = {
|
|
77
|
+
sessionId: entry.sessionId,
|
|
78
|
+
url: snapshot.url,
|
|
79
|
+
title: snapshot.title,
|
|
80
|
+
elements: windowed,
|
|
81
|
+
totalElements: snapshot.totalElements,
|
|
82
|
+
evidence,
|
|
83
|
+
graph: observed.graph,
|
|
84
|
+
};
|
|
85
|
+
return browserAnswer({
|
|
86
|
+
ctx,
|
|
87
|
+
headline: `${snapshot.url} — ${windowed.length} of ${snapshot.totalElements} elements.`,
|
|
88
|
+
notes,
|
|
89
|
+
data,
|
|
90
|
+
fitToBudget: (budgetBytes) => fitSnapshot(data, budgetBytes),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Shrink by dropping elements from the end of the window, never by dropping
|
|
95
|
+
* the count or the evidence.
|
|
96
|
+
*
|
|
97
|
+
* Evidence stays because it is the part a caller cannot re-request by paging —
|
|
98
|
+
* the observations were drained when this reply was built, and a second
|
|
99
|
+
* snapshot would return a different set. Elements can always be asked for
|
|
100
|
+
* again with `startAt`, so they are what gives way.
|
|
101
|
+
*/
|
|
102
|
+
export function fitSnapshot(data, budgetBytes) {
|
|
103
|
+
const fits = (elements) => Buffer.byteLength(JSON.stringify({ ...data, elements }), "utf8") <= budgetBytes;
|
|
104
|
+
if (fits(data.elements))
|
|
105
|
+
return { data, notes: [] };
|
|
106
|
+
let low = 0;
|
|
107
|
+
let high = data.elements.length;
|
|
108
|
+
while (low < high) {
|
|
109
|
+
const mid = Math.ceil((low + high) / 2);
|
|
110
|
+
if (fits(data.elements.slice(0, mid)))
|
|
111
|
+
low = mid;
|
|
112
|
+
else
|
|
113
|
+
high = mid - 1;
|
|
114
|
+
}
|
|
115
|
+
const kept = data.elements.slice(0, low);
|
|
116
|
+
return {
|
|
117
|
+
data: { ...data, elements: kept },
|
|
118
|
+
notes: [
|
|
119
|
+
`The page's elements did not fit in one reply, so ${kept.length} of ${data.totalElements} are included. ` +
|
|
120
|
+
`totalElements is the real count, not the number shown — ask again with startAt set to ${kept.length} ` +
|
|
121
|
+
"for the next batch.",
|
|
122
|
+
],
|
|
123
|
+
truncatedDetail: { shown: kept.length, total: data.totalElements, more: null },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export const browserSnapshotTool = {
|
|
127
|
+
name: "browser_snapshot",
|
|
128
|
+
title: "Read the current page",
|
|
129
|
+
description: "Return the elements on the page an open browser session is showing, each with a ref the click, type " +
|
|
130
|
+
"and fill tools take. Also returns anything the page produced since your last call — console errors and " +
|
|
131
|
+
"failed requests, raw and untriaged. totalElements is always the page's real element count, so a short " +
|
|
132
|
+
"list means the rest did not fit rather than that the page is small.",
|
|
133
|
+
class: "stateful-read",
|
|
134
|
+
tier: "evidence",
|
|
135
|
+
version: "1.0.0",
|
|
136
|
+
inputSchema: SCHEMA,
|
|
137
|
+
run,
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=browser-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-snapshot.js","sourceRoot":"","sources":["../../src/tools/browser-snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `browser_start_session` — open a real browser against the developer's app.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this one is the gate
|
|
5
|
+
*
|
|
6
|
+
* `DEC-380` puts consent here and nowhere
|
|
7
|
+
* else. This is the call where a target URL is named, so it is the only call
|
|
8
|
+
* where the fact a developer actually approves — *Descry may drive a browser
|
|
9
|
+
* against this application* — can be stated. A per-click confirmation would
|
|
10
|
+
* describe "click ref-14", which is a string the caller supplied one call
|
|
11
|
+
* earlier and can neither evaluate nor be surprised by, and it would train
|
|
12
|
+
* callers to confirm reflexively.
|
|
13
|
+
*
|
|
14
|
+
* The grant is bound to this URL's **origin**. `browser_navigate` refuses to
|
|
15
|
+
* leave it. Without that bound, "drive my local app" would silently become
|
|
16
|
+
* "drive anything".
|
|
17
|
+
*
|
|
18
|
+
* ## Class
|
|
19
|
+
*
|
|
20
|
+
* `action`, unambiguously: this launches a process and points it at a running
|
|
21
|
+
* application. It is the only browser tool that mints a token; the verbs it
|
|
22
|
+
* consents for redeem nothing, because the session they run in has already
|
|
23
|
+
* been confirmed.
|
|
24
|
+
*/
|
|
25
|
+
import type { BrowserEvidenceReport } from "../browser/evidence.ts";
|
|
26
|
+
import { type BrowserGraphWrite } from "../browser/graph-write.ts";
|
|
27
|
+
import { type ToolDefinition } from "./kit.ts";
|
|
28
|
+
export interface BrowserStartSessionData {
|
|
29
|
+
/** Hand this to every other browser tool. Valid on this connection only. */
|
|
30
|
+
readonly sessionId: string;
|
|
31
|
+
readonly url: string;
|
|
32
|
+
readonly title: string;
|
|
33
|
+
/** The origin this session's consent covers. Navigation outside it refuses. */
|
|
34
|
+
readonly consentedOrigin: string;
|
|
35
|
+
/** The safety level this session runs under, or null when no profile was given. */
|
|
36
|
+
readonly safetyLevel: "readOnly" | "write" | "destructiveWithApproval" | null;
|
|
37
|
+
/** How many elements the landing page has, before any reply budget applies. */
|
|
38
|
+
readonly totalElements: number;
|
|
39
|
+
readonly evidence: BrowserEvidenceReport;
|
|
40
|
+
readonly graph: BrowserGraphWrite;
|
|
41
|
+
}
|
|
42
|
+
export declare const browserStartSessionTool: ToolDefinition;
|
|
43
|
+
//# sourceMappingURL=browser-start-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-start-session.d.ts","sourceRoot":"","sources":["../../src/tools/browser-start-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAOnF,OAAO,EAAmG,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGhJ,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,mFAAmF;IACnF,QAAQ,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,GAAG,yBAAyB,GAAG,IAAI,CAAC;IAC9E,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;CACnC;AAoPD,eAAO,MAAM,uBAAuB,EAAE,cAsBrC,CAAC"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `browser_start_session` — open a real browser against the developer's app.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this one is the gate
|
|
5
|
+
*
|
|
6
|
+
* `DEC-380` puts consent here and nowhere
|
|
7
|
+
* else. This is the call where a target URL is named, so it is the only call
|
|
8
|
+
* where the fact a developer actually approves — *Descry may drive a browser
|
|
9
|
+
* against this application* — can be stated. A per-click confirmation would
|
|
10
|
+
* describe "click ref-14", which is a string the caller supplied one call
|
|
11
|
+
* earlier and can neither evaluate nor be surprised by, and it would train
|
|
12
|
+
* callers to confirm reflexively.
|
|
13
|
+
*
|
|
14
|
+
* The grant is bound to this URL's **origin**. `browser_navigate` refuses to
|
|
15
|
+
* leave it. Without that bound, "drive my local app" would silently become
|
|
16
|
+
* "drive anything".
|
|
17
|
+
*
|
|
18
|
+
* ## Class
|
|
19
|
+
*
|
|
20
|
+
* `action`, unambiguously: this launches a process and points it at a running
|
|
21
|
+
* application. It is the only browser tool that mints a token; the verbs it
|
|
22
|
+
* consents for redeem nothing, because the session they run in has already
|
|
23
|
+
* been confirmed.
|
|
24
|
+
*/
|
|
25
|
+
import { browserAnswer, emptyEvidence, observeAndCorrelate } from "../browser/tool-support.js";
|
|
26
|
+
import { NO_GRAPH_WRITE } from "../browser/graph-write.js";
|
|
27
|
+
import { isAbsolute, join } from "node:path";
|
|
28
|
+
import { loadStackParser, StackParserLoadError } from "../browser/stack-parser.js";
|
|
29
|
+
import { optionalBoolean, optionalEnum, optionalString, requiredString, ToolInputError } from "./kit.js";
|
|
30
|
+
const SCHEMA = {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
targetUrl: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "REQUIRED. The URL to open, including scheme and port — \"http://localhost:3000/invoices\". " +
|
|
36
|
+
"This is also what consent is granted for: the browser may be driven anywhere on this URL's " +
|
|
37
|
+
"origin for the life of the session, and navigating off it refuses rather than asking again.",
|
|
38
|
+
},
|
|
39
|
+
headless: {
|
|
40
|
+
type: "boolean",
|
|
41
|
+
description: "Run without a visible window. Defaults to true; pass false to watch it happen.",
|
|
42
|
+
},
|
|
43
|
+
stackParser: {
|
|
44
|
+
type: "object",
|
|
45
|
+
description: "The module that can parse this application's stack traces, named the way observe_runtime names a " +
|
|
46
|
+
"runtime adapter. Without one, requests are still observed and the endpoints they reached are still " +
|
|
47
|
+
"named, but NO frontend→backend caller edge is written — a parsed call site is what the confirmation " +
|
|
48
|
+
"needs, and there is no way to guess one. The reply says so rather than reporting a run that found " +
|
|
49
|
+
"nothing.",
|
|
50
|
+
properties: {
|
|
51
|
+
module: { type: "string", description: "Module specifier to import, e.g. a runtime adapter package for the language your frontend is written in." },
|
|
52
|
+
export: { type: "string", description: "Named export to call. Defaults to the module's single create*StackTraceParser factory; two matches is an error rather than a guess." },
|
|
53
|
+
},
|
|
54
|
+
required: ["module"],
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
},
|
|
57
|
+
documentRoot: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "The directory on disk that the target URL's origin serves files from, if it is not the repository " +
|
|
60
|
+
"root. Absolute, or relative to the repository. THIS IS THE ONE THAT SILENTLY BREAKS THE ARROW: a " +
|
|
61
|
+
"server that serves scripts out of \"public/\" makes public/ the document root while the repo root is " +
|
|
62
|
+
"one level above it, and getting it wrong resolves a script URL to a path that does not exist, which " +
|
|
63
|
+
"degrades the call-site stack to nothing and produces a run that looks clean and confirmed no edges. " +
|
|
64
|
+
"Defaults to the repository root.",
|
|
65
|
+
},
|
|
66
|
+
environment: {
|
|
67
|
+
type: "object",
|
|
68
|
+
description: "The environment profile this session runs against, if you have one. Its safetyLevel governs " +
|
|
69
|
+
"whether the session may change the application: \"readOnly\" refuses browser_click, browser_type " +
|
|
70
|
+
"and browser_fill while still permitting browser_navigate and browser_snapshot. Declared once here " +
|
|
71
|
+
"rather than per call, and it is the same profile observe_runtime takes — a browser run and a " +
|
|
72
|
+
"runtime run must not hold two different opinions about the same environment (DEC-270). Omit it and " +
|
|
73
|
+
"the session's own consent is the only authorization, which still means this call had to be confirmed.",
|
|
74
|
+
properties: {
|
|
75
|
+
name: { type: "string", description: "The profile's name, free-form. Named back to you in any refusal it causes." },
|
|
76
|
+
safetyLevel: {
|
|
77
|
+
type: "string",
|
|
78
|
+
enum: ["readOnly", "write", "destructiveWithApproval"],
|
|
79
|
+
description: "\"readOnly\" refuses every action that could change the application, whatever the profile is " +
|
|
80
|
+
"called — a profile named \"local\" with safetyLevel \"readOnly\" is read-only too.",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
required: ["name", "safetyLevel"],
|
|
84
|
+
additionalProperties: true,
|
|
85
|
+
},
|
|
86
|
+
confirmToken: {
|
|
87
|
+
type: "string",
|
|
88
|
+
description: "The token returned by a previous call. Starting a browser session takes two calls: the first " +
|
|
89
|
+
"returns a token and launches nothing, the second redeems it. A token is single-use, expires, " +
|
|
90
|
+
"and is only valid for this tool. Consent is granted once here — the click, type and fill tools " +
|
|
91
|
+
"need no token of their own.",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
required: ["targetUrl"],
|
|
95
|
+
additionalProperties: false,
|
|
96
|
+
};
|
|
97
|
+
const SAFETY_LEVELS = ["readOnly", "write", "destructiveWithApproval"];
|
|
98
|
+
/**
|
|
99
|
+
* Read the profile, do not re-derive it.
|
|
100
|
+
*
|
|
101
|
+
* Only the two fields this surface actually consults are read; everything
|
|
102
|
+
* else a caller sends is carried by the profile it came from and is none of
|
|
103
|
+
* this tool's business. `additionalProperties: true` on the schema is what
|
|
104
|
+
* lets a caller pass the same object it passes `observe_runtime` rather than
|
|
105
|
+
* trimming a second copy of it.
|
|
106
|
+
*/
|
|
107
|
+
function readProfile(args) {
|
|
108
|
+
const raw = args["environment"];
|
|
109
|
+
if (raw === undefined || raw === null)
|
|
110
|
+
return null;
|
|
111
|
+
if (typeof raw !== "object" || Array.isArray(raw))
|
|
112
|
+
throw new ToolInputError('"environment" must be an object');
|
|
113
|
+
const fields = raw;
|
|
114
|
+
const name = requiredString(fields, "name");
|
|
115
|
+
const safetyLevel = optionalEnum(fields, "safetyLevel", SAFETY_LEVELS);
|
|
116
|
+
if (safetyLevel === undefined) {
|
|
117
|
+
throw new ToolInputError('"environment.safetyLevel" is required when an environment is given — a profile with no declared ' +
|
|
118
|
+
"safety level would be read as permitting everything, which is the one reading it must never get by default");
|
|
119
|
+
}
|
|
120
|
+
return { name, safetyLevel };
|
|
121
|
+
}
|
|
122
|
+
function readStackParserSpec(args) {
|
|
123
|
+
const raw = args["stackParser"];
|
|
124
|
+
if (raw === undefined || raw === null)
|
|
125
|
+
return null;
|
|
126
|
+
if (typeof raw !== "object" || Array.isArray(raw))
|
|
127
|
+
throw new ToolInputError('"stackParser" must be an object');
|
|
128
|
+
const fields = raw;
|
|
129
|
+
const module = requiredString(fields, "module");
|
|
130
|
+
const named = optionalString(fields, "export");
|
|
131
|
+
return named === undefined ? { module } : { module, export: named };
|
|
132
|
+
}
|
|
133
|
+
function parseTarget(args) {
|
|
134
|
+
const raw = requiredString(args, "targetUrl");
|
|
135
|
+
try {
|
|
136
|
+
return new URL(raw);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
throw new ToolInputError(`"targetUrl" must be an absolute URL including scheme — got "${raw}". A bare host or path cannot be ` +
|
|
140
|
+
"opened, and cannot be turned into the origin this session's consent is bound to.");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function run(args, ctx) {
|
|
144
|
+
const target = parseTarget(args);
|
|
145
|
+
const headless = optionalBoolean(args, "headless") ?? true;
|
|
146
|
+
const profile = readProfile(args);
|
|
147
|
+
const parserSpec = readStackParserSpec(args);
|
|
148
|
+
const documentRoot = optionalString(args, "documentRoot");
|
|
149
|
+
const backend = ctx.session.browserBackend();
|
|
150
|
+
if (!backend.available) {
|
|
151
|
+
return browserAnswer({
|
|
152
|
+
ctx,
|
|
153
|
+
state: "refused",
|
|
154
|
+
headline: "This build cannot open a browser.",
|
|
155
|
+
notes: [backend.reason, `Remedy: ${backend.remedy}`],
|
|
156
|
+
data: { sessionId: "", url: target.href, title: "", consentedOrigin: target.origin, safetyLevel: null, totalElements: 0, evidence: emptyEvidence(), graph: NO_GRAPH_WRITE },
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// The parser is loaded before anything launches. A parser that will not
|
|
160
|
+
// import is a refusal the caller can fix, and finding that out after a
|
|
161
|
+
// browser is open would mean either tearing it down again or running a
|
|
162
|
+
// session that quietly cannot write the edge it was opened for.
|
|
163
|
+
let parser;
|
|
164
|
+
try {
|
|
165
|
+
parser = parserSpec === null ? undefined : await loadStackParser(parserSpec);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
if (!(error instanceof StackParserLoadError))
|
|
169
|
+
throw error;
|
|
170
|
+
return browserAnswer({
|
|
171
|
+
ctx,
|
|
172
|
+
state: "refused",
|
|
173
|
+
headline: `Could not load the stack-trace parser "${error.specifier}".`,
|
|
174
|
+
notes: [error.message],
|
|
175
|
+
data: { sessionId: "", url: target.href, title: "", consentedOrigin: target.origin, safetyLevel: null, totalElements: 0, evidence: emptyEvidence(), graph: NO_GRAPH_WRITE },
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// Trap A, closed here rather than left to a caller to get right silently.
|
|
179
|
+
// The origin being driven maps to the directory that actually serves its
|
|
180
|
+
// scripts; `documentRoot` overrides the repository root for exactly the
|
|
181
|
+
// `public/` case the schema describes.
|
|
182
|
+
const servedFrom = documentRoot === undefined
|
|
183
|
+
? ctx.session.repoPath
|
|
184
|
+
: isAbsolute(documentRoot)
|
|
185
|
+
? documentRoot
|
|
186
|
+
: join(ctx.session.repoPath, documentRoot);
|
|
187
|
+
const resolveSourceRoot = (origin) => origin === target.origin
|
|
188
|
+
? { found: true, root: { serviceName: ctx.session.config.repo, declaredCwd: servedFrom } }
|
|
189
|
+
: { found: false, refusal: "noProcessOnPort", message: `${origin} is not the origin this session was opened against` };
|
|
190
|
+
ctx.progress(`Opening a browser at ${target.href}`);
|
|
191
|
+
const driver = await backend.createDriver({
|
|
192
|
+
...(parser === undefined ? {} : { stackParser: parser }),
|
|
193
|
+
resolveSourceRoot,
|
|
194
|
+
service: ctx.session.config.repo,
|
|
195
|
+
});
|
|
196
|
+
const opened = await ctx.session.browserSessions().open(driver, { targetUrl: target.href, headless, profile });
|
|
197
|
+
if (!opened.opened) {
|
|
198
|
+
const notes = opened.kind === "atCapacity"
|
|
199
|
+
? [opened.reason]
|
|
200
|
+
: // The remedy travels verbatim. A refusal that says "could not launch"
|
|
201
|
+
// and stops is one the developer has to go and research.
|
|
202
|
+
[opened.message, `Remedy: ${opened.remedy}`, `Expected the browser at ${opened.expectedExecutablePath}.`];
|
|
203
|
+
return browserAnswer({
|
|
204
|
+
ctx,
|
|
205
|
+
state: "refused",
|
|
206
|
+
headline: opened.kind === "atCapacity" ? "Too many browser sessions are already open." : "No browser is installed.",
|
|
207
|
+
notes,
|
|
208
|
+
data: { sessionId: "", url: target.href, title: "", consentedOrigin: target.origin, safetyLevel: profile?.safetyLevel ?? null, totalElements: 0, evidence: emptyEvidence(), graph: NO_GRAPH_WRITE },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
const { entry } = opened;
|
|
212
|
+
const snapshot = await entry.page.snapshot();
|
|
213
|
+
const observed = await observeAndCorrelate(ctx, entry);
|
|
214
|
+
const evidence = observed.evidence;
|
|
215
|
+
return browserAnswer({
|
|
216
|
+
ctx,
|
|
217
|
+
headline: `Browser session ${entry.sessionId} open at ${snapshot.url} (${snapshot.totalElements} elements).`,
|
|
218
|
+
notes: [
|
|
219
|
+
`Consent covers ${entry.consentedOrigin} for the life of this session — click, type and fill need no ` +
|
|
220
|
+
"further confirmation, and navigating to another origin will refuse. The session is torn down after " +
|
|
221
|
+
"five idle minutes, when the client disconnects, or when browser_close_session is called.",
|
|
222
|
+
...(parserSpec === null
|
|
223
|
+
? [
|
|
224
|
+
"No stackParser was named, so requests will be observed and the endpoints they reach will be " +
|
|
225
|
+
"named, but no frontend→backend caller edge can be written from this session. That is a gap " +
|
|
226
|
+
"in what this run can confirm, not a finding about your application.",
|
|
227
|
+
]
|
|
228
|
+
: []),
|
|
229
|
+
...(entry.profile === null
|
|
230
|
+
? []
|
|
231
|
+
: [
|
|
232
|
+
`Running under environment profile "${entry.profile.name}" (safetyLevel ` +
|
|
233
|
+
`"${entry.profile.safetyLevel}").` +
|
|
234
|
+
(entry.profile.safetyLevel === "readOnly"
|
|
235
|
+
? " Clicking, typing and filling will be refused; navigate and snapshot remain available."
|
|
236
|
+
: ""),
|
|
237
|
+
]),
|
|
238
|
+
...observed.notes,
|
|
239
|
+
],
|
|
240
|
+
data: {
|
|
241
|
+
sessionId: entry.sessionId,
|
|
242
|
+
url: snapshot.url,
|
|
243
|
+
title: snapshot.title,
|
|
244
|
+
consentedOrigin: entry.consentedOrigin,
|
|
245
|
+
safetyLevel: entry.profile?.safetyLevel ?? null,
|
|
246
|
+
totalElements: snapshot.totalElements,
|
|
247
|
+
evidence,
|
|
248
|
+
graph: observed.graph,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
export const browserStartSessionTool = {
|
|
253
|
+
name: "browser_start_session",
|
|
254
|
+
title: "Open a browser session",
|
|
255
|
+
description: "Open a real browser at a URL in your running application and return a session id the other browser " +
|
|
256
|
+
"tools take. This is where consent is granted: confirming it allows Descry to drive a browser anywhere " +
|
|
257
|
+
"on that URL's origin until the session ends, so the click, type and fill tools do not ask again. " +
|
|
258
|
+
"Reports what the page produced — console errors, failed requests — raw and untriaged; deciding which " +
|
|
259
|
+
"of them matter is yours.",
|
|
260
|
+
class: "action",
|
|
261
|
+
tier: "evidence",
|
|
262
|
+
version: "1.0.0",
|
|
263
|
+
inputSchema: SCHEMA,
|
|
264
|
+
run,
|
|
265
|
+
describeAction(args) {
|
|
266
|
+
const target = parseTarget(args);
|
|
267
|
+
return (`Launch a browser and open ${target.href}. For as long as the session lives, Descry may navigate, ` +
|
|
268
|
+
`click, type and fill anywhere on ${target.origin} without asking again — including actions that ` +
|
|
269
|
+
"change data in that application. It may not leave that origin.");
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
//# sourceMappingURL=browser-start-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-start-session.js","sourceRoot":"","sources":["../../src/tools/browser-start-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `browser_type` — type into a field with real keystrokes.
|
|
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
|
+
* ## Why this is not the same tool as `browser_fill`
|
|
26
|
+
*
|
|
27
|
+
* The visible difference is that this appends and `fill` replaces. The
|
|
28
|
+
* difference that decides which one is correct for a real page is that **this
|
|
29
|
+
* one fires key events and `fill` fires none.** A field that opens an
|
|
30
|
+
* autocomplete on keydown, validates per character, or debounces a search as
|
|
31
|
+
* you type sees nothing at all from a `fill`. An agent that reaches for
|
|
32
|
+
* `fill` on a search box gets a page that was never typed into — a wrong
|
|
33
|
+
* answer produced by entirely correct tool code, which is why the schema
|
|
34
|
+
* prose below says so rather than describing only the tidy half.
|
|
35
|
+
*/
|
|
36
|
+
import type { BrowserEvidenceReport } from "../browser/evidence.ts";
|
|
37
|
+
import { type BrowserGraphWrite } from "../browser/graph-write.ts";
|
|
38
|
+
import { type ToolDefinition } from "./kit.ts";
|
|
39
|
+
export interface BrowserTypeData {
|
|
40
|
+
readonly sessionId: string;
|
|
41
|
+
readonly ref: string;
|
|
42
|
+
readonly url: string;
|
|
43
|
+
readonly evidence: BrowserEvidenceReport;
|
|
44
|
+
/** Graph nodes this action's own requests reached, and any R4 edges it witnessed. */
|
|
45
|
+
readonly graph: BrowserGraphWrite;
|
|
46
|
+
}
|
|
47
|
+
export declare const browserTypeTool: ToolDefinition;
|
|
48
|
+
//# sourceMappingURL=browser-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-type.d.ts","sourceRoot":"","sources":["../../src/tools/browser-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH;;;;;;;;;;;GAWG;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;AAyFD,eAAO,MAAM,eAAe,EAAE,cAiB7B,CAAC"}
|