@descryy/mcp 0.6.0 → 0.7.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/action-handshake.d.ts +8 -75
- package/dist/action-handshake.d.ts.map +1 -1
- package/dist/action-handshake.js +9 -79
- package/dist/action-handshake.js.map +1 -1
- package/dist/bin/descry-mcp.d.ts +4 -15
- package/dist/bin/descry-mcp.d.ts.map +1 -1
- package/dist/bin/descry-mcp.js +12 -42
- package/dist/bin/descry-mcp.js.map +1 -1
- package/dist/browser/driver.d.ts +59 -224
- package/dist/browser/driver.d.ts.map +1 -1
- package/dist/browser/driver.js +9 -55
- package/dist/browser/driver.js.map +1 -1
- package/dist/browser/evidence.d.ts +10 -43
- package/dist/browser/evidence.d.ts.map +1 -1
- package/dist/browser/evidence.js +20 -54
- package/dist/browser/evidence.js.map +1 -1
- package/dist/browser/fake-driver.d.ts +4 -16
- package/dist/browser/fake-driver.d.ts.map +1 -1
- package/dist/browser/fake-driver.js +6 -20
- package/dist/browser/fake-driver.js.map +1 -1
- package/dist/browser/graph-write.d.ts +4 -45
- package/dist/browser/graph-write.d.ts.map +1 -1
- package/dist/browser/graph-write.js +8 -53
- package/dist/browser/graph-write.js.map +1 -1
- package/dist/browser/playwright-driver.d.ts +7 -91
- package/dist/browser/playwright-driver.d.ts.map +1 -1
- package/dist/browser/playwright-driver.js +40 -194
- package/dist/browser/playwright-driver.js.map +1 -1
- package/dist/browser/provider.d.ts +5 -34
- package/dist/browser/provider.d.ts.map +1 -1
- package/dist/browser/provider.js +4 -24
- package/dist/browser/provider.js.map +1 -1
- package/dist/browser/registry.d.ts +26 -106
- package/dist/browser/registry.d.ts.map +1 -1
- package/dist/browser/registry.js +20 -77
- package/dist/browser/registry.js.map +1 -1
- package/dist/browser/scenario-resolve.d.ts +10 -44
- package/dist/browser/scenario-resolve.d.ts.map +1 -1
- package/dist/browser/scenario-resolve.js +10 -41
- package/dist/browser/scenario-resolve.js.map +1 -1
- package/dist/browser/scenario-runner.d.ts +12 -70
- package/dist/browser/scenario-runner.d.ts.map +1 -1
- package/dist/browser/scenario-runner.js +30 -90
- package/dist/browser/scenario-runner.js.map +1 -1
- package/dist/browser/stack-parser.d.ts +4 -28
- package/dist/browser/stack-parser.d.ts.map +1 -1
- package/dist/browser/stack-parser.js +12 -39
- package/dist/browser/stack-parser.js.map +1 -1
- package/dist/browser/tool-support.d.ts +20 -67
- package/dist/browser/tool-support.d.ts.map +1 -1
- package/dist/browser/tool-support.js +21 -65
- package/dist/browser/tool-support.js.map +1 -1
- package/dist/browser/url-scheme.d.ts +14 -0
- package/dist/browser/url-scheme.d.ts.map +1 -0
- package/dist/browser/url-scheme.js +38 -0
- package/dist/browser/url-scheme.js.map +1 -0
- package/dist/cancellation.d.ts +11 -46
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/cancellation.js +11 -46
- package/dist/cancellation.js.map +1 -1
- package/dist/capped.d.ts +9 -33
- package/dist/capped.d.ts.map +1 -1
- package/dist/capped.js +7 -27
- package/dist/capped.js.map +1 -1
- package/dist/disclosure-ledger.d.ts +6 -30
- package/dist/disclosure-ledger.d.ts.map +1 -1
- package/dist/disclosure-ledger.js +4 -26
- package/dist/disclosure-ledger.js.map +1 -1
- package/dist/index.d.ts +10 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -17
- package/dist/index.js.map +1 -1
- package/dist/module-trust.d.ts +24 -0
- package/dist/module-trust.d.ts.map +1 -0
- package/dist/module-trust.js +60 -0
- package/dist/module-trust.js.map +1 -0
- package/dist/path-confinement.d.ts +31 -0
- package/dist/path-confinement.d.ts.map +1 -0
- package/dist/path-confinement.js +44 -0
- package/dist/path-confinement.js.map +1 -0
- package/dist/protocol.d.ts +10 -53
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +14 -60
- package/dist/protocol.js.map +1 -1
- package/dist/registry.d.ts +10 -58
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +38 -90
- package/dist/registry.js.map +1 -1
- package/dist/render.d.ts +42 -11
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +74 -10
- package/dist/render.js.map +1 -1
- package/dist/runtime-registry.d.ts +7 -66
- package/dist/runtime-registry.d.ts.map +1 -1
- package/dist/runtime-registry.js +39 -114
- package/dist/runtime-registry.js.map +1 -1
- package/dist/scenarios/index.d.ts +1 -1
- package/dist/scenarios/index.d.ts.map +1 -1
- package/dist/scenarios/index.js +1 -1
- package/dist/scenarios/index.js.map +1 -1
- package/dist/scenarios/parse.d.ts +4 -18
- package/dist/scenarios/parse.d.ts.map +1 -1
- package/dist/scenarios/parse.js +14 -34
- package/dist/scenarios/parse.js.map +1 -1
- package/dist/scenarios/scenario.d.ts +18 -74
- package/dist/scenarios/scenario.d.ts.map +1 -1
- package/dist/scenarios/scenario.js +7 -34
- package/dist/scenarios/scenario.js.map +1 -1
- package/dist/scenarios/storage.d.ts +11 -41
- package/dist/scenarios/storage.d.ts.map +1 -1
- package/dist/scenarios/storage.js +57 -47
- package/dist/scenarios/storage.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +37 -17
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +67 -259
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +69 -250
- package/dist/session.js.map +1 -1
- package/dist/tools/analyze.d.ts +24 -118
- package/dist/tools/analyze.d.ts.map +1 -1
- package/dist/tools/analyze.js +44 -166
- package/dist/tools/analyze.js.map +1 -1
- package/dist/tools/browser-click.d.ts +3 -21
- package/dist/tools/browser-click.d.ts.map +1 -1
- package/dist/tools/browser-click.js +10 -31
- package/dist/tools/browser-click.js.map +1 -1
- package/dist/tools/browser-close-session.d.ts +4 -13
- package/dist/tools/browser-close-session.d.ts.map +1 -1
- package/dist/tools/browser-close-session.js +4 -13
- package/dist/tools/browser-close-session.js.map +1 -1
- package/dist/tools/browser-fill.d.ts +5 -36
- package/dist/tools/browser-fill.d.ts.map +1 -1
- package/dist/tools/browser-fill.js +9 -44
- package/dist/tools/browser-fill.js.map +1 -1
- package/dist/tools/browser-navigate.d.ts +6 -27
- package/dist/tools/browser-navigate.d.ts.map +1 -1
- package/dist/tools/browser-navigate.js +6 -23
- package/dist/tools/browser-navigate.js.map +1 -1
- package/dist/tools/browser-run-scenario.d.ts +5 -31
- package/dist/tools/browser-run-scenario.d.ts.map +1 -1
- package/dist/tools/browser-run-scenario.js +10 -46
- package/dist/tools/browser-run-scenario.js.map +1 -1
- package/dist/tools/browser-save-scenario.d.ts +3 -28
- package/dist/tools/browser-save-scenario.d.ts.map +1 -1
- package/dist/tools/browser-save-scenario.js +15 -74
- package/dist/tools/browser-save-scenario.js.map +1 -1
- package/dist/tools/browser-snapshot.d.ts +6 -39
- package/dist/tools/browser-snapshot.d.ts.map +1 -1
- package/dist/tools/browser-snapshot.js +4 -31
- package/dist/tools/browser-snapshot.js.map +1 -1
- package/dist/tools/browser-start-session.d.ts +4 -23
- package/dist/tools/browser-start-session.d.ts.map +1 -1
- package/dist/tools/browser-start-session.js +33 -55
- package/dist/tools/browser-start-session.js.map +1 -1
- package/dist/tools/browser-type.d.ts +5 -35
- package/dist/tools/browser-type.d.ts.map +1 -1
- package/dist/tools/browser-type.js +10 -45
- package/dist/tools/browser-type.js.map +1 -1
- package/dist/tools/contracts.d.ts +9 -86
- package/dist/tools/contracts.d.ts.map +1 -1
- package/dist/tools/contracts.js +24 -80
- package/dist/tools/contracts.js.map +1 -1
- package/dist/tools/cross-pr.d.ts +42 -30
- package/dist/tools/cross-pr.d.ts.map +1 -1
- package/dist/tools/cross-pr.js +81 -62
- package/dist/tools/cross-pr.js.map +1 -1
- package/dist/tools/git-diff.d.ts +18 -2
- package/dist/tools/git-diff.d.ts.map +1 -1
- package/dist/tools/git-diff.js +118 -26
- package/dist/tools/git-diff.js.map +1 -1
- package/dist/tools/git-history.d.ts +5 -16
- package/dist/tools/git-history.d.ts.map +1 -1
- package/dist/tools/git-history.js +3 -10
- package/dist/tools/git-history.js.map +1 -1
- package/dist/tools/history.d.ts +4 -33
- package/dist/tools/history.d.ts.map +1 -1
- package/dist/tools/history.js +6 -31
- package/dist/tools/history.js.map +1 -1
- package/dist/tools/impact.d.ts +7 -51
- package/dist/tools/impact.d.ts.map +1 -1
- package/dist/tools/impact.js +15 -67
- package/dist/tools/impact.js.map +1 -1
- package/dist/tools/index.d.ts +3 -8
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kit.d.ts +66 -113
- package/dist/tools/kit.d.ts.map +1 -1
- package/dist/tools/kit.js +60 -28
- package/dist/tools/kit.js.map +1 -1
- package/dist/tools/link-workspace.d.ts +7 -45
- package/dist/tools/link-workspace.d.ts.map +1 -1
- package/dist/tools/link-workspace.js +10 -48
- package/dist/tools/link-workspace.js.map +1 -1
- package/dist/tools/lookup.d.ts +4 -14
- package/dist/tools/lookup.d.ts.map +1 -1
- package/dist/tools/lookup.js +4 -14
- package/dist/tools/lookup.js.map +1 -1
- package/dist/tools/mark-incident.d.ts +7 -54
- package/dist/tools/mark-incident.d.ts.map +1 -1
- package/dist/tools/mark-incident.js +15 -68
- package/dist/tools/mark-incident.js.map +1 -1
- package/dist/tools/observe-runtime.d.ts +27 -210
- package/dist/tools/observe-runtime.d.ts.map +1 -1
- package/dist/tools/observe-runtime.js +257 -435
- package/dist/tools/observe-runtime.js.map +1 -1
- package/dist/tools/observe-tests.d.ts +9 -100
- package/dist/tools/observe-tests.d.ts.map +1 -1
- package/dist/tools/observe-tests.js +24 -152
- package/dist/tools/observe-tests.js.map +1 -1
- package/dist/tools/pr-analysis.d.ts +18 -2
- package/dist/tools/pr-analysis.d.ts.map +1 -1
- package/dist/tools/pr-analysis.js +73 -23
- package/dist/tools/pr-analysis.js.map +1 -1
- package/dist/tools/pre-push.d.ts +68 -16
- package/dist/tools/pre-push.d.ts.map +1 -1
- package/dist/tools/pre-push.js +98 -35
- package/dist/tools/pre-push.js.map +1 -1
- package/dist/tools/propagation.d.ts +11 -53
- package/dist/tools/propagation.d.ts.map +1 -1
- package/dist/tools/propagation.js +14 -57
- package/dist/tools/propagation.js.map +1 -1
- package/dist/tools/questions.d.ts +13 -63
- package/dist/tools/questions.d.ts.map +1 -1
- package/dist/tools/questions.js +33 -105
- package/dist/tools/questions.js.map +1 -1
- package/dist/tools/refusal-fetch.d.ts +4 -40
- package/dist/tools/refusal-fetch.d.ts.map +1 -1
- package/dist/tools/refusal-fetch.js +4 -40
- package/dist/tools/refusal-fetch.js.map +1 -1
- package/dist/tools/runtime-incident.d.ts +4 -63
- package/dist/tools/runtime-incident.d.ts.map +1 -1
- package/dist/tools/runtime-incident.js +10 -87
- package/dist/tools/runtime-incident.js.map +1 -1
- package/dist/tools/scope.d.ts +7 -25
- package/dist/tools/scope.d.ts.map +1 -1
- package/dist/tools/scope.js +10 -29
- package/dist/tools/scope.js.map +1 -1
- package/dist/tools/similar-incidents.d.ts +11 -86
- package/dist/tools/similar-incidents.d.ts.map +1 -1
- package/dist/tools/similar-incidents.js +7 -71
- package/dist/tools/similar-incidents.js.map +1 -1
- package/dist/tools/validate.d.ts +14 -69
- package/dist/tools/validate.d.ts.map +1 -1
- package/dist/tools/validate.js +23 -74
- package/dist/tools/validate.js.map +1 -1
- package/dist/tools/verification-status.d.ts +9 -64
- package/dist/tools/verification-status.d.ts.map +1 -1
- package/dist/tools/verification-status.js +9 -62
- package/dist/tools/verification-status.js.map +1 -1
- package/dist/tools/verify-claim.d.ts +5 -52
- package/dist/tools/verify-claim.d.ts.map +1 -1
- package/dist/tools/verify-claim.js +6 -56
- package/dist/tools/verify-claim.js.map +1 -1
- package/dist/transport.d.ts +15 -52
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +16 -60
- package/dist/transport.js.map +1 -1
- package/package.json +30 -21
|
@@ -1,85 +1,27 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Every other tool on this surface is one-shot. It opens what it needs,
|
|
8
|
-
* answers, and holds nothing; a crash in the middle costs the caller one
|
|
9
|
-
* reply. A browser process that outlives a call is a different shape
|
|
10
|
-
* entirely, and the failure it introduces is not "a call fails" but "a
|
|
11
|
-
* process is still running on the developer's machine an hour after the
|
|
12
|
-
* agent stopped caring about it".
|
|
13
|
-
*
|
|
14
|
-
* So the design rule here is that **there is no path on which a session stops
|
|
15
|
-
* being tracked without its browser being closed and its evidence being
|
|
16
|
-
* handed back**. Four paths reach that state — idle timeout, client
|
|
17
|
-
* disconnect, crash, explicit close — and they converge on one private
|
|
18
|
-
* function, `reap`, rather than each doing their own teardown. They were four
|
|
19
|
-
* separate implementations in the first draft and the evidence flush was
|
|
20
|
-
* correct on three of them.
|
|
21
|
-
*
|
|
22
|
-
* ## Evidence is held, not dropped, and not silently discarded either
|
|
23
|
-
*
|
|
24
|
-
* A timeout or a disconnect has no reply to attach evidence to. Discarding it
|
|
25
|
-
* would be the failure rule 7 names — silently doing less — so a reaped
|
|
26
|
-
* session leaves a `ReapedSession` record behind carrying the reason and the
|
|
27
|
-
* final flush, and the next caller who names that id receives it instead of a
|
|
28
|
-
* bare "unknown session". That record is what turns "your session vanished"
|
|
29
|
-
* into "your session was idle for five minutes; here is what it saw before it
|
|
30
|
-
* went".
|
|
31
|
-
*
|
|
32
|
-
* ## Per connection, never global
|
|
33
|
-
*
|
|
34
|
-
* A registry is created per `Session`, which is created per connection. A
|
|
35
|
-
* global one would let a second client `use` a first client's browser by
|
|
36
|
-
* guessing a uuid, and would make the disconnect reap ambiguous about whose
|
|
37
|
-
* sessions it was reaping.
|
|
38
|
-
*/
|
|
1
|
+
/** Live browser sessions belonging to one connection — `mcp-browser-tools.md` §4. Four
|
|
2
|
+
* teardown paths (idle timeout, disconnect, crash, close) converge on one `reap` so a
|
|
3
|
+
* session never stops being tracked without its browser closing and evidence being
|
|
4
|
+
* flushed (kept, never dropped — rule 7). Per-connection, never global — a global
|
|
5
|
+
* registry would let one client guess another's browser by session id. */
|
|
39
6
|
import type { BrowserDriver, BrowserLaunchOptions, BrowserObservation, BrowserPageSession, ChromiumRefusal } from "./driver.ts";
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* A starting value, not a measurement, in this repo's usual sense: no agent
|
|
44
|
-
* has yet been observed pausing mid-exploration, so there is nothing to fit
|
|
45
|
-
* it to. Five minutes is long enough to survive a model thinking, short
|
|
46
|
-
* enough that a forgotten browser is a five-minute leak rather than an
|
|
47
|
-
* overnight one. It is deliberately the same figure as the action handshake's
|
|
48
|
-
* token TTL — an agent that cannot decide within one is unlikely to be coming
|
|
49
|
-
* back for the other.
|
|
50
|
-
*/
|
|
7
|
+
/** How long a session may go untouched before torn down. Starting value, not a
|
|
8
|
+
* measurement — 5 min balances agent-thinking time against leak size; matches the
|
|
9
|
+
* action handshake's token TTL deliberately. */
|
|
51
10
|
export declare const BROWSER_IDLE_TIMEOUT_MS: number;
|
|
52
|
-
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* Small on purpose. A calling agent driving more than a couple of browsers
|
|
56
|
-
* simultaneously is not doing something this surface was built for, and each
|
|
57
|
-
* one is a real Chromium. Three leaves room for the honest case — a
|
|
58
|
-
* comparison between two states, plus one being opened — without letting a
|
|
59
|
-
* loop with a missing `browser_close_session` fill the machine.
|
|
60
|
-
*/
|
|
11
|
+
/** Max sessions per connection. Small on purpose — each is a real Chromium process;
|
|
12
|
+
* 3 leaves room for comparing two states plus one opening, without a loop with a
|
|
13
|
+
* missing `browser_close_session` filling the machine. */
|
|
61
14
|
export declare const MAX_BROWSER_SESSIONS = 3;
|
|
62
15
|
export type ReapReason = "idleTimeout" | "clientDisconnect" | "crashed" | "closed";
|
|
63
|
-
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* A browser session's safety level is the environment profile's — the same
|
|
67
|
-
* field `observe_runtime` already reads. Declaring a second, browser-only
|
|
68
|
-
* notion of "safe" is how a runtime run and a browser run end up disagreeing
|
|
69
|
-
* about the same environment, which is the failure item 27 exists to prevent.
|
|
70
|
-
* Null means no profile was supplied, in which case the session's consent is
|
|
71
|
-
* the only authorization and it governs alone.
|
|
72
|
-
*/
|
|
16
|
+
/** DEC-270's vocabulary. Reuses `observe_runtime`'s environment-profile field rather
|
|
17
|
+
* than a browser-only notion of "safe" (item 27: two definitions could disagree).
|
|
18
|
+
* Null means no profile — session consent alone governs. */
|
|
73
19
|
export type BrowserSafetyLevel = "readOnly" | "write" | "destructiveWithApproval";
|
|
74
20
|
export interface BrowserSessionEntry {
|
|
75
21
|
readonly sessionId: string;
|
|
76
|
-
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* One per session, not one per call: an edge's `observedByRun` answers
|
|
80
|
-
* "which run saw this", and a run that changed identity between a click and
|
|
81
|
-
* the request it caused would make that answer meaningless.
|
|
82
|
-
*/
|
|
22
|
+
/** Run id every R4 edge this session witnesses is stamped with. One per session, not
|
|
23
|
+
* per call — a run changing identity between a click and the request it caused would
|
|
24
|
+
* make `observedByRun` meaningless. */
|
|
83
25
|
readonly runId: string;
|
|
84
26
|
readonly page: BrowserPageSession;
|
|
85
27
|
/** The profile this session was opened under, or null when none was declared. */
|
|
@@ -87,14 +29,9 @@ export interface BrowserSessionEntry {
|
|
|
87
29
|
readonly name: string;
|
|
88
30
|
readonly safetyLevel: BrowserSafetyLevel;
|
|
89
31
|
} | null;
|
|
90
|
-
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* Recorded at open time rather than read from the page later: the consent
|
|
94
|
-
* ruling binds the grant to the origin the developer approved, and a page
|
|
95
|
-
* that has since navigated is exactly the case where re-deriving it would
|
|
96
|
-
* launder a cross-origin move into an approved one.
|
|
97
|
-
*/
|
|
32
|
+
/** Origin `browser_start_session` was consented for, recorded at open time — a page
|
|
33
|
+
* that navigated since would let re-deriving it launder a cross-origin move into an
|
|
34
|
+
* approved one. */
|
|
98
35
|
readonly consentedOrigin: string;
|
|
99
36
|
readonly openedAt: number;
|
|
100
37
|
readonly lastUsedAt: number;
|
|
@@ -140,35 +77,18 @@ export interface BrowserSessionRegistryOptions {
|
|
|
140
77
|
readonly now?: () => number;
|
|
141
78
|
readonly maxSessions?: number;
|
|
142
79
|
readonly idleTimeoutMs?: number;
|
|
143
|
-
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* True in production: a session that goes idle because the agent wandered
|
|
147
|
-
* off is precisely the session that will never trigger a lazy sweep, so the
|
|
148
|
-
* timeout cannot be enforced only on the next call. The timer is `unref`'d,
|
|
149
|
-
* so it never holds the process open by itself.
|
|
150
|
-
*
|
|
151
|
-
* False in tests, which drive `sweepIdle()` against an injected clock —
|
|
152
|
-
* a real timer would make the idle assertions take real minutes.
|
|
153
|
-
*/
|
|
80
|
+
/** Runs the background idle sweep. True in production — an idle session needs a
|
|
81
|
+
* timer since it will never trigger a lazy sweep on its own; timer is `unref`'d.
|
|
82
|
+
* False in tests, which drive `sweepIdle()` against an injected clock. */
|
|
154
83
|
readonly startSweeper?: boolean;
|
|
155
84
|
}
|
|
156
85
|
export interface BrowserSessionRegistry {
|
|
157
86
|
readonly idleTimeoutMs: number;
|
|
158
87
|
readonly maxSessions: number;
|
|
159
88
|
open(driver: BrowserDriver, options: BrowserOpenOptions): Promise<OpenResult>;
|
|
160
|
-
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* Asynchronous because the crash path is not a lookup — it is a teardown.
|
|
165
|
-
* A synchronous `use` would have to either drop the dead page's final
|
|
166
|
-
* evidence or pretend to have collected it, and the first draft of this
|
|
167
|
-
* file did the second: it returned a record it then filled in from a
|
|
168
|
-
* floating promise, so the flush landed after the caller had already read
|
|
169
|
-
* the record. Awaiting the reap here is what makes "a crash flushes what
|
|
170
|
-
* preceded it" true rather than nearly true.
|
|
171
|
-
*/
|
|
89
|
+
/** Looks up a session, touching its idle clock; detects a crash before returning.
|
|
90
|
+
* Async because the crash path is a teardown, not a lookup — a sync version would
|
|
91
|
+
* drop the dead page's evidence or (as the first draft did) flush it too late. */
|
|
172
92
|
use(sessionId: string): Promise<UseResult>;
|
|
173
93
|
close(sessionId: string): Promise<BrowserObservation>;
|
|
174
94
|
sweepIdle(): Promise<readonly ReapedSession[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/browser/registry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/browser/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/browser/registry.js
CHANGED
|
@@ -1,63 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Every other tool on this surface is one-shot. It opens what it needs,
|
|
8
|
-
* answers, and holds nothing; a crash in the middle costs the caller one
|
|
9
|
-
* reply. A browser process that outlives a call is a different shape
|
|
10
|
-
* entirely, and the failure it introduces is not "a call fails" but "a
|
|
11
|
-
* process is still running on the developer's machine an hour after the
|
|
12
|
-
* agent stopped caring about it".
|
|
13
|
-
*
|
|
14
|
-
* So the design rule here is that **there is no path on which a session stops
|
|
15
|
-
* being tracked without its browser being closed and its evidence being
|
|
16
|
-
* handed back**. Four paths reach that state — idle timeout, client
|
|
17
|
-
* disconnect, crash, explicit close — and they converge on one private
|
|
18
|
-
* function, `reap`, rather than each doing their own teardown. They were four
|
|
19
|
-
* separate implementations in the first draft and the evidence flush was
|
|
20
|
-
* correct on three of them.
|
|
21
|
-
*
|
|
22
|
-
* ## Evidence is held, not dropped, and not silently discarded either
|
|
23
|
-
*
|
|
24
|
-
* A timeout or a disconnect has no reply to attach evidence to. Discarding it
|
|
25
|
-
* would be the failure rule 7 names — silently doing less — so a reaped
|
|
26
|
-
* session leaves a `ReapedSession` record behind carrying the reason and the
|
|
27
|
-
* final flush, and the next caller who names that id receives it instead of a
|
|
28
|
-
* bare "unknown session". That record is what turns "your session vanished"
|
|
29
|
-
* into "your session was idle for five minutes; here is what it saw before it
|
|
30
|
-
* went".
|
|
31
|
-
*
|
|
32
|
-
* ## Per connection, never global
|
|
33
|
-
*
|
|
34
|
-
* A registry is created per `Session`, which is created per connection. A
|
|
35
|
-
* global one would let a second client `use` a first client's browser by
|
|
36
|
-
* guessing a uuid, and would make the disconnect reap ambiguous about whose
|
|
37
|
-
* sessions it was reaping.
|
|
38
|
-
*/
|
|
1
|
+
/** Live browser sessions belonging to one connection — `mcp-browser-tools.md` §4. Four
|
|
2
|
+
* teardown paths (idle timeout, disconnect, crash, close) converge on one `reap` so a
|
|
3
|
+
* session never stops being tracked without its browser closing and evidence being
|
|
4
|
+
* flushed (kept, never dropped — rule 7). Per-connection, never global — a global
|
|
5
|
+
* registry would let one client guess another's browser by session id. */
|
|
39
6
|
import { EMPTY_OBSERVATION } from "./driver.js";
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* A starting value, not a measurement, in this repo's usual sense: no agent
|
|
44
|
-
* has yet been observed pausing mid-exploration, so there is nothing to fit
|
|
45
|
-
* it to. Five minutes is long enough to survive a model thinking, short
|
|
46
|
-
* enough that a forgotten browser is a five-minute leak rather than an
|
|
47
|
-
* overnight one. It is deliberately the same figure as the action handshake's
|
|
48
|
-
* token TTL — an agent that cannot decide within one is unlikely to be coming
|
|
49
|
-
* back for the other.
|
|
50
|
-
*/
|
|
7
|
+
/** How long a session may go untouched before torn down. Starting value, not a
|
|
8
|
+
* measurement — 5 min balances agent-thinking time against leak size; matches the
|
|
9
|
+
* action handshake's token TTL deliberately. */
|
|
51
10
|
export const BROWSER_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
52
|
-
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* Small on purpose. A calling agent driving more than a couple of browsers
|
|
56
|
-
* simultaneously is not doing something this surface was built for, and each
|
|
57
|
-
* one is a real Chromium. Three leaves room for the honest case — a
|
|
58
|
-
* comparison between two states, plus one being opened — without letting a
|
|
59
|
-
* loop with a missing `browser_close_session` fill the machine.
|
|
60
|
-
*/
|
|
11
|
+
/** Max sessions per connection. Small on purpose — each is a real Chromium process;
|
|
12
|
+
* 3 leaves room for comparing two states plus one opening, without a loop with a
|
|
13
|
+
* missing `browser_close_session` filling the machine. */
|
|
61
14
|
export const MAX_BROWSER_SESSIONS = 3;
|
|
62
15
|
const SWEEP_INTERVAL_MS = 30_000;
|
|
63
16
|
function originOf(url) {
|
|
@@ -65,9 +18,8 @@ function originOf(url) {
|
|
|
65
18
|
return new URL(url).origin;
|
|
66
19
|
}
|
|
67
20
|
catch {
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// comparison total rather than throwing from a bookkeeping call.
|
|
21
|
+
// Unparseable target already refused at the schema; return the raw string so
|
|
22
|
+
// consent comparison stays total instead of throwing from a bookkeeping call.
|
|
71
23
|
return url;
|
|
72
24
|
}
|
|
73
25
|
}
|
|
@@ -77,14 +29,9 @@ export function createBrowserSessionRegistry(options = {}) {
|
|
|
77
29
|
const idleTimeoutMs = options.idleTimeoutMs ?? BROWSER_IDLE_TIMEOUT_MS;
|
|
78
30
|
const live = new Map();
|
|
79
31
|
const reapedSessions = new Map();
|
|
80
|
-
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* `close()` on the port returns the final flush and is idempotent, so a
|
|
85
|
-
* crashed session still yields whatever it had buffered — which is the case
|
|
86
|
-
* that matters most, the page's last words before it died.
|
|
87
|
-
*/
|
|
32
|
+
/** The one teardown; every path routes through here so "closed" and "evidence kept"
|
|
33
|
+
* cannot come apart. `close()` on the port is idempotent, so even a crashed session
|
|
34
|
+
* yields its last buffered evidence. */
|
|
88
35
|
async function reap(sessionId, session, reason) {
|
|
89
36
|
live.delete(sessionId);
|
|
90
37
|
let observation = EMPTY_OBSERVATION;
|
|
@@ -92,9 +39,8 @@ export function createBrowserSessionRegistry(options = {}) {
|
|
|
92
39
|
observation = await session.page.close();
|
|
93
40
|
}
|
|
94
41
|
catch {
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
// what matters is that this id is now unusable and says why.
|
|
42
|
+
// Teardown that throws must not leave the session tracked or lose the record —
|
|
43
|
+
// the browser may be gone already; this id is now unusable and says why.
|
|
98
44
|
}
|
|
99
45
|
const record = { sessionId, reason, observation, at: now() };
|
|
100
46
|
reapedSessions.set(sessionId, record);
|
|
@@ -115,9 +61,7 @@ export function createBrowserSessionRegistry(options = {}) {
|
|
|
115
61
|
return {
|
|
116
62
|
opened: false,
|
|
117
63
|
kind: "atCapacity",
|
|
118
|
-
// Both numbers
|
|
119
|
-
// something a caller can act on and "3 of 3" is — it tells them
|
|
120
|
-
// whether to close one or to stop opening them in a loop.
|
|
64
|
+
// Both numbers so the caller knows whether to close one or stop opening in a loop.
|
|
121
65
|
reason: `This connection already has ${live.size} of ${maxSessions} browser sessions open. Close one with ` +
|
|
122
66
|
"browser_close_session before starting another. The cap is per connection and exists because each " +
|
|
123
67
|
"session is a real browser process.",
|
|
@@ -155,8 +99,7 @@ export function createBrowserSessionRegistry(options = {}) {
|
|
|
155
99
|
};
|
|
156
100
|
}
|
|
157
101
|
if (!session.page.isUsable()) {
|
|
158
|
-
//
|
|
159
|
-
// cannot be the one where the flush is skipped.
|
|
102
|
+
// Same teardown as every other path, so the crash case can't skip the flush.
|
|
160
103
|
const record = await reap(sessionId, session, "crashed");
|
|
161
104
|
return { ok: false, kind: "crashed", reason: crashReason(sessionId), reaped: record };
|
|
162
105
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/browser/registry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/browser/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,43 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `role` + `name` + `selector` and no `ref`, because a ref is scoped to the
|
|
7
|
-
* snapshot that issued it and a replay is always a later session. So every
|
|
8
|
-
* step re-resolves against a fresh snapshot, and the resolution is
|
|
9
|
-
* **exact-or-refuse**: exactly one element matching all three fields, or stop.
|
|
10
|
-
*
|
|
11
|
-
* **There is deliberately no fallback chain.** "Try the selector, else
|
|
12
|
-
* role+name" is the dangerous option and it is dangerous in a specific way: it
|
|
13
|
-
* turns a real disagreement into a quiet substitution, which is how a scenario
|
|
14
|
-
* keeps reporting green against a page that changed underneath it. A
|
|
15
|
-
* disagreement between the three fields is the highest-value signal this
|
|
16
|
-
* format can produce, and a fallback is exactly the code that throws it away.
|
|
17
|
-
*
|
|
18
|
-
* The refusal messages are load-bearing rather than decorative, per
|
|
19
|
-
* `DEC-384`: a stopped push that says "step 3
|
|
20
|
-
* failed to resolve" is what developers route around, and one that says "the
|
|
21
|
-
* button is still there; its accessible name changed from Delete to Remove" is
|
|
22
|
-
* a one-line fix. So every refusal names which step, which descriptor, which
|
|
23
|
-
* of the three fields disagreed, and what the page holds now.
|
|
24
|
-
*
|
|
25
|
-
* Lives beside the port rather than in `scenarios/`, on purpose: the format
|
|
26
|
-
* module imports nothing from the driver and is tested without a browser in
|
|
27
|
-
* the process, and this function needs a `PageSnapshot`.
|
|
28
|
-
*/
|
|
1
|
+
/** Turning a stored target descriptor back into something clickable — DEC-383 §2/§3. Stored
|
|
2
|
+
* steps carry role+name+selector, no `ref` (scoped to the issuing snapshot); resolution is
|
|
3
|
+
* exact-or-refuse against a fresh snapshot, deliberately no fallback chain — substituting one
|
|
4
|
+
* matching field for another turns a real disagreement into a quiet one, and the scenario
|
|
5
|
+
* keeps reporting green against a changed page. Refusal messages name the exact disagreement (DEC-384). */
|
|
29
6
|
import type { TargetDescriptor } from "../scenarios/index.ts";
|
|
30
7
|
import type { PageSnapshot, SnapshotElement } from "./driver.ts";
|
|
31
8
|
export type DescriptorField = "role" | "name" | "selector";
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* Three kinds, kept apart because they send a developer to three different
|
|
36
|
-
* places. `zero` — nothing on the page answers to this at all. `ambiguous` —
|
|
37
|
-
* several do, and picking one would be reporting on a run nobody chose.
|
|
38
|
-
* `partial` — something is still there and one part of the descriptor has
|
|
39
|
-
* moved, which is the case worth the most and the reason `disagreed` exists.
|
|
40
|
-
*/
|
|
9
|
+
/** Why a descriptor failed to resolve. `zero` — nothing matches at all. `ambiguous` —
|
|
10
|
+
* several do, picking one would report on an unchosen run. `partial` — something still
|
|
11
|
+
* matches part of it, the case `disagreed` exists for. */
|
|
41
12
|
export interface ResolutionFailure {
|
|
42
13
|
readonly kind: "zero" | "ambiguous" | "partial";
|
|
43
14
|
/** Which of role/name/selector no longer agree. Empty for `zero` and `ambiguous`, where nothing partial matched to disagree with. */
|
|
@@ -51,12 +22,7 @@ export type TargetResolution = {
|
|
|
51
22
|
readonly resolved: false;
|
|
52
23
|
readonly failure: ResolutionFailure;
|
|
53
24
|
};
|
|
54
|
-
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* `where` is the caller's label for the thing being resolved — `"step 0"`,
|
|
58
|
-
* `"assertion 2"` — and it is required rather than optional because a refusal
|
|
59
|
-
* that does not say *which* step is the version this ruling exists to avoid.
|
|
60
|
-
*/
|
|
25
|
+
/** Resolves one stored descriptor against a fresh snapshot. `where` labels what's being
|
|
26
|
+
* resolved ("step 0") and is required — a refusal not saying which step is the failure this exists to avoid. */
|
|
61
27
|
export declare function resolveTarget(where: string, target: TargetDescriptor, snapshot: PageSnapshot): TargetResolution;
|
|
62
28
|
//# sourceMappingURL=scenario-resolve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario-resolve.d.ts","sourceRoot":"","sources":["../../src/browser/scenario-resolve.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"scenario-resolve.d.ts","sourceRoot":"","sources":["../../src/browser/scenario-resolve.ts"],"names":[],"mappings":"AAAA;;;;4GAI4G;AAE5G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAE3D;;2DAE2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IAChD,qIAAqI;IACrI,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAuBtE;iHACiH;AACjH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,GAAG,gBAAgB,CA6E/G"}
|
|
@@ -1,31 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `role` + `name` + `selector` and no `ref`, because a ref is scoped to the
|
|
7
|
-
* snapshot that issued it and a replay is always a later session. So every
|
|
8
|
-
* step re-resolves against a fresh snapshot, and the resolution is
|
|
9
|
-
* **exact-or-refuse**: exactly one element matching all three fields, or stop.
|
|
10
|
-
*
|
|
11
|
-
* **There is deliberately no fallback chain.** "Try the selector, else
|
|
12
|
-
* role+name" is the dangerous option and it is dangerous in a specific way: it
|
|
13
|
-
* turns a real disagreement into a quiet substitution, which is how a scenario
|
|
14
|
-
* keeps reporting green against a page that changed underneath it. A
|
|
15
|
-
* disagreement between the three fields is the highest-value signal this
|
|
16
|
-
* format can produce, and a fallback is exactly the code that throws it away.
|
|
17
|
-
*
|
|
18
|
-
* The refusal messages are load-bearing rather than decorative, per
|
|
19
|
-
* `DEC-384`: a stopped push that says "step 3
|
|
20
|
-
* failed to resolve" is what developers route around, and one that says "the
|
|
21
|
-
* button is still there; its accessible name changed from Delete to Remove" is
|
|
22
|
-
* a one-line fix. So every refusal names which step, which descriptor, which
|
|
23
|
-
* of the three fields disagreed, and what the page holds now.
|
|
24
|
-
*
|
|
25
|
-
* Lives beside the port rather than in `scenarios/`, on purpose: the format
|
|
26
|
-
* module imports nothing from the driver and is tested without a browser in
|
|
27
|
-
* the process, and this function needs a `PageSnapshot`.
|
|
28
|
-
*/
|
|
1
|
+
/** Turning a stored target descriptor back into something clickable — DEC-383 §2/§3. Stored
|
|
2
|
+
* steps carry role+name+selector, no `ref` (scoped to the issuing snapshot); resolution is
|
|
3
|
+
* exact-or-refuse against a fresh snapshot, deliberately no fallback chain — substituting one
|
|
4
|
+
* matching field for another turns a real disagreement into a quiet one, and the scenario
|
|
5
|
+
* keeps reporting green against a changed page. Refusal messages name the exact disagreement (DEC-384). */
|
|
29
6
|
const FIELDS = ["role", "name", "selector"];
|
|
30
7
|
function describe(target) {
|
|
31
8
|
return `role "${target.role}", name "${target.name}", selector "${target.selector}"`;
|
|
@@ -44,13 +21,8 @@ function pageContents(snapshot, limit = 8) {
|
|
|
44
21
|
function disagreementsWith(target, element) {
|
|
45
22
|
return FIELDS.filter((field) => element[field] !== target[field]);
|
|
46
23
|
}
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* `where` is the caller's label for the thing being resolved — `"step 0"`,
|
|
51
|
-
* `"assertion 2"` — and it is required rather than optional because a refusal
|
|
52
|
-
* that does not say *which* step is the version this ruling exists to avoid.
|
|
53
|
-
*/
|
|
24
|
+
/** Resolves one stored descriptor against a fresh snapshot. `where` labels what's being
|
|
25
|
+
* resolved ("step 0") and is required — a refusal not saying which step is the failure this exists to avoid. */
|
|
54
26
|
export function resolveTarget(where, target, snapshot) {
|
|
55
27
|
const exact = snapshot.elements.filter((e) => e.role === target.role && e.name === target.name && e.selector === target.selector);
|
|
56
28
|
if (exact.length === 1)
|
|
@@ -81,10 +53,8 @@ export function resolveTarget(where, target, snapshot) {
|
|
|
81
53
|
},
|
|
82
54
|
};
|
|
83
55
|
}
|
|
84
|
-
// The partial case
|
|
85
|
-
//
|
|
86
|
-
// at something AND an element with this role and name still exists" is two
|
|
87
|
-
// candidates and a person is the one who knows which is theirs.
|
|
56
|
+
// The partial case — why the format stores all three fields. Both halves are reported
|
|
57
|
+
// when both matched, since two candidates exist and only a person knows which is theirs.
|
|
88
58
|
const parts = [];
|
|
89
59
|
const disagreed = new Set();
|
|
90
60
|
if (bySelector.length === 1) {
|
|
@@ -114,8 +84,7 @@ export function resolveTarget(where, target, snapshot) {
|
|
|
114
84
|
resolved: false,
|
|
115
85
|
failure: {
|
|
116
86
|
kind: "partial",
|
|
117
|
-
// Ordered
|
|
118
|
-
// is comparing sets rather than orderings.
|
|
87
|
+
// Ordered like the descriptor, so callers compare sets, not orderings.
|
|
119
88
|
disagreed: FIELDS.filter((field) => disagreed.has(field)),
|
|
120
89
|
message: `${where}: ${describe(target)} does not match exactly one element on ${snapshot.url}, but part of it ` +
|
|
121
90
|
`still does — ${parts.join("; ")}. The step is not run: substituting the half that still matches would ` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario-resolve.js","sourceRoot":"","sources":["../../src/browser/scenario-resolve.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"scenario-resolve.js","sourceRoot":"","sources":["../../src/browser/scenario-resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,44 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Everything below is composition: the port drives, `scenarios/` is the
|
|
7
|
-
* artifact, `scenario-resolve.ts` re-resolves a descriptor, `tool-support.ts`
|
|
8
|
-
* drains and correlates. What the runner adds is *order*, and four ordering
|
|
9
|
-
* mistakes each fail as flakiness or as a false pass rather than as an error,
|
|
10
|
-
* which is why each is named here rather than left to be rediscovered.
|
|
11
|
-
*
|
|
12
|
-
* 1. **`registry.use()` is called once per step.** It is not a lookup: it is
|
|
13
|
-
* asynchronous, it is the crash detector, and it touches the idle clock. A
|
|
14
|
-
* runner that resolved the session once and held the entry across N steps
|
|
15
|
-
* would be reaped mid-scenario at the five-minute idle timeout, and would
|
|
16
|
-
* read as a flaky test rather than as a design error.
|
|
17
|
-
* 2. **`drainEvidence()` drains, it does not peek.** Once per step, aggregated
|
|
18
|
-
* into the summary. A single read at the end loses every earlier step's
|
|
19
|
-
* observations — and would silently pass an assertion anchored at step 0,
|
|
20
|
-
* which is the false green this whole feature exists to prevent.
|
|
21
|
-
* 3. **A stored step carries no `ref`.** Every step re-resolves against a
|
|
22
|
-
* fresh snapshot, exact-or-refuse, with no fallback chain. See
|
|
23
|
-
* `scenario-resolve.ts`.
|
|
24
|
-
* 4. **Assertions evaluate here, at replay.** An empty assertion list replays
|
|
25
|
-
* as `unverified`, never `passed`.
|
|
26
|
-
*
|
|
27
|
-
* ## The outcome rule
|
|
28
|
-
*
|
|
29
|
-
* `DEC-384`, implemented rather than restated. A
|
|
30
|
-
* failed assertion is the scenario's own claim about the application, written
|
|
31
|
-
* down by a person, and it blocks. A console error or a failed request is
|
|
32
|
-
* reported and does not block — frequently true of a working page, and a check
|
|
33
|
-
* people disable protects nothing. No assertions is `unverified`. An
|
|
34
|
-
* unresolvable step is **neither pass nor fail**: Descry cannot tell a
|
|
35
|
-
* deliberate rename from a vanished button, and both produce a byte-identical
|
|
36
|
-
* unresolvable step, so it claims neither while still stopping the run.
|
|
37
|
-
*
|
|
38
|
-
* Three counts, **never summed** — `failed`, `unresolved`, `unverified` — and
|
|
39
|
-
* only failed assertions join `blockingTotal`, because folding `unresolved`
|
|
40
|
-
* into it would re-assert through arithmetic the defect claim the ruling
|
|
41
|
-
* refused to make in prose.
|
|
2
|
+
* Replays a stored scenario (§7 runner). DEC-384: failed assertion blocks; console
|
|
3
|
+
* error/failed request doesn't; no assertions is `unverified`; unresolvable step is
|
|
4
|
+
* neither pass nor fail. `registry.use()` and evidence draining run once per step only.
|
|
42
5
|
*/
|
|
43
6
|
import type { Scenario, ScenarioActionKind, ScenarioAssertionKind } from "../scenarios/index.ts";
|
|
44
7
|
import type { ToolContext } from "../tools/kit.ts";
|
|
@@ -59,18 +22,9 @@ export interface ScenarioAssertionResult {
|
|
|
59
22
|
readonly index: number;
|
|
60
23
|
readonly kind: ScenarioAssertionKind;
|
|
61
24
|
readonly afterStep: number | null;
|
|
62
|
-
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* `unevaluable` is a hole in the stored artifact — a `textEquals` with
|
|
67
|
-
* nothing to compare against. `notReached` is an assertion anchored at a
|
|
68
|
-
* step the replay never got to. Neither is a defect claim about the
|
|
69
|
-
* application, so neither may be reported as `failed`; only `unevaluable`
|
|
70
|
-
* joins `unresolved`, because `notReached` is already accounted for by the
|
|
71
|
-
* unresolved step that stopped the run and counting it twice would inflate
|
|
72
|
-
* a count the ruling requires to stay separate.
|
|
73
|
-
*/
|
|
25
|
+
/** `unevaluable` = hole in the artifact (e.g. missing comparison value); `notReached`
|
|
26
|
+
* = anchored past where replay stopped. Neither is a defect claim; only
|
|
27
|
+
* `unevaluable` joins `unresolved` (notReached is already counted there). */
|
|
74
28
|
readonly status: "passed" | "failed" | "unevaluable" | "notReached";
|
|
75
29
|
readonly detail: string;
|
|
76
30
|
}
|
|
@@ -87,16 +41,9 @@ export interface ScenarioReplay {
|
|
|
87
41
|
readonly counts: ScenarioCounts;
|
|
88
42
|
/** Only failed assertions. See this file's header for why `unresolved` is not folded in. */
|
|
89
43
|
readonly blockingTotal: number;
|
|
90
|
-
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* The exit ruling governs the agentless CLI and explicitly does not govern
|
|
95
|
-
* MCP replies, so this is data on the reply rather than a verdict in it. It
|
|
96
|
-
* is computed in the one place that knows the outcome so the CLI box does
|
|
97
|
-
* not become a second implementation of the same rule, drifting from this
|
|
98
|
-
* one the first time either changes.
|
|
99
|
-
*/
|
|
44
|
+
/** 0 clean, 1 blocking findings, 2 could-not-run — `descry check`'s exit vocabulary,
|
|
45
|
+
* computed here (not in the CLI) so the two never drift. Governs the CLI only;
|
|
46
|
+
* this reply carries it as data, not a verdict. */
|
|
100
47
|
readonly exitCode: 0 | 1 | 2;
|
|
101
48
|
readonly steps: readonly ScenarioStepResult[];
|
|
102
49
|
readonly assertions: readonly ScenarioAssertionResult[];
|
|
@@ -105,14 +52,9 @@ export interface ScenarioReplay {
|
|
|
105
52
|
readonly graph: BrowserGraphWrite;
|
|
106
53
|
/** Set when the session went away mid-replay — a crash, a reap, a disconnect. Null on a replay that ran to its own end. */
|
|
107
54
|
readonly sessionEnded: DeadSession | null;
|
|
108
|
-
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* Reported rather than folded into `outcome` alone, because "stopped because
|
|
112
|
-
* the caller cancelled" and "stopped because a step would not resolve" are
|
|
113
|
-
* different facts about the run and only one of them says anything about the
|
|
114
|
-
* application.
|
|
115
|
-
*/
|
|
55
|
+
/** True when the caller's own signal aborted mid-replay — distinct from `outcome`
|
|
56
|
+
* because "caller cancelled" vs "a step wouldn't resolve" are different facts,
|
|
57
|
+
* and only one says anything about the application. */
|
|
116
58
|
readonly cancelled: boolean;
|
|
117
59
|
readonly notes: readonly string[];
|
|
118
60
|
}
|