@unfenced-ai/sdk 0.1.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/LICENSE +30 -0
- package/README.md +171 -0
- package/dist/client.d.ts +385 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +349 -0
- package/dist/client.js.map +1 -0
- package/dist/credentials.d.ts +14 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +17 -0
- package/dist/credentials.js.map +1 -0
- package/dist/fetch.d.ts +16 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +387 -0
- package/dist/fetch.js.map +1 -0
- package/dist/http.d.ts +76 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +250 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/library.d.ts +17 -0
- package/dist/library.d.ts.map +1 -0
- package/dist/library.js +25 -0
- package/dist/library.js.map +1 -0
- package/dist/memory.d.ts +13 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +45 -0
- package/dist/memory.js.map +1 -0
- package/dist/permissions.d.ts +29 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +69 -0
- package/dist/permissions.js.map +1 -0
- package/dist/protocol.d.ts +296 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +89 -0
- package/dist/protocol.js.map +1 -0
- package/dist/session.d.ts +97 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +88 -0
- package/dist/session.js.map +1 -0
- package/dist/sessions.d.ts +131 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +197 -0
- package/dist/sessions.js.map +1 -0
- package/dist/token-usage.d.ts +11 -0
- package/dist/token-usage.d.ts.map +1 -0
- package/dist/token-usage.js +34 -0
- package/dist/token-usage.js.map +1 -0
- package/dist/types.d.ts +1409 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/url-identity.d.ts +8 -0
- package/dist/url-identity.d.ts.map +1 -0
- package/dist/url-identity.js +18 -0
- package/dist/url-identity.js.map +1 -0
- package/package.json +44 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,1409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public domain and wire types of the Unfenced SDK.
|
|
3
|
+
*
|
|
4
|
+
* Split out of the client so a consumer that only wants the shapes — a dashboard
|
|
5
|
+
* that may take no runtime dependency on this package — can `import type` them,
|
|
6
|
+
* and so the client modules share one copy. Every name here is re-exported
|
|
7
|
+
* unchanged from the package entry (`index.ts`); this is a move, not a redesign.
|
|
8
|
+
*/
|
|
9
|
+
import type { ExtractedDoc, FetchFailureMeta, FetchMeta, OutputFormat, Tier } from "./protocol.js";
|
|
10
|
+
export interface UnfencedOptions {
|
|
11
|
+
/** Where the server is. Defaults to http://127.0.0.1:8787. */
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
/** The access token, if the server sets UNFENCED_TOKEN. */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Injectable fetch (for tests / non-standard runtimes). Defaults to global.
|
|
17
|
+
*
|
|
18
|
+
* Declared as what this client actually calls rather than as `typeof fetch`.
|
|
19
|
+
* It only ever passes a string URL, so demanding a function that also handles
|
|
20
|
+
* `URL` and `Request` objects asked callers to satisfy a contract the library
|
|
21
|
+
* never exercises — and made an ordinary test double, typed by the one shape
|
|
22
|
+
* it receives, fail to typecheck. The global `fetch` still satisfies this.
|
|
23
|
+
*/
|
|
24
|
+
fetch?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
25
|
+
/** Extra headers sent on every request — e.g. to attribute the calling client. */
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* Cancel every request this client makes.
|
|
29
|
+
*
|
|
30
|
+
* Combined with each call's own deadline rather than replacing it, so a
|
|
31
|
+
* caller that aborts a whole task does not have to reach into per-call
|
|
32
|
+
* options, and a per-call `timeoutMs` still bounds a request this never
|
|
33
|
+
* aborts.
|
|
34
|
+
*/
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}
|
|
37
|
+
/** The protocol calls this `OutputFormat`; `Format` is the name the SDK shipped. */
|
|
38
|
+
export type Format = OutputFormat;
|
|
39
|
+
export interface FetchOptions {
|
|
40
|
+
format?: Format;
|
|
41
|
+
/**
|
|
42
|
+
* Stable creation key for safe replay if the POST acknowledgement is lost.
|
|
43
|
+
* When omitted, the SDK generates one per fetch call and reuses it only for
|
|
44
|
+
* its own bounded transport retry. Batch calls derive a per-URL suffix from
|
|
45
|
+
* this base key. The server retains keys for the job's normal 30-minute
|
|
46
|
+
* result lifetime, process-locally.
|
|
47
|
+
*/
|
|
48
|
+
idempotencyKey?: string;
|
|
49
|
+
/** Batch only: maximum concurrent fetch jobs, 1-32. Default 3, matching the production tenant share. */
|
|
50
|
+
concurrency?: number;
|
|
51
|
+
/** Skip straight to a rendered browser instead of trying plain HTTP first. */
|
|
52
|
+
forceRender?: boolean;
|
|
53
|
+
/** Route this fetch through a proxy, e.g. "http://user:pass@host:8080". */
|
|
54
|
+
proxy?: string;
|
|
55
|
+
/**
|
|
56
|
+
* How long this fetch may take in total, in ms. Default 120s.
|
|
57
|
+
* Integer 0..2147483647. Zero returns timeout without submitting a job;
|
|
58
|
+
* invalid durations reject with RangeError before work starts.
|
|
59
|
+
*
|
|
60
|
+
* The WALL CLOCK, not the gap between polls. It used to bound only the poll
|
|
61
|
+
* loop's own check, which ran after each request returned — so a hung `POST
|
|
62
|
+
* /jobs` never reached the check at all and a hung `GET /jobs/:id` blocked
|
|
63
|
+
* forever. Against this repository's own documented failure (a Cloudflare
|
|
64
|
+
* quick tunnel dead at the edge while cloudflared is alive) `timeoutMs: 5000`
|
|
65
|
+
* meant "until the OS gives up". Every request now carries an AbortSignal cut
|
|
66
|
+
* from the remaining budget.
|
|
67
|
+
*/
|
|
68
|
+
timeoutMs?: number;
|
|
69
|
+
/** Ignore our own back-off for this host — for a deliberate retest. */
|
|
70
|
+
force?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Whether to tell the site what we are.
|
|
73
|
+
*
|
|
74
|
+
* `auto` (default) uses what has been learned about this host: sites that
|
|
75
|
+
* serve agents a purpose-built rendering get told, sites that refuse bots do
|
|
76
|
+
* not. Some publishers hand a declared agent strictly more than they hand a
|
|
77
|
+
* browser — g2 answers one with markdown carrying scores and summaries the
|
|
78
|
+
* human page does not show.
|
|
79
|
+
*/
|
|
80
|
+
identify?: "auto" | "agent" | "stealth";
|
|
81
|
+
/** Ignore robots.txt, report its verdict, or obey it. Defaults to the identity-aware server policy. */
|
|
82
|
+
robots?: "ignore" | "report" | "obey";
|
|
83
|
+
/** Cap the content at this many words, counted in the page's own script. */
|
|
84
|
+
maxWords?: number;
|
|
85
|
+
/**
|
|
86
|
+
* The language to present, e.g. "en-US" (default), "he-IL", "ja-JP".
|
|
87
|
+
*
|
|
88
|
+
* Sets `Accept-Language` and the browser's own language, so the site chooses
|
|
89
|
+
* content in it. Without it the worker's geography decided: the same call
|
|
90
|
+
* answered in Hebrew from one worker and English from another, which is
|
|
91
|
+
* irreproducible and invisible to the caller.
|
|
92
|
+
*/
|
|
93
|
+
locale?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Collect links from the whole page rather than just the extracted article.
|
|
96
|
+
*
|
|
97
|
+
* Crawling needs the navigation the cleaner strips: a ministry front page
|
|
98
|
+
* yields six links from its content region and dozens from the page.
|
|
99
|
+
*/
|
|
100
|
+
wholePageLinks?: boolean;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* What a fetch actually returns, which is more than this used to say.
|
|
104
|
+
*
|
|
105
|
+
* The client passes the server's job result through untouched, and that result
|
|
106
|
+
* carries six fields — verified over the wire: `ok, url, format, content, doc,
|
|
107
|
+
* meta`. This type declared three of them. The missing one that matters is
|
|
108
|
+
* `content`: the clean page text the call was made to get. A consumer writing
|
|
109
|
+
* TypeScript could not reach `result.content` without a cast, on the primary
|
|
110
|
+
* method of the client library, while it sat there at runtime the whole time.
|
|
111
|
+
*
|
|
112
|
+
* The failure branch was thinner still, and it contradicted a promise the
|
|
113
|
+
* product makes in its own source: "an agent must be able to tell 'the site
|
|
114
|
+
* blocked us' from 'the page is empty' — every failure says which tier failed
|
|
115
|
+
* and why, in machine-readable form". `tier` was not in the type, so it did not
|
|
116
|
+
* say so here.
|
|
117
|
+
*
|
|
118
|
+
* `url`, `tier` and `status` are optional on a failure because not every
|
|
119
|
+
* failure comes from the server. A request rejected before it was sent, a job
|
|
120
|
+
* that never finished — those are the client's own, and they have no tier to
|
|
121
|
+
* report. Declaring them required would be the mirror of this bug: a type
|
|
122
|
+
* promising a field that is sometimes absent.
|
|
123
|
+
*/
|
|
124
|
+
/** Whose fault it was. Mirrors the engine's own `Fault`. */
|
|
125
|
+
export type Fault = "site" | "network" | "us" | "caller" | "policy";
|
|
126
|
+
/** A door the server found and did not open. Mirrors the engine's `Offer`. */
|
|
127
|
+
export interface Offer {
|
|
128
|
+
need: "payment" | "compute" | "human";
|
|
129
|
+
because: string;
|
|
130
|
+
site: string;
|
|
131
|
+
ticket: string;
|
|
132
|
+
expiresAt: string;
|
|
133
|
+
expiresInMs: number;
|
|
134
|
+
quote: {
|
|
135
|
+
micros: number;
|
|
136
|
+
cents: number;
|
|
137
|
+
currency: "USD";
|
|
138
|
+
estimateMs: number;
|
|
139
|
+
payee: "unfenced" | "site";
|
|
140
|
+
capped: boolean;
|
|
141
|
+
};
|
|
142
|
+
accept: {
|
|
143
|
+
kind: "call";
|
|
144
|
+
tool: string;
|
|
145
|
+
arguments: Record<string, unknown>;
|
|
146
|
+
} | {
|
|
147
|
+
kind: "visit";
|
|
148
|
+
url: string;
|
|
149
|
+
why: string;
|
|
150
|
+
};
|
|
151
|
+
for: {
|
|
152
|
+
url: string;
|
|
153
|
+
title: string | null;
|
|
154
|
+
why: string;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Four outcomes: delivered content, a refusal, an offer, or an archived
|
|
159
|
+
* receipt whose full content has expired.
|
|
160
|
+
*
|
|
161
|
+
* `ok` is gone rather than kept alongside `outcome`. Keeping it would compile
|
|
162
|
+
* everywhere and route every offer into whichever branch each caller already
|
|
163
|
+
* had for failure — silently, and discovered by whoever is holding the bill.
|
|
164
|
+
*/
|
|
165
|
+
export type FetchResult = {
|
|
166
|
+
outcome: "delivered";
|
|
167
|
+
/** The URL as asked for; `meta.finalUrl` is where it ended up. */
|
|
168
|
+
url: string;
|
|
169
|
+
format: Format;
|
|
170
|
+
/** The page, in the requested format. The reason for the call. */
|
|
171
|
+
content: string;
|
|
172
|
+
doc: ExtractedDoc;
|
|
173
|
+
meta: FetchMeta;
|
|
174
|
+
} | {
|
|
175
|
+
outcome: "offered";
|
|
176
|
+
url: string;
|
|
177
|
+
offer: Offer;
|
|
178
|
+
/** What was visible without paying: the abstract, page one, the teaser. */
|
|
179
|
+
partial?: ExtractedDoc;
|
|
180
|
+
meta?: FetchMeta;
|
|
181
|
+
} | {
|
|
182
|
+
outcome: "archived";
|
|
183
|
+
url: string;
|
|
184
|
+
/** The full result aged out; only the small history receipt remains. */
|
|
185
|
+
entry: {
|
|
186
|
+
id: string;
|
|
187
|
+
url: string;
|
|
188
|
+
ok: boolean;
|
|
189
|
+
fetchedAt: string;
|
|
190
|
+
title?: string;
|
|
191
|
+
error?: string;
|
|
192
|
+
[field: string]: unknown;
|
|
193
|
+
};
|
|
194
|
+
} | {
|
|
195
|
+
outcome: "failed";
|
|
196
|
+
error: string;
|
|
197
|
+
fault?: Fault;
|
|
198
|
+
detail?: string;
|
|
199
|
+
/** Server advice for a refused request, when one was supplied. */
|
|
200
|
+
remedy?: string;
|
|
201
|
+
/** Server-supplied delay before retrying, in milliseconds. */
|
|
202
|
+
retryAfterMs?: number;
|
|
203
|
+
url?: string;
|
|
204
|
+
/** Which tier failed. Absent on a failure the client produced itself. */
|
|
205
|
+
tier?: Tier;
|
|
206
|
+
/** The HTTP status, when the site answered with one. */
|
|
207
|
+
status?: number;
|
|
208
|
+
/**
|
|
209
|
+
* Not `Partial<FetchMeta>`: a failure carries `retryAfter`/`retryAfterMs`,
|
|
210
|
+
* which a success never does, and carries none of the fields that only
|
|
211
|
+
* exist once a document was produced. Still optional, because a failure
|
|
212
|
+
* the client produced itself never reached the server and has no meta.
|
|
213
|
+
*/
|
|
214
|
+
meta?: FetchFailureMeta;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* A pointer action addressed by the words visible on the target, rather than by a
|
|
218
|
+
* ref from a snapshot.
|
|
219
|
+
*
|
|
220
|
+
* For the choices a page renders without declaring them as controls — they appear
|
|
221
|
+
* in `excerpt` and never in `controls`, so no ref exists for them. The engine
|
|
222
|
+
* resolves the words to an ordinary ref before any guard runs, so these are
|
|
223
|
+
* gated exactly as a ref-addressed action is.
|
|
224
|
+
*/
|
|
225
|
+
/** A spot inside a target, as fractions from 0 to 1 of its box. */
|
|
226
|
+
export interface Spot {
|
|
227
|
+
x: number;
|
|
228
|
+
y: number;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* A pointer action aimed at a SPOT rather than at a thing.
|
|
232
|
+
*
|
|
233
|
+
* For anything whose meaning is a position: a slider track, a video scrubber, a
|
|
234
|
+
* canvas, a map, a chart. Combine `at` with `on` or `ref` to say which thing;
|
|
235
|
+
* with neither it is a fraction of the window. The engine hit-tests the pixel and
|
|
236
|
+
* judges whatever is actually under it, so these are gated at least as strictly
|
|
237
|
+
* as a ref-addressed action.
|
|
238
|
+
*/
|
|
239
|
+
export type AtAction = {
|
|
240
|
+
kind: "click";
|
|
241
|
+
at: Spot;
|
|
242
|
+
ref?: string;
|
|
243
|
+
on?: string;
|
|
244
|
+
confirm?: boolean;
|
|
245
|
+
button?: "left" | "middle";
|
|
246
|
+
} | {
|
|
247
|
+
kind: "dblclick";
|
|
248
|
+
at: Spot;
|
|
249
|
+
ref?: string;
|
|
250
|
+
on?: string;
|
|
251
|
+
confirm?: boolean;
|
|
252
|
+
} | {
|
|
253
|
+
kind: "rightclick";
|
|
254
|
+
at: Spot;
|
|
255
|
+
ref?: string;
|
|
256
|
+
on?: string;
|
|
257
|
+
confirm?: boolean;
|
|
258
|
+
} | {
|
|
259
|
+
kind: "hover";
|
|
260
|
+
at: Spot;
|
|
261
|
+
ref?: string;
|
|
262
|
+
on?: string;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The one whose ADDRESS is usually a spot and nothing else: a map tile, an
|
|
266
|
+
* empty part of a scrolling pane, a chart. `by` is how far down, `across` how
|
|
267
|
+
* far sideways, and the modifiers are held across the gesture — Control+wheel
|
|
268
|
+
* is how a map zooms.
|
|
269
|
+
*/
|
|
270
|
+
| {
|
|
271
|
+
kind: "wheel";
|
|
272
|
+
at: Spot;
|
|
273
|
+
ref?: string;
|
|
274
|
+
on?: string;
|
|
275
|
+
within?: string;
|
|
276
|
+
by: number;
|
|
277
|
+
across?: number;
|
|
278
|
+
modifiers?: readonly ("Shift" | "Control" | "Meta")[];
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* THE WRITING VERBS A COORDINATE CAN AIM, mirrored from core.
|
|
282
|
+
*
|
|
283
|
+
* Rich text is the case: a contenteditable is the field, and everything a
|
|
284
|
+
* pixel can land on inside it is a p or a span, so a position is the only
|
|
285
|
+
* address the surface has. The engine has always hit-tested `at` for any kind
|
|
286
|
+
* and judged the element actually under it, so the capability was real over
|
|
287
|
+
* the wire while this type said the call did not type-check — which meant a
|
|
288
|
+
* spot-addressed `type` into a canvas-hosted editor, and `contentBase64` on a
|
|
289
|
+
* spot-addressed upload, were unreachable from this package alone.
|
|
290
|
+
*
|
|
291
|
+
* NOT the `fill_*` verbs, deliberately: a credential resolved from a
|
|
292
|
+
* coordinate is a secret typed into whatever happened to be under a pixel.
|
|
293
|
+
*/
|
|
294
|
+
| {
|
|
295
|
+
kind: "type";
|
|
296
|
+
at: Spot;
|
|
297
|
+
ref?: string;
|
|
298
|
+
on?: string;
|
|
299
|
+
within?: string;
|
|
300
|
+
text: string;
|
|
301
|
+
submit?: boolean;
|
|
302
|
+
append?: boolean;
|
|
303
|
+
confirm?: boolean;
|
|
304
|
+
} | {
|
|
305
|
+
kind: "select";
|
|
306
|
+
at: Spot;
|
|
307
|
+
ref?: string;
|
|
308
|
+
on?: string;
|
|
309
|
+
within?: string;
|
|
310
|
+
value: string | string[];
|
|
311
|
+
} | {
|
|
312
|
+
kind: "paste";
|
|
313
|
+
at: Spot;
|
|
314
|
+
ref?: string;
|
|
315
|
+
on?: string;
|
|
316
|
+
within?: string;
|
|
317
|
+
text?: string;
|
|
318
|
+
} | {
|
|
319
|
+
kind: "upload";
|
|
320
|
+
at: Spot;
|
|
321
|
+
ref?: string;
|
|
322
|
+
on?: string;
|
|
323
|
+
within?: string;
|
|
324
|
+
filename?: string;
|
|
325
|
+
content?: string;
|
|
326
|
+
contentBase64?: string;
|
|
327
|
+
download?: string;
|
|
328
|
+
};
|
|
329
|
+
export type OnAction = {
|
|
330
|
+
kind: "click";
|
|
331
|
+
on: string;
|
|
332
|
+
within?: string;
|
|
333
|
+
confirm?: boolean;
|
|
334
|
+
button?: "left" | "middle";
|
|
335
|
+
} | {
|
|
336
|
+
kind: "dblclick";
|
|
337
|
+
on: string;
|
|
338
|
+
within?: string;
|
|
339
|
+
confirm?: boolean;
|
|
340
|
+
} | {
|
|
341
|
+
kind: "rightclick";
|
|
342
|
+
on: string;
|
|
343
|
+
within?: string;
|
|
344
|
+
confirm?: boolean;
|
|
345
|
+
} | {
|
|
346
|
+
kind: "hover";
|
|
347
|
+
on: string;
|
|
348
|
+
within?: string;
|
|
349
|
+
}
|
|
350
|
+
/** Scroll a pane by naming something inside it, when the pane itself has no
|
|
351
|
+
* name of its own. `by` is how far down, `across` how far sideways. */
|
|
352
|
+
| {
|
|
353
|
+
kind: "wheel";
|
|
354
|
+
on: string;
|
|
355
|
+
within?: string;
|
|
356
|
+
by: number;
|
|
357
|
+
across?: number;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* THE WRITING VERBS BY THE WORDS ON THE PAGE, mirrored from core.
|
|
361
|
+
*
|
|
362
|
+
* A field is often labelled and never named — "Attach a file" is the words a
|
|
363
|
+
* person reads, and the `<input type=file>` behind it has no accessible name
|
|
364
|
+
* of its own; the same is true of half the text inputs on the web. Naming the
|
|
365
|
+
* label resolves to the field before any guard runs, exactly as it does for a
|
|
366
|
+
* ref, so these gate identically.
|
|
367
|
+
*
|
|
368
|
+
* `type` and `select` were the ones this package could not express, and they
|
|
369
|
+
* are the half of `on` that matters most: an agent could click "3 people" and
|
|
370
|
+
* could not type into "Full name". The wire has accepted both since the
|
|
371
|
+
* server's `worded()` learned them — only this declaration said otherwise.
|
|
372
|
+
*/
|
|
373
|
+
| {
|
|
374
|
+
kind: "type";
|
|
375
|
+
on: string;
|
|
376
|
+
within?: string;
|
|
377
|
+
text: string;
|
|
378
|
+
submit?: boolean;
|
|
379
|
+
append?: boolean;
|
|
380
|
+
confirm?: boolean;
|
|
381
|
+
}
|
|
382
|
+
/** `values` is the plural spelling, for a `<select multiple>`; both land on
|
|
383
|
+
* core's one field, because "what to choose" is one idea. */
|
|
384
|
+
| {
|
|
385
|
+
kind: "select";
|
|
386
|
+
on: string;
|
|
387
|
+
within?: string;
|
|
388
|
+
value?: string;
|
|
389
|
+
values?: readonly string[];
|
|
390
|
+
} | {
|
|
391
|
+
kind: "paste";
|
|
392
|
+
on: string;
|
|
393
|
+
within?: string;
|
|
394
|
+
text?: string;
|
|
395
|
+
confirm?: boolean;
|
|
396
|
+
} | {
|
|
397
|
+
kind: "upload";
|
|
398
|
+
on: string;
|
|
399
|
+
within?: string;
|
|
400
|
+
filename?: string;
|
|
401
|
+
content?: string;
|
|
402
|
+
contentBase64?: string;
|
|
403
|
+
download?: string;
|
|
404
|
+
};
|
|
405
|
+
/** One control or link on a live page. */
|
|
406
|
+
export interface SnapshotElement {
|
|
407
|
+
ref: string;
|
|
408
|
+
role: string;
|
|
409
|
+
name: string;
|
|
410
|
+
value?: string;
|
|
411
|
+
/** True when `value` was cut to length: it is a PREFIX of what the field holds.
|
|
412
|
+
* Without it, checking a long write against what came back looks like a failed
|
|
413
|
+
* write on a field that is correct. */
|
|
414
|
+
valueTruncated?: boolean;
|
|
415
|
+
disabled?: boolean;
|
|
416
|
+
/**
|
|
417
|
+
* A count that sat in its own element beside the name, e.g. "232" on a
|
|
418
|
+
* "Summer 2026" filter — split out because textContent glues them together.
|
|
419
|
+
*/
|
|
420
|
+
badge?: string;
|
|
421
|
+
/** True when `name` was cut to length: it is a prefix, not the whole value. */
|
|
422
|
+
truncated?: boolean;
|
|
423
|
+
/** True when this holds a credential — typing here is refused. */
|
|
424
|
+
secret?: boolean;
|
|
425
|
+
/** Whether a secret field has something in it. Never what. */
|
|
426
|
+
filled?: boolean;
|
|
427
|
+
/** True when acting here submits a form — requires confirm. */
|
|
428
|
+
submits?: boolean;
|
|
429
|
+
/**
|
|
430
|
+
* Why it submits: `type=submit` is written on the control, while a bare
|
|
431
|
+
* `<button>` inside a form is the form's default one and says nothing at all.
|
|
432
|
+
* Present only when `submits` is true.
|
|
433
|
+
*/
|
|
434
|
+
submitsBecause?: "type=submit" | "default button in a form";
|
|
435
|
+
/**
|
|
436
|
+
* WIDGET STATE, mirrored from core's `SnapshotElement`. Absent means the page
|
|
437
|
+
* did not say — never that the answer is false.
|
|
438
|
+
*/
|
|
439
|
+
/** Is this menu / accordion / picker currently open? */
|
|
440
|
+
expanded?: boolean;
|
|
441
|
+
/** Clicking this opens a popup rather than acting: "menu", "listbox",
|
|
442
|
+
* "dialog", "tree", "grid". Tells you to click and then look for the choices,
|
|
443
|
+
* instead of trying `select` on something that is not a `<select>`. */
|
|
444
|
+
hasPopup?: string;
|
|
445
|
+
/** Checkbox, radio, switch or menu item: is it on? */
|
|
446
|
+
checked?: boolean;
|
|
447
|
+
/** Tab, option or treeitem: is this the chosen one? */
|
|
448
|
+
selected?: boolean;
|
|
449
|
+
/** Takes MORE THAN ONE value — `<input type=file multiple>` or `<select
|
|
450
|
+
* multiple>`. Both REPLACE rather than accumulate, so adding items one call at
|
|
451
|
+
* a time silently ends with the last one. */
|
|
452
|
+
multiple?: boolean;
|
|
453
|
+
/** Which embedded document this lives in — the frame's name, or its host.
|
|
454
|
+
* Absent for the page's own, which is where most controls are. The merged
|
|
455
|
+
* list is flat, so this is the only thing separating a page's "Continue"
|
|
456
|
+
* from an embedded checkout's. */
|
|
457
|
+
frame?: string;
|
|
458
|
+
}
|
|
459
|
+
/** What an agent can see and act on, per observe. */
|
|
460
|
+
/**
|
|
461
|
+
* Why a page did or did not open carrying the account's sign-in-provider session.
|
|
462
|
+
*
|
|
463
|
+
* `carried` is the good case. `agent-scoped` and `fresh-signin` are DECISIONS,
|
|
464
|
+
* not faults. `held-elsewhere` means another machine legitimately holds it — a
|
|
465
|
+
* provider session may live in exactly one browser at a time, because a
|
|
466
|
+
* duplicate gets BOTH invalidated. `store-unavailable` is an outage — or a
|
|
467
|
+
* stored provider profile the worker could not open, which is the same answer to
|
|
468
|
+
* the same question — and `none-stored` an account that has never connected one.
|
|
469
|
+
*
|
|
470
|
+
* All of them render as the same logged-out page, which is why they are named: a
|
|
471
|
+
* caller reading this can tell "correctly refused" from "try again shortly" from
|
|
472
|
+
* "the owner has to sign in once", instead of guessing from a screenshot.
|
|
473
|
+
*/
|
|
474
|
+
export type ProviderSessionReason = "carried" | "agent-scoped" | "fresh-signin" | "none-stored" | "held-elsewhere" | "store-unavailable";
|
|
475
|
+
/**
|
|
476
|
+
* One thing waiting on the account owner — a sign-in, a 2FA code, a consent, a
|
|
477
|
+
* per-action approval, or a site that needs its first provider login.
|
|
478
|
+
*/
|
|
479
|
+
/**
|
|
480
|
+
* Every wall the server can raise, mirrored from `server/src/interrupts.ts`.
|
|
481
|
+
*
|
|
482
|
+
* Declared as a union rather than `string` so a consumer that maps kinds to
|
|
483
|
+
* something a person reads is checked for exhaustiveness by `tsc` instead of
|
|
484
|
+
* silently falling through to the raw slug. `interrupt-kinds.test.ts` binds
|
|
485
|
+
* this list to the producer's, because a mirror that cannot drift is worth
|
|
486
|
+
* more here than one more `string`.
|
|
487
|
+
*/
|
|
488
|
+
export type InterruptKind = "credential" | "otp" | "captcha" | "consent" | "approval" | "grant" | "site-login" | "wait" | "watch";
|
|
489
|
+
export interface PendingApproval {
|
|
490
|
+
id: string;
|
|
491
|
+
kind: InterruptKind;
|
|
492
|
+
host: string;
|
|
493
|
+
url?: string | null;
|
|
494
|
+
reason?: string | null;
|
|
495
|
+
status: string;
|
|
496
|
+
createdAt: string;
|
|
497
|
+
expiresAt: string;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* One saveable picture, video or embed, present only when `observe`/`read` was
|
|
501
|
+
* called with `media: true`. A content image is not a control and has no ref
|
|
502
|
+
* anywhere else, so this is the handle `save` is pointed at. `w`/`h` are the
|
|
503
|
+
* rendered size; the scan skips anything under 64x64.
|
|
504
|
+
*/
|
|
505
|
+
export interface MediaItem {
|
|
506
|
+
ref: string;
|
|
507
|
+
kind: "image" | "video" | "audio" | "embed";
|
|
508
|
+
/** Its alt text, or a filename derived from the URL. */
|
|
509
|
+
name: string;
|
|
510
|
+
w: number;
|
|
511
|
+
h: number;
|
|
512
|
+
}
|
|
513
|
+
export interface PageSnapshot {
|
|
514
|
+
url: string;
|
|
515
|
+
title: string;
|
|
516
|
+
controls: SnapshotElement[];
|
|
517
|
+
links: SnapshotElement[];
|
|
518
|
+
excerpt: string;
|
|
519
|
+
truncated: {
|
|
520
|
+
controls: number;
|
|
521
|
+
links: number;
|
|
522
|
+
media?: number;
|
|
523
|
+
};
|
|
524
|
+
/**
|
|
525
|
+
* Saveable media, present ONLY when `media: true` was asked for. Each entry's
|
|
526
|
+
* `ref` goes straight to `save`. Off by default — it is extra payload the
|
|
527
|
+
* ordinary reading does not carry.
|
|
528
|
+
*/
|
|
529
|
+
media?: MediaItem[];
|
|
530
|
+
/**
|
|
531
|
+
* A few examples of what the page DRAWS as clickable and never declared, so
|
|
532
|
+
* they carry no ref and can only be named by their words. Absent when the page
|
|
533
|
+
* declares everything it means, which is what keeps it a signal.
|
|
534
|
+
*/
|
|
535
|
+
undeclared?: string[];
|
|
536
|
+
/**
|
|
537
|
+
* Why this reading may not be enough on its own, when it is not: a screen that is mostly
|
|
538
|
+
* canvas or picture, or several controls the page never named. Names see_page as the
|
|
539
|
+
* remedy. Absent whenever the reading stands alone, which is most pages.
|
|
540
|
+
*/
|
|
541
|
+
look?: {
|
|
542
|
+
code: "pictorial" | "unnamed";
|
|
543
|
+
reason: string;
|
|
544
|
+
};
|
|
545
|
+
/**
|
|
546
|
+
* The embedded documents this reading could and could not see. Absent on a page
|
|
547
|
+
* with no frames.
|
|
548
|
+
*
|
|
549
|
+
* A skipped frame is a whole document missing from the answer with nothing to
|
|
550
|
+
* mark its absence — which reads exactly like a page that has no such control.
|
|
551
|
+
* `more.reason` says the same thing in words when this is non-empty.
|
|
552
|
+
*/
|
|
553
|
+
frames?: {
|
|
554
|
+
included: number;
|
|
555
|
+
skipped: Array<{
|
|
556
|
+
host: string;
|
|
557
|
+
/** cross-origin is a legacy label; unreadable does not guess the cause. */
|
|
558
|
+
reason: "cross-origin" | "unreadable" | "detached" | "over-limit";
|
|
559
|
+
}>;
|
|
560
|
+
};
|
|
561
|
+
/** The page had not finished loading when this was read. Absent means it had.
|
|
562
|
+
* A thin reading with an innocent explanation, and a different remedy from
|
|
563
|
+
* every other thin reading: observe again in a moment. */
|
|
564
|
+
loading?: boolean;
|
|
565
|
+
/** Identifies the document these refs belong to; refs go stale on navigation. */
|
|
566
|
+
document: string;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* A picture of a page with the things that can be acted on drawn onto it.
|
|
570
|
+
*
|
|
571
|
+
* `marks` and `controls` describe the same instant: mark N is controls[N - 1].
|
|
572
|
+
* A control with no mark is scrolled out of view or covered, and `note` says so
|
|
573
|
+
* when that happens — it is still listed and still actable by ref.
|
|
574
|
+
*/
|
|
575
|
+
/**
|
|
576
|
+
* A reading, plus a picture when the reading says it needs one.
|
|
577
|
+
*
|
|
578
|
+
* `picture` is present only when the snapshot carries `look` — a screen that is
|
|
579
|
+
* mostly canvas or picture, or controls the page never named — and only for the
|
|
580
|
+
* first reading that hits a given wall on a given document.
|
|
581
|
+
*/
|
|
582
|
+
export interface ReadPage {
|
|
583
|
+
page: PageSnapshot;
|
|
584
|
+
/** JPEG data URI, with every control outlined and numbered. */
|
|
585
|
+
picture?: string;
|
|
586
|
+
/** Mark N is the Nth control. Empty unless `picture` is present. */
|
|
587
|
+
marks?: Array<{
|
|
588
|
+
mark: number;
|
|
589
|
+
ref: string;
|
|
590
|
+
role: string;
|
|
591
|
+
name: string;
|
|
592
|
+
}>;
|
|
593
|
+
/** Why some controls have no box. */
|
|
594
|
+
pictureNote?: string;
|
|
595
|
+
}
|
|
596
|
+
/** One field in a batched fill. Needs a `ref` or an `on`. */
|
|
597
|
+
export interface FormField {
|
|
598
|
+
ref?: string;
|
|
599
|
+
/** The field's visible words — its label, or its placeholder. */
|
|
600
|
+
on?: string;
|
|
601
|
+
/** Narrow `on` to one region, when a page has two of the same field. */
|
|
602
|
+
within?: string;
|
|
603
|
+
text: string;
|
|
604
|
+
/** Add to what is there instead of replacing it. */
|
|
605
|
+
append?: boolean;
|
|
606
|
+
}
|
|
607
|
+
export interface FormFillResult {
|
|
608
|
+
ok: boolean;
|
|
609
|
+
/** One entry per field, in the order sent, whether or not it worked. */
|
|
610
|
+
filled: Array<{
|
|
611
|
+
target: string;
|
|
612
|
+
ok: boolean;
|
|
613
|
+
code?: string;
|
|
614
|
+
reason?: string;
|
|
615
|
+
}>;
|
|
616
|
+
page: PageSnapshot;
|
|
617
|
+
}
|
|
618
|
+
export interface SeenPage {
|
|
619
|
+
/** JPEG data URI of the current viewport. */
|
|
620
|
+
image: string;
|
|
621
|
+
marks: Array<{
|
|
622
|
+
mark: number;
|
|
623
|
+
ref: string;
|
|
624
|
+
role: string;
|
|
625
|
+
name: string;
|
|
626
|
+
}>;
|
|
627
|
+
controls: SnapshotElement[];
|
|
628
|
+
/**
|
|
629
|
+
* The page's links, present only when `links` was asked for.
|
|
630
|
+
*
|
|
631
|
+
* Beside `controls` rather than inside it, because `controls` has meant "the
|
|
632
|
+
* things this page declares as controls" since the first version and a caller
|
|
633
|
+
* resolving a mark to a list entry looks in both.
|
|
634
|
+
*/
|
|
635
|
+
links?: SnapshotElement[];
|
|
636
|
+
/** What the page draws as clickable and never declared. No ref, so no box. */
|
|
637
|
+
undeclared?: string[];
|
|
638
|
+
note?: string;
|
|
639
|
+
}
|
|
640
|
+
/** A file a page handed to the browser during a session. */
|
|
641
|
+
export interface DownloadInfo {
|
|
642
|
+
filename: string;
|
|
643
|
+
bytes: number;
|
|
644
|
+
at: string;
|
|
645
|
+
fromUrl: string;
|
|
646
|
+
/** Set instead of the rest when the transfer failed. */
|
|
647
|
+
failed?: string;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* WHY AN ACT ANSWERED WHAT IT ANSWERED, as a value rather than a sentence.
|
|
651
|
+
*
|
|
652
|
+
* `reason` is written for a reader and is reworded whenever a clearer sentence
|
|
653
|
+
* is found; this is written for a `switch` and is not. The server has sent it on
|
|
654
|
+
* every act since codes existed and this type did not declare it, so the field
|
|
655
|
+
* arrived, type-checked as nothing, and was invisible to every SDK caller — the
|
|
656
|
+
* same hole `credentialRequired` had one layer up, with the same cause.
|
|
657
|
+
*
|
|
658
|
+
* A MIRROR OF `ACT_CODES` in `@unfenced/core`, which is the authority. This
|
|
659
|
+
* package has zero dependencies on purpose — it runs in a browser — so it cannot
|
|
660
|
+
* import the union, and a copy that could not follow its original is exactly the
|
|
661
|
+
* defect this repo keeps finding. So it is BOUND BY A CHECK instead:
|
|
662
|
+
* `scripts/docs-check.ts` compares the two lists in both directions and goes red
|
|
663
|
+
* on the first one to drift.
|
|
664
|
+
*
|
|
665
|
+
* Optional because an older server answers without it, never because a newer one
|
|
666
|
+
* might omit it.
|
|
667
|
+
*/
|
|
668
|
+
export type ActCode = "ok" | "no-effect" | "session-busy" | "permission-required" | "agent-scope-required" | "approval-required" | "confirmation-required" | "credential-required" | "credential-wrong-site" | "otp-required" | "site-login-required" | "no-credential" | "wrong-credential-kind" | "no-2fa" | "bad-totp-seed" | "provider-login" | "not-a-one-time-code" | "act-ambiguous" | "within-ambiguous" | "within-not-found" | "target-not-found" | "target-too-common" | "ref-stale" | "ref-malformed" | "wrong-element-kind" | "spot-invalid" | "bad-action" | "no-window" | "target-not-ready" | "target-covered" | "target-no-size" | "target-disabled" | "value-rejected" | "write-failed" | "page-moved" | "page-unresponsive" | "blocked-target" | "navigate-failed" | "no-history" | "no-session" | "tenant-session-limit" | "quota-exceeded" | "park-full" | "engine-error";
|
|
669
|
+
/**
|
|
670
|
+
* What an act changed, as a summary rather than a second copy of the page.
|
|
671
|
+
*
|
|
672
|
+
* The mirror of core's `PageChanges`. Attached to an act as `changes` when
|
|
673
|
+
* something moved and the change is describable; a navigation reports `redrawn`
|
|
674
|
+
* instead of listing every control twice. A SUMMARY, NOT A SUBSTITUTE — refs
|
|
675
|
+
* come from `page`, and a diff carries none for what did not change.
|
|
676
|
+
*/
|
|
677
|
+
export interface PageChanges {
|
|
678
|
+
url?: {
|
|
679
|
+
from: string;
|
|
680
|
+
to: string;
|
|
681
|
+
};
|
|
682
|
+
title?: {
|
|
683
|
+
from: string;
|
|
684
|
+
to: string;
|
|
685
|
+
};
|
|
686
|
+
/** Controls that were not there before. */
|
|
687
|
+
appeared?: SnapshotElement[];
|
|
688
|
+
/** What vanished, by name — a ref that is gone cannot be acted on anyway. */
|
|
689
|
+
vanished?: string[];
|
|
690
|
+
/** Same element, different words — how a page says it accepted a choice. */
|
|
691
|
+
renamed?: Array<{
|
|
692
|
+
ref: string;
|
|
693
|
+
from: string;
|
|
694
|
+
to: string;
|
|
695
|
+
}>;
|
|
696
|
+
/** Words on the page now that were not on it before. */
|
|
697
|
+
newText?: string;
|
|
698
|
+
/** The page was replaced rather than adjusted. Read the snapshot instead. */
|
|
699
|
+
redrawn?: boolean;
|
|
700
|
+
/**
|
|
701
|
+
* A wall that just appeared — the act ran into a block page. `blocked` also
|
|
702
|
+
* rides on `page`, but a `brief:true` act drops `page` and keeps only this
|
|
703
|
+
* diff, so a submit that navigated into an "Access Denied" would otherwise
|
|
704
|
+
* lose the structured signal. Set only on the transition into a block.
|
|
705
|
+
*/
|
|
706
|
+
blocked?: {
|
|
707
|
+
by: string;
|
|
708
|
+
reason: string;
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
export type ActEvidenceKind = "navigation" | "dom-change" | "popup" | "dialog" | "download" | "postcondition";
|
|
712
|
+
export interface ActEvidence {
|
|
713
|
+
kind: ActEvidenceKind;
|
|
714
|
+
}
|
|
715
|
+
export interface ActExpectation {
|
|
716
|
+
text?: string;
|
|
717
|
+
gone?: string;
|
|
718
|
+
/** A substring or `*` glob matched against the resulting URL. */
|
|
719
|
+
url?: string;
|
|
720
|
+
ms?: number;
|
|
721
|
+
}
|
|
722
|
+
export interface ActResult {
|
|
723
|
+
ok: boolean;
|
|
724
|
+
/** Privacy-safe evidence categories observed while completing the act. */
|
|
725
|
+
evidence?: ActEvidence[];
|
|
726
|
+
/** Why the action was refused — actionable, recoverable. */
|
|
727
|
+
reason?: string;
|
|
728
|
+
/**
|
|
729
|
+
* The same answer as `reason`, from a closed set. Present on successes too
|
|
730
|
+
* (`"ok"`, or `"no-effect"` when the act landed and the page did not move), so
|
|
731
|
+
* a caller can group every act by outcome without special-casing absence.
|
|
732
|
+
*/
|
|
733
|
+
code?: ActCode;
|
|
734
|
+
/**
|
|
735
|
+
* What the page asked NATIVELY during this act: alert, confirm, prompt or
|
|
736
|
+
* beforeunload. Reported whether or not it changed the outcome, because a
|
|
737
|
+
* dismissed confirm is the case that otherwise looks like a successful no-op.
|
|
738
|
+
*/
|
|
739
|
+
dialogs?: Array<{
|
|
740
|
+
type: string;
|
|
741
|
+
message: string;
|
|
742
|
+
handled: "accepted" | "dismissed";
|
|
743
|
+
}>;
|
|
744
|
+
/** Something worth knowing about an otherwise successful act. Not `reason`,
|
|
745
|
+
* which means the action was refused. */
|
|
746
|
+
note?: string;
|
|
747
|
+
/**
|
|
748
|
+
* Whether the page had stopped changing when this reading was taken. Sent
|
|
749
|
+
* only as `false`, and only when the wait after the act hit its ceiling — a
|
|
750
|
+
* page that settled says nothing.
|
|
751
|
+
*
|
|
752
|
+
* It sits on a successful act deliberately, like `expected.held` above: the
|
|
753
|
+
* act landed, and the snapshot beside it is of a page still drawing. Observe
|
|
754
|
+
* again if the next act depends on something that was still arriving.
|
|
755
|
+
*/
|
|
756
|
+
settled?: boolean;
|
|
757
|
+
/** How long that wait took, in milliseconds. Sent with `settled: false` and
|
|
758
|
+
* never on its own. */
|
|
759
|
+
settleMs?: number;
|
|
760
|
+
/**
|
|
761
|
+
* Whether a post-condition supplied as `expect` held, and how long it was
|
|
762
|
+
* waited for. `held: false` sits on a successful act deliberately: the act
|
|
763
|
+
* landed and the page did something else, which is a different problem from
|
|
764
|
+
* the act not landing.
|
|
765
|
+
*/
|
|
766
|
+
expected?: {
|
|
767
|
+
held: boolean;
|
|
768
|
+
waitedMs: number;
|
|
769
|
+
};
|
|
770
|
+
/**
|
|
771
|
+
* PRESENT ONLY WHEN THE PAGE DID NOT MOVE — the act landed and the page read
|
|
772
|
+
* exactly the same afterwards, watched for `waitedMs`.
|
|
773
|
+
*
|
|
774
|
+
* An observation, not a verdict. A slow page and a dead button are
|
|
775
|
+
* indistinguishable from here, so the answer is never to repeat the act:
|
|
776
|
+
* observe again, or look. The note says as much, and says it more strongly
|
|
777
|
+
* when the act submits.
|
|
778
|
+
*/
|
|
779
|
+
effect?: {
|
|
780
|
+
changed: false;
|
|
781
|
+
waitedMs: number;
|
|
782
|
+
note: string;
|
|
783
|
+
};
|
|
784
|
+
/**
|
|
785
|
+
* WHAT THIS ACT CHANGED, as a summary rather than a second copy of the page.
|
|
786
|
+
*
|
|
787
|
+
* Present when something moved and the change is describable; a navigation
|
|
788
|
+
* reports `redrawn` instead. Asked for with `brief`, which drops `page` in its
|
|
789
|
+
* favour — so `changes.blocked` is where a block reached by a brief submit
|
|
790
|
+
* surfaces. A SUMMARY, NOT A SUBSTITUTE: refs come from `page`.
|
|
791
|
+
*/
|
|
792
|
+
changes?: PageChanges;
|
|
793
|
+
/**
|
|
794
|
+
* What the server can do, attached to REFUSALS.
|
|
795
|
+
*
|
|
796
|
+
* A tool description is cached when a client connects, so one that connected
|
|
797
|
+
* before a capability shipped cannot see it and will misread the page as
|
|
798
|
+
* impossible. `targeting` lists the ways this engine accepts a target; a mode
|
|
799
|
+
* here that your client has no parameter for means the client is stale.
|
|
800
|
+
*/
|
|
801
|
+
engine?: {
|
|
802
|
+
version: string;
|
|
803
|
+
commit: string;
|
|
804
|
+
targeting: readonly string[];
|
|
805
|
+
note: string;
|
|
806
|
+
};
|
|
807
|
+
/** The several things that read the same, when `on` did not name exactly one.
|
|
808
|
+
* Each carries a ref, so choosing costs one round trip. `frame` is set when
|
|
809
|
+
* a candidate is in an embedded document rather than the page's own — the
|
|
810
|
+
* distinction `within` cannot draw. */
|
|
811
|
+
candidates?: Array<{
|
|
812
|
+
ref: string;
|
|
813
|
+
role: string;
|
|
814
|
+
name: string;
|
|
815
|
+
within?: string;
|
|
816
|
+
frame?: string;
|
|
817
|
+
}>;
|
|
818
|
+
/** What a words-addressed target turned out to be, so the next act on the same
|
|
819
|
+
* thing can use the ref. */
|
|
820
|
+
resolved?: {
|
|
821
|
+
ref: string;
|
|
822
|
+
name: string;
|
|
823
|
+
role: string;
|
|
824
|
+
within?: string;
|
|
825
|
+
};
|
|
826
|
+
/** Set when the action needs { confirm: true } to proceed. */
|
|
827
|
+
confirmationRequired?: string;
|
|
828
|
+
/** Set when the site is not on the act-allowlist — carries the site to allow. */
|
|
829
|
+
permissionRequired?: string;
|
|
830
|
+
/**
|
|
831
|
+
* Set when the action was refused because the field holds a credential.
|
|
832
|
+
*
|
|
833
|
+
* Its own signal rather than a `reason` string, because it needs its own
|
|
834
|
+
* advice: do not ask the user for the password, and do not retry. The core
|
|
835
|
+
* has carried this since the refusal was fixed and this type did not, so an
|
|
836
|
+
* SDK caller could not tell a credential refusal from any other failure —
|
|
837
|
+
* which is the same hole one layer up.
|
|
838
|
+
*/
|
|
839
|
+
credentialRequired?: string;
|
|
840
|
+
/**
|
|
841
|
+
* Set alongside `credentialRequired` when the refused field is a ONE-TIME-CODE
|
|
842
|
+
* field, not a password — so the remedy is "enter this 2FA code once" (via
|
|
843
|
+
* fill_otp, or by taking the wheel), not "add a login". Never a value.
|
|
844
|
+
*/
|
|
845
|
+
otpField?: boolean;
|
|
846
|
+
/** Set when the action needs the account owner's approval to act on the site. */
|
|
847
|
+
approvalRequired?: string;
|
|
848
|
+
/**
|
|
849
|
+
* WHAT THE AUTHORITY GATE DECIDED — present on an allow as well as a refusal.
|
|
850
|
+
*
|
|
851
|
+
* MIRRORED HERE DELIBERATELY. This package hand-copies the engine's result
|
|
852
|
+
* shape, so a field the engine adds and this file does not arrives over the
|
|
853
|
+
* wire, type-checks nowhere, and is dropped before any SDK or MCP caller can
|
|
854
|
+
* see it — silently, which is the failure mode the engine's own comment on
|
|
855
|
+
* this type warns about at length. The server writes its own audit row from
|
|
856
|
+
* the engine's value, so a receipt does not depend on this copy; a CALLER
|
|
857
|
+
* wanting to show why an act was permitted does.
|
|
858
|
+
*
|
|
859
|
+
* ABSENT WHEN THE GATE DID NOT RUN — a scroll, a hover, an ordinary wheel.
|
|
860
|
+
* Absent is not an allow: it means no decision was taken, and a caller that
|
|
861
|
+
* renders it as permission is reporting something the engine never said.
|
|
862
|
+
*/
|
|
863
|
+
authorization?: {
|
|
864
|
+
decision: "allow" | "deny" | "require-approval";
|
|
865
|
+
/** The hostname the gate judged. */
|
|
866
|
+
host?: string;
|
|
867
|
+
/**
|
|
868
|
+
* The allowlist key the verdict was decided by. Absent when the allowlist
|
|
869
|
+
* was never consulted — an any-site credential, or a sign-in provider a
|
|
870
|
+
* login is continuing on — so its presence is the stronger claim.
|
|
871
|
+
*/
|
|
872
|
+
site?: string;
|
|
873
|
+
deniedBy?: "account" | "agent";
|
|
874
|
+
/**
|
|
875
|
+
* A PERSON CLEARED THIS ONE. Set only alongside
|
|
876
|
+
* `decision: "require-approval"`, and deliberately not flattened into an
|
|
877
|
+
* ordinary allow: "a standing grant covered it" and "a human looked at
|
|
878
|
+
* this act and said yes" are different claims, and the second is the one
|
|
879
|
+
* worth forwarding to somebody who does not trust you.
|
|
880
|
+
*/
|
|
881
|
+
approved?: true;
|
|
882
|
+
};
|
|
883
|
+
/**
|
|
884
|
+
* A deep link that opens the dashboard already set up to clear this wall — a
|
|
885
|
+
* missing login opens the Add-login drawer for the host, a missing permission
|
|
886
|
+
* the Sites grant. Present on a credential/permission/approval refusal so the
|
|
887
|
+
* caller can offer the user a click instead of an instruction. Carries only the
|
|
888
|
+
* blocked host, never a secret.
|
|
889
|
+
*/
|
|
890
|
+
setupUrl?: string;
|
|
891
|
+
/** Files the page handed to the browser while this action ran. */
|
|
892
|
+
downloads?: DownloadInfo[];
|
|
893
|
+
/** The page after the action. */
|
|
894
|
+
page?: PageSnapshot;
|
|
895
|
+
/**
|
|
896
|
+
* A marked picture of the instant `page` describes. Present only when the call
|
|
897
|
+
* asked for it with `see`.
|
|
898
|
+
*
|
|
899
|
+
* ONE instant, which act-then-see_page cannot promise: that is two calls, and
|
|
900
|
+
* a page may move between them with nothing in either answer saying so. Carries
|
|
901
|
+
* no reading of its own — that is `page` above.
|
|
902
|
+
*/
|
|
903
|
+
view?: {
|
|
904
|
+
/** JPEG data URI of the viewport, with the controls outlined and numbered. */
|
|
905
|
+
image: string;
|
|
906
|
+
/** Mark N is the Nth control in `page.controls`. */
|
|
907
|
+
marks?: Array<{
|
|
908
|
+
mark: number;
|
|
909
|
+
ref: string;
|
|
910
|
+
role: string;
|
|
911
|
+
name: string;
|
|
912
|
+
}>;
|
|
913
|
+
/** Why some controls have no box. */
|
|
914
|
+
note?: string;
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
export type Action = {
|
|
918
|
+
kind: "click";
|
|
919
|
+
ref: string;
|
|
920
|
+
confirm?: boolean;
|
|
921
|
+
modifiers?: readonly ("Shift" | "Control" | "Meta")[];
|
|
922
|
+
/** Which button. `middle` opens a link in a background tab, which shows
|
|
923
|
+
* up in `windows` like any other popup. A right click is `rightclick`. */
|
|
924
|
+
button?: "left" | "middle";
|
|
925
|
+
} | {
|
|
926
|
+
kind: "type";
|
|
927
|
+
ref: string;
|
|
928
|
+
text: string;
|
|
929
|
+
submit?: boolean;
|
|
930
|
+
confirm?: boolean;
|
|
931
|
+
/** Add to the field instead of replacing it. Replacing is the default. */
|
|
932
|
+
append?: boolean;
|
|
933
|
+
}
|
|
934
|
+
/** Choose in a dropdown. One value, or several for a `<select multiple>` — it
|
|
935
|
+
* sets the whole selection rather than adding to it. */
|
|
936
|
+
| {
|
|
937
|
+
kind: "select";
|
|
938
|
+
ref: string;
|
|
939
|
+
value: string | string[];
|
|
940
|
+
} | {
|
|
941
|
+
kind: "press";
|
|
942
|
+
key: string;
|
|
943
|
+
confirm?: boolean;
|
|
944
|
+
} | {
|
|
945
|
+
kind: "scroll";
|
|
946
|
+
to: "top" | "bottom" | {
|
|
947
|
+
ref: string;
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
/** Move between the windows this session holds. 0 is the page you opened. */
|
|
951
|
+
| {
|
|
952
|
+
kind: "switch";
|
|
953
|
+
to: number;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Close one of the windows this session holds — the other half of `switch`.
|
|
957
|
+
*
|
|
958
|
+
* For a popup a flow left behind: an OAuth window that did not close itself,
|
|
959
|
+
* a print preview, a chat tab. `0` is refused, because window 0 is the page
|
|
960
|
+
* the session was opened on and closing that is closing the session.
|
|
961
|
+
*/
|
|
962
|
+
| {
|
|
963
|
+
kind: "close_window";
|
|
964
|
+
to: number;
|
|
965
|
+
}
|
|
966
|
+
/** Move the pointer onto something without pressing it — for hover menus. */
|
|
967
|
+
| {
|
|
968
|
+
kind: "hover";
|
|
969
|
+
ref: string;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Scroll with a real wheel, optionally over one element.
|
|
973
|
+
*
|
|
974
|
+
* Positive `by` scrolls content downward, the way a wheel pulled toward you
|
|
975
|
+
* does; `across` is the sideways half, for a pane that scrolls that way. The
|
|
976
|
+
* modifiers are held across the gesture — Control+wheel is how a map zooms.
|
|
977
|
+
*
|
|
978
|
+
* It was declared for `on` and for `at` and NOT here, so the one address a
|
|
979
|
+
* caller usually has for a scrollable pane — the ref of something inside it,
|
|
980
|
+
* straight out of the last snapshot — did not typecheck, while the wire had
|
|
981
|
+
* accepted it since the verb shipped. `action-kinds.test.ts` now compares all
|
|
982
|
+
* three unions against core's own, so the next verb cannot arrive in two of
|
|
983
|
+
* three places again.
|
|
984
|
+
*/
|
|
985
|
+
| {
|
|
986
|
+
kind: "wheel";
|
|
987
|
+
by: number;
|
|
988
|
+
across?: number;
|
|
989
|
+
ref?: string;
|
|
990
|
+
modifiers?: readonly ("Shift" | "Control" | "Meta")[];
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Wait for the page to say something, go somewhere, or show something —
|
|
994
|
+
* rather than for a fixed time.
|
|
995
|
+
*
|
|
996
|
+
* `url` is a substring of the URL, or a `*` glob against the whole of it: the
|
|
997
|
+
* honest predicate for a sign-in or a checkout, which are defined by arriving
|
|
998
|
+
* somewhere. `visible` beside `ref` asks only whether the element is THERE,
|
|
999
|
+
* skipping the cover check a click needs. Everything sent has to hold.
|
|
1000
|
+
*/
|
|
1001
|
+
| {
|
|
1002
|
+
kind: "wait";
|
|
1003
|
+
text?: string;
|
|
1004
|
+
gone?: string;
|
|
1005
|
+
ref?: string;
|
|
1006
|
+
url?: string;
|
|
1007
|
+
visible?: boolean;
|
|
1008
|
+
ms?: number;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Put a file into a file input.
|
|
1012
|
+
*
|
|
1013
|
+
* `content` writes one from text you supply, `contentBase64` from bytes you
|
|
1014
|
+
* supply (10MB decoded, for a PNG, a PDF, a zip); `download` re-uses a file
|
|
1015
|
+
* this session downloaded. There is deliberately no path parameter — an
|
|
1016
|
+
* upload can only send bytes the caller already had.
|
|
1017
|
+
*/
|
|
1018
|
+
| {
|
|
1019
|
+
kind: "upload";
|
|
1020
|
+
ref: string;
|
|
1021
|
+
filename?: string;
|
|
1022
|
+
content?: string;
|
|
1023
|
+
contentBase64?: string;
|
|
1024
|
+
download?: string;
|
|
1025
|
+
}
|
|
1026
|
+
/** Double-click an element — the real mouse, at the ref's point, like click. */
|
|
1027
|
+
/**
|
|
1028
|
+
* `confirm` is carried on all three pointer verbs, not just click.
|
|
1029
|
+
*
|
|
1030
|
+
* The guard asks one predicate over click, dblclick and rightclick before any
|
|
1031
|
+
* of them activates a submit control. Omitting it here — while the MCP layer
|
|
1032
|
+
* builds it with a SPREAD, which slips past tsc's excess-property check —
|
|
1033
|
+
* meant the refusal said "re-send with confirm: true" and the re-sent call
|
|
1034
|
+
* left byte-identical to the first. An agent doing exactly what it was told
|
|
1035
|
+
* could not read its way out.
|
|
1036
|
+
*/
|
|
1037
|
+
| {
|
|
1038
|
+
kind: "dblclick";
|
|
1039
|
+
ref: string;
|
|
1040
|
+
confirm?: boolean;
|
|
1041
|
+
}
|
|
1042
|
+
/** Context-menu click — the real right button, opening the page's own menu. */
|
|
1043
|
+
| {
|
|
1044
|
+
kind: "rightclick";
|
|
1045
|
+
ref: string;
|
|
1046
|
+
confirm?: boolean;
|
|
1047
|
+
}
|
|
1048
|
+
/** Drag one element onto another with the real pointer, so drag handlers fire. */
|
|
1049
|
+
/**
|
|
1050
|
+
* Drag from one thing to another, optionally along a route.
|
|
1051
|
+
*
|
|
1052
|
+
* `via` is viewport fractions the pointer travels through. Without it a drag
|
|
1053
|
+
* is a straight line, which is right for dropping a card into a column and
|
|
1054
|
+
* cannot express a gesture whose meaning is its shape.
|
|
1055
|
+
*/
|
|
1056
|
+
| {
|
|
1057
|
+
kind: "drag";
|
|
1058
|
+
/** A ref, or a SPOT as viewport fractions — a map has nothing to name. */
|
|
1059
|
+
from: string | {
|
|
1060
|
+
x: number;
|
|
1061
|
+
y: number;
|
|
1062
|
+
};
|
|
1063
|
+
to: string | {
|
|
1064
|
+
x: number;
|
|
1065
|
+
y: number;
|
|
1066
|
+
};
|
|
1067
|
+
via?: ReadonlyArray<{
|
|
1068
|
+
x: number;
|
|
1069
|
+
y: number;
|
|
1070
|
+
}>;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Change the page's zoom, 0.25 to 3.
|
|
1074
|
+
*
|
|
1075
|
+
* `see_page` photographs the viewport and marks only what is on screen, so a
|
|
1076
|
+
* control below the fold is neither photographed nor markable. Zooming out is
|
|
1077
|
+
* how a long page becomes one picture with everything in it addressable.
|
|
1078
|
+
*/
|
|
1079
|
+
| {
|
|
1080
|
+
kind: "zoom";
|
|
1081
|
+
scale: number;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* The page as a PDF, filed in the session's downloads.
|
|
1085
|
+
*
|
|
1086
|
+
* The one durable artefact an agent could not make. Carries the WHOLE page
|
|
1087
|
+
* rather than the viewport, and renders the site's print stylesheet, which on
|
|
1088
|
+
* a receipt is the layout the publisher meant it to have. Read it back with
|
|
1089
|
+
* `readDownload`.
|
|
1090
|
+
*/
|
|
1091
|
+
| {
|
|
1092
|
+
kind: "print";
|
|
1093
|
+
filename?: string;
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Save what the page is SHOWING as a file — an image, a video, an embedded PDF.
|
|
1097
|
+
*
|
|
1098
|
+
* Fetched from inside the page, with the session it already holds, which is
|
|
1099
|
+
* why it reaches a signed link or a private invoice that a plain fetch cannot.
|
|
1100
|
+
* Aimed at an element rather than a URL; the address it resolves to is checked
|
|
1101
|
+
* before it is read.
|
|
1102
|
+
*/
|
|
1103
|
+
| {
|
|
1104
|
+
kind: "save";
|
|
1105
|
+
ref: string;
|
|
1106
|
+
filename?: string;
|
|
1107
|
+
}
|
|
1108
|
+
/** Copy the current selection. Focuses `ref` first if given. Not mutating. */
|
|
1109
|
+
| {
|
|
1110
|
+
kind: "copy";
|
|
1111
|
+
ref?: string;
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Paste into a field. Focuses `ref`, then the paste shortcut. Credential-guarded.
|
|
1115
|
+
*
|
|
1116
|
+
* `text` puts that value on the clipboard first, so a paste no longer needs a
|
|
1117
|
+
* `copy` to have found the value somewhere on the page. Reach for it over
|
|
1118
|
+
* `type` when the field parses a PASTE — a rich-text or code editor, a tag
|
|
1119
|
+
* input, a card-number box. Never for a stored credential: that is
|
|
1120
|
+
* `fill_secret`, and the value never travels.
|
|
1121
|
+
*/
|
|
1122
|
+
| {
|
|
1123
|
+
kind: "paste";
|
|
1124
|
+
ref: string;
|
|
1125
|
+
text?: string;
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Fill a stored credential into a field by NAME — never by value.
|
|
1129
|
+
*
|
|
1130
|
+
* `credential` is the name of a secret stored in the vault ("github-pw"); the
|
|
1131
|
+
* value is resolved engine-side and typed in without ever passing through the
|
|
1132
|
+
* caller or the model. There is no value field, deliberately: storing a secret
|
|
1133
|
+
* is a separate, human action (`storeCredential`), and using one is by name.
|
|
1134
|
+
*/
|
|
1135
|
+
| {
|
|
1136
|
+
kind: "fill_secret";
|
|
1137
|
+
ref: string;
|
|
1138
|
+
credential: string;
|
|
1139
|
+
} | {
|
|
1140
|
+
kind: "fill_totp";
|
|
1141
|
+
ref: string;
|
|
1142
|
+
credential: string;
|
|
1143
|
+
} | {
|
|
1144
|
+
kind: "fill_otp";
|
|
1145
|
+
ref: string;
|
|
1146
|
+
code: string;
|
|
1147
|
+
}
|
|
1148
|
+
/** `settleMs`: how long to let the network go quiet after the document
|
|
1149
|
+
* commits, before the reading is taken. Default 4000, maximum 15000 — raise
|
|
1150
|
+
* it for a page you know draws from several slow fetches. */
|
|
1151
|
+
| {
|
|
1152
|
+
kind: "navigate";
|
|
1153
|
+
url: string;
|
|
1154
|
+
settleMs?: number;
|
|
1155
|
+
} | {
|
|
1156
|
+
kind: "back";
|
|
1157
|
+
}
|
|
1158
|
+
/** Forward, the other half of back. */
|
|
1159
|
+
| {
|
|
1160
|
+
kind: "forward";
|
|
1161
|
+
}
|
|
1162
|
+
/** Reload the current page — NOT the same as navigating to the same URL, which
|
|
1163
|
+
* discards the history entry and re-posts or drops what was typed. */
|
|
1164
|
+
| {
|
|
1165
|
+
kind: "reload";
|
|
1166
|
+
};
|
|
1167
|
+
export interface SessionInfo {
|
|
1168
|
+
connection?: ConnectionSnapshot;
|
|
1169
|
+
id: string;
|
|
1170
|
+
url: string;
|
|
1171
|
+
type: "ephemeral" | "agent" | "cdp";
|
|
1172
|
+
/** Why the provider login was or was not carried into this live session. */
|
|
1173
|
+
providerSession?: ProviderSessionReason;
|
|
1174
|
+
openedAt: string;
|
|
1175
|
+
lastUsedAt: string;
|
|
1176
|
+
/**
|
|
1177
|
+
* True once something has been done to this page rather than only read.
|
|
1178
|
+
*
|
|
1179
|
+
* It decides which idle clock the page gets — 5 minutes for one only read,
|
|
1180
|
+
* 15 once acted on — because the two differ in what closing destroys.
|
|
1181
|
+
*/
|
|
1182
|
+
actedOn?: boolean;
|
|
1183
|
+
/** Set while the page is held open waiting on something out of band. */
|
|
1184
|
+
parkedUntil?: string;
|
|
1185
|
+
/** What it is waiting for, if it was said. */
|
|
1186
|
+
parkedReason?: string;
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* A finished run in the recorded-session library.
|
|
1190
|
+
*
|
|
1191
|
+
* This and the two types under it are hand-mirrored from the server's
|
|
1192
|
+
* `packages/server/src/sessions-db.ts`, deliberately and not by oversight: this
|
|
1193
|
+
* client has zero dependencies and never imports the server, and cross-package
|
|
1194
|
+
* types here resolve through `dist/`, so importing one would tie a browser-side
|
|
1195
|
+
* client to a Node build. The cost is that the mirror has to be updated when the
|
|
1196
|
+
* server's shape moves — as it did when `api` joined the kinds and `stepCount`
|
|
1197
|
+
* joined the summary.
|
|
1198
|
+
*/
|
|
1199
|
+
export interface ConnectionSnapshot {
|
|
1200
|
+
kind: "direct" | "proxy" | "unknown";
|
|
1201
|
+
name: string;
|
|
1202
|
+
country?: string;
|
|
1203
|
+
}
|
|
1204
|
+
export interface SessionSummary {
|
|
1205
|
+
connection?: ConnectionSnapshot;
|
|
1206
|
+
id: string;
|
|
1207
|
+
/** `api` is a run driven through this SDK / the MCP connector, as opposed to
|
|
1208
|
+
* one of the dashboard's own demo, console, or bridge runs. */
|
|
1209
|
+
kind: "demo" | "agent" | "bridge" | "api";
|
|
1210
|
+
title: string;
|
|
1211
|
+
url: string;
|
|
1212
|
+
status: "ok" | "blocked" | "error";
|
|
1213
|
+
label: string;
|
|
1214
|
+
startedAt: string;
|
|
1215
|
+
durationMs: number;
|
|
1216
|
+
frameCount: number;
|
|
1217
|
+
/** How many steps the log holds — with film and an answer, what makes a run
|
|
1218
|
+
* worth opening. Runs recorded before it was counted report 0. */
|
|
1219
|
+
stepCount: number;
|
|
1220
|
+
/**
|
|
1221
|
+
* Which of the fleet's machines produced this run, and what OS it ran.
|
|
1222
|
+
*
|
|
1223
|
+
* BOTH are absent on a row recorded before the columns existed, and `worker`
|
|
1224
|
+
* alone is absent on a worker with neither UNFENCED_WORKER set nor a router
|
|
1225
|
+
* in front of it. Absent means UNKNOWN and must be rendered that way.
|
|
1226
|
+
*/
|
|
1227
|
+
worker?: string;
|
|
1228
|
+
workerOs?: string;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* One thing that happened, in order — the unit the trace is made of.
|
|
1232
|
+
*
|
|
1233
|
+
* The trace used to be `string[]`, one pre-formatted sentence per step, so a
|
|
1234
|
+
* client could print it and nothing else: which step failed, what it was aimed
|
|
1235
|
+
* at, and when it happened had all been flattened away before storage.
|
|
1236
|
+
*
|
|
1237
|
+
* A step never carries a secret. `chars` is why: a typed value is reported as a
|
|
1238
|
+
* LENGTH, so `type 11 chars into "Search"` reaches a client and the eleven
|
|
1239
|
+
* characters do not. There is no field that can hold a credential, a one-time
|
|
1240
|
+
* code, or field content, and that is the design rather than an omission.
|
|
1241
|
+
*/
|
|
1242
|
+
export interface SessionStep {
|
|
1243
|
+
/** Milliseconds since the session started — replay time, not wall clock. */
|
|
1244
|
+
at: number;
|
|
1245
|
+
/** `open`, `click`, `type`, `navigate`, `observe`, `fill_secret`, `note`, `tool`, … */
|
|
1246
|
+
kind: string;
|
|
1247
|
+
/** The one line a person reads: `click "Sign in"`. */
|
|
1248
|
+
text: string;
|
|
1249
|
+
/** Did it do what it was asked? Absent for steps that are not attempts. */
|
|
1250
|
+
ok?: boolean;
|
|
1251
|
+
/** Why not — a refusal reason, a wall hit, an error. */
|
|
1252
|
+
detail?: string;
|
|
1253
|
+
/** Privacy-safe proof of the effect observed after an action. */
|
|
1254
|
+
evidence?: ActEvidence[];
|
|
1255
|
+
/** Whether a post-condition held, and how long it was waited. */
|
|
1256
|
+
expected?: {
|
|
1257
|
+
held: boolean;
|
|
1258
|
+
waitedMs: number;
|
|
1259
|
+
};
|
|
1260
|
+
/** A measured no-change observation, not a failure verdict. */
|
|
1261
|
+
effect?: {
|
|
1262
|
+
changed: false;
|
|
1263
|
+
waitedMs: number;
|
|
1264
|
+
note: string;
|
|
1265
|
+
};
|
|
1266
|
+
/** Present only when the settle ceiling was reached. */
|
|
1267
|
+
settled?: false;
|
|
1268
|
+
/** The settle wait paired with `settled: false`. */
|
|
1269
|
+
settleMs?: number;
|
|
1270
|
+
/** Where the page ended up after this step. */
|
|
1271
|
+
url?: string;
|
|
1272
|
+
/** How many characters were typed. The characters themselves never appear. */
|
|
1273
|
+
chars?: number;
|
|
1274
|
+
/**
|
|
1275
|
+
* WHY this step ended as it did, as a value a query can group by.
|
|
1276
|
+
*
|
|
1277
|
+
* `detail` is the sentence written for the agent and is free to be reworded;
|
|
1278
|
+
* this is the same answer as a literal from the engine's closed `ACT_CODES`
|
|
1279
|
+
* set. Typed as a string rather than that union because this package takes no
|
|
1280
|
+
* dependency on the engine.
|
|
1281
|
+
*/
|
|
1282
|
+
code?: string;
|
|
1283
|
+
/** How far into the run this step happened: 1-based, monotonic, and counted
|
|
1284
|
+
* even after the trace stopped recording at its ceiling. */
|
|
1285
|
+
act?: number;
|
|
1286
|
+
/**
|
|
1287
|
+
* Index into `SessionDetail.frames` of the moment this step is visible, resolved
|
|
1288
|
+
* server-side after the strip was thinned, so it can be used as-is. ABSENT is
|
|
1289
|
+
* normal, not an error: a credential fill stops the camera on purpose, an
|
|
1290
|
+
* observe takes no picture, and a hand-driven run keeps no film at all.
|
|
1291
|
+
*/
|
|
1292
|
+
frame?: number;
|
|
1293
|
+
}
|
|
1294
|
+
export interface SessionDetail extends SessionSummary {
|
|
1295
|
+
answer: string;
|
|
1296
|
+
/**
|
|
1297
|
+
* The log, structured. This is the field to read.
|
|
1298
|
+
*
|
|
1299
|
+
* It was declared as `trace` here and the wire has never sent structured
|
|
1300
|
+
* steps under that name: the server sends `steps: SessionStep[]` and keeps
|
|
1301
|
+
* `trace: string[]` beside it, deliberately and permanently, so a reader
|
|
1302
|
+
* older than `steps` degrades rather than crashes. A typed caller doing
|
|
1303
|
+
* `detail.trace[0].kind` therefore read `undefined` off a string, and nothing
|
|
1304
|
+
* said so — the mirror had simply been written against the wrong field.
|
|
1305
|
+
*/
|
|
1306
|
+
steps: SessionStep[];
|
|
1307
|
+
/** The same log as plain lines, for readers older than `steps`. */
|
|
1308
|
+
trace: string[];
|
|
1309
|
+
frames: string[];
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* One durable note an agent has stored for itself.
|
|
1313
|
+
*
|
|
1314
|
+
* The task-memory scratchpad, scoped to (account, agent) on the server: a short
|
|
1315
|
+
* `key` to find it by, a small `value` that is the note, and when it was last
|
|
1316
|
+
* written. Survives across sessions — it is the agent's memory of its own WORK,
|
|
1317
|
+
* distinct from the browser profile's memory of the WEB.
|
|
1318
|
+
*/
|
|
1319
|
+
export interface MemoryEntry {
|
|
1320
|
+
key: string;
|
|
1321
|
+
value: string;
|
|
1322
|
+
updatedAt: string;
|
|
1323
|
+
}
|
|
1324
|
+
/** Bounded recall filters, useful when an agent keeps namespaced task notes. */
|
|
1325
|
+
export interface MemoryRecallOptions {
|
|
1326
|
+
/** Return only keys beginning with this prefix. */
|
|
1327
|
+
prefix?: string;
|
|
1328
|
+
/** Return at most this many newest notes (1..64). */
|
|
1329
|
+
limit?: number;
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* One stored credential, as the client ever sees it: a NAME and a time.
|
|
1333
|
+
*
|
|
1334
|
+
* There is deliberately no `value`. A secret goes IN through `storeCredential`
|
|
1335
|
+
* and is never handed back — no client method returns a stored secret value.
|
|
1336
|
+
* The agent uses one only by name, via a `fill_secret` action.
|
|
1337
|
+
*/
|
|
1338
|
+
export interface CredentialName {
|
|
1339
|
+
/** Pass this selector to open_page/account when reusing a linked workspace login. */
|
|
1340
|
+
browserAccount?: string;
|
|
1341
|
+
name: string;
|
|
1342
|
+
/** The account identifier (email/username) this login is for, or null for a
|
|
1343
|
+
* bare token. Shown back so a login is recognizable; the SECRET never is. */
|
|
1344
|
+
username: string | null;
|
|
1345
|
+
/** "password" (typed verbatim by fill_secret), "totp" (an authenticator seed the
|
|
1346
|
+
* engine turns into the current 2FA code via fill_totp), or "oauth" (a per-site
|
|
1347
|
+
* marker: this site signs in with `provider` — no fillable secret of its own). */
|
|
1348
|
+
kind: "password" | "totp" | "oauth";
|
|
1349
|
+
/** Whether a `kind:"password"` login ALSO carries a 2FA seed (fill it with
|
|
1350
|
+
* fill_totp under this same name). A standalone `kind:"totp"` reports false. */
|
|
1351
|
+
hasTotp: boolean;
|
|
1352
|
+
/** The site (host) this login is for, or null if tied to no site. Lets an agent
|
|
1353
|
+
* match a stored credential to the page it is on, rather than guess by name. */
|
|
1354
|
+
site: string | null;
|
|
1355
|
+
/** The sign-in provider (google/apple/microsoft/github), or null for a direct
|
|
1356
|
+
* login. On a sign-in account it names the account's provider; on an oauth
|
|
1357
|
+
* marker it names which provider the site signs in with. */
|
|
1358
|
+
provider: string | null;
|
|
1359
|
+
updatedAt: string;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* How the agent should choose among a person's several sign-in accounts.
|
|
1363
|
+
* - askMode "always": ask which account every time a site's provider is ambiguous.
|
|
1364
|
+
* - askMode "remember": ask once per site, then reuse that choice silently.
|
|
1365
|
+
* `sites` maps host → its CURATED accounts (the allow-list of account names the
|
|
1366
|
+
* agent may use there) and the `main` default among them ("" = ask every time).
|
|
1367
|
+
*/
|
|
1368
|
+
export interface SitePref {
|
|
1369
|
+
accounts: string[];
|
|
1370
|
+
main: string;
|
|
1371
|
+
/**
|
|
1372
|
+
* The DOWNSTREAM identity each curated account resolves to on this site (account
|
|
1373
|
+
* name → e.g. "or@gmail.com"), learned as the agent signs in with each. Lets a
|
|
1374
|
+
* caller flag two accounts that resolve to the SAME underlying account, or one not
|
|
1375
|
+
* linked yet. A plaintext identifier, never a secret.
|
|
1376
|
+
*/
|
|
1377
|
+
identities?: Record<string, string>;
|
|
1378
|
+
/** The profile photo URL each curated account resolves to here (account name → an
|
|
1379
|
+
* https avatar URL), so an account can wear its own face. A public URL, not a secret. */
|
|
1380
|
+
avatars?: Record<string, string>;
|
|
1381
|
+
}
|
|
1382
|
+
export interface AccountPrefs {
|
|
1383
|
+
askMode: "always" | "remember";
|
|
1384
|
+
sites: Record<string, SitePref>;
|
|
1385
|
+
/** sign-in account name → its profile photo URL, captured at sign-in. */
|
|
1386
|
+
accountAvatars?: Record<string, string>;
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* What GET /permissions answers with. Three arms, and on the unavailable one
|
|
1390
|
+
* the arrays are ABSENT rather than empty — so no caller can count them as
|
|
1391
|
+
* zero and conclude the owner granted nothing.
|
|
1392
|
+
*/
|
|
1393
|
+
export interface PermissionsAnswer {
|
|
1394
|
+
allowed?: string[];
|
|
1395
|
+
entries?: Array<{
|
|
1396
|
+
host: string;
|
|
1397
|
+
mode: "free" | "approve" | "read";
|
|
1398
|
+
}>;
|
|
1399
|
+
/** Explicit per-agent denies. These override every positive scope mode. */
|
|
1400
|
+
excludedSites?: string[];
|
|
1401
|
+
/** Rolling-deploy capability: explicit denies are enforced before every agent scope. */
|
|
1402
|
+
agentExclusionsAllScopes?: boolean;
|
|
1403
|
+
/** Served from the last good read; the store could not be refreshed. */
|
|
1404
|
+
stale?: boolean;
|
|
1405
|
+
/** The allowlist could not be read at all. The arrays are absent. */
|
|
1406
|
+
unavailable?: boolean;
|
|
1407
|
+
detail?: string;
|
|
1408
|
+
}
|
|
1409
|
+
//# sourceMappingURL=types.d.ts.map
|