@anweat/dsh-browser 0.1.17 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +266 -88
  2. package/assets/skills/dsh-browser/SKILL.md +120 -0
  3. package/assets/skills/dsh-browser/references/develop.md +48 -0
  4. package/assets/skills/dsh-browser/references/observe.md +91 -0
  5. package/assets/skills/dsh-browser/references/opencli.md +25 -0
  6. package/assets/skills/dsh-browser/references/recipes.md +149 -0
  7. package/assets/skills/dsh-browser/references/userscripts.md +44 -0
  8. package/cordis.patch.yml +11 -3
  9. package/lib/actions/act.d.ts +3 -0
  10. package/lib/actions/act.js +147 -0
  11. package/lib/actions/act.js.map +1 -0
  12. package/lib/actions/automation.d.ts +3 -0
  13. package/lib/actions/automation.js +356 -0
  14. package/lib/actions/automation.js.map +1 -0
  15. package/lib/actions/crawl.d.ts +3 -0
  16. package/lib/actions/crawl.js +28 -0
  17. package/lib/actions/crawl.js.map +1 -0
  18. package/lib/actions/errors.d.ts +53 -0
  19. package/lib/actions/errors.js +178 -0
  20. package/lib/actions/errors.js.map +1 -0
  21. package/lib/actions/index-view.d.ts +42 -0
  22. package/lib/actions/index-view.js +268 -0
  23. package/lib/actions/index-view.js.map +1 -0
  24. package/lib/actions/inspect.d.ts +3 -0
  25. package/lib/actions/inspect.js +35 -0
  26. package/lib/actions/inspect.js.map +1 -0
  27. package/lib/actions/observe.d.ts +3 -0
  28. package/lib/actions/observe.js +121 -0
  29. package/lib/actions/observe.js.map +1 -0
  30. package/lib/actions/opencli.d.ts +3 -0
  31. package/lib/actions/opencli.js +51 -0
  32. package/lib/actions/opencli.js.map +1 -0
  33. package/lib/actions/registry.d.ts +35 -0
  34. package/lib/actions/registry.js +89 -0
  35. package/lib/actions/registry.js.map +1 -0
  36. package/lib/actions/run.d.ts +34 -0
  37. package/lib/actions/run.js +226 -0
  38. package/lib/actions/run.js.map +1 -0
  39. package/lib/actions/runtime.d.ts +3 -0
  40. package/lib/actions/runtime.js +33 -0
  41. package/lib/actions/runtime.js.map +1 -0
  42. package/lib/actions/schema.d.ts +31 -0
  43. package/lib/actions/schema.js +309 -0
  44. package/lib/actions/schema.js.map +1 -0
  45. package/lib/actions/script.d.ts +3 -0
  46. package/lib/actions/script.js +97 -0
  47. package/lib/actions/script.js.map +1 -0
  48. package/lib/actions/shared.d.ts +31 -0
  49. package/lib/actions/shared.js +45 -0
  50. package/lib/actions/shared.js.map +1 -0
  51. package/lib/actions/surface.d.ts +22 -0
  52. package/lib/actions/surface.js +18 -0
  53. package/lib/actions/surface.js.map +1 -0
  54. package/lib/actions/target.d.ts +3 -0
  55. package/lib/actions/target.js +66 -0
  56. package/lib/actions/target.js.map +1 -0
  57. package/lib/actions/types.d.ts +181 -0
  58. package/lib/actions/types.js +48 -0
  59. package/lib/actions/types.js.map +1 -0
  60. package/lib/activation-rules.d.ts +29 -0
  61. package/lib/activation-rules.js +33 -0
  62. package/lib/activation-rules.js.map +1 -0
  63. package/lib/approval-policy.d.ts +40 -2
  64. package/lib/approval-policy.js +184 -97
  65. package/lib/approval-policy.js.map +1 -1
  66. package/lib/automation-assets-rpc.d.ts +8 -2
  67. package/lib/automation-assets-rpc.js +68 -10
  68. package/lib/automation-assets-rpc.js.map +1 -1
  69. package/lib/automation-assets.d.ts +152 -4
  70. package/lib/automation-assets.js +255 -15
  71. package/lib/automation-assets.js.map +1 -1
  72. package/lib/automation-convert.d.ts +23 -0
  73. package/lib/automation-convert.js +110 -0
  74. package/lib/automation-convert.js.map +1 -0
  75. package/lib/automation-development.d.ts +26 -0
  76. package/lib/automation-development.js +47 -0
  77. package/lib/automation-development.js.map +1 -1
  78. package/lib/automation-execution.d.ts +57 -1
  79. package/lib/automation-execution.js +167 -11
  80. package/lib/automation-execution.js.map +1 -1
  81. package/lib/automation-journal.d.ts +92 -0
  82. package/lib/automation-journal.js +449 -0
  83. package/lib/automation-journal.js.map +1 -0
  84. package/lib/automation-v2.d.ts +138 -0
  85. package/lib/automation-v2.js +631 -0
  86. package/lib/automation-v2.js.map +1 -0
  87. package/lib/automation.d.ts +82 -5
  88. package/lib/automation.js +239 -82
  89. package/lib/automation.js.map +1 -1
  90. package/lib/browser-service.d.ts +309 -32
  91. package/lib/browser-service.js +636 -182
  92. package/lib/browser-service.js.map +1 -1
  93. package/lib/client/SettingsCard.js +95 -57
  94. package/lib/client/SettingsCard.js.map +1 -1
  95. package/lib/client/asset-editor.d.ts +55 -0
  96. package/lib/client/asset-editor.js +101 -0
  97. package/lib/client/asset-editor.js.map +1 -0
  98. package/lib/client/automation-assets-client.d.ts +99 -2
  99. package/lib/client/automation-assets-client.js +243 -10
  100. package/lib/client/automation-assets-client.js.map +1 -1
  101. package/lib/client/form.d.ts +48 -4
  102. package/lib/client/form.js +142 -29
  103. package/lib/client/form.js.map +1 -1
  104. package/lib/client/index.d.ts +25 -2
  105. package/lib/client/index.js +5 -1
  106. package/lib/client/index.js.map +1 -1
  107. package/lib/client/locales.d.ts +85 -6
  108. package/lib/client/locales.js +64 -12
  109. package/lib/client/locales.js.map +1 -1
  110. package/lib/client/prompts-form.d.ts +67 -0
  111. package/lib/client/prompts-form.js +140 -0
  112. package/lib/client/prompts-form.js.map +1 -0
  113. package/lib/client/prompts-status-client.d.ts +34 -0
  114. package/lib/client/prompts-status-client.js +53 -0
  115. package/lib/client/prompts-status-client.js.map +1 -0
  116. package/lib/client/settings-namespace.d.ts +1 -1
  117. package/lib/client/settings-namespace.js +1 -1
  118. package/lib/client/settings-namespace.js.map +1 -1
  119. package/lib/client.js +1915 -330
  120. package/lib/client.js.map +1 -1
  121. package/lib/config.d.ts +50 -1
  122. package/lib/config.js +31 -1
  123. package/lib/config.js.map +1 -1
  124. package/lib/dialogs.d.ts +37 -0
  125. package/lib/dialogs.js +73 -0
  126. package/lib/dialogs.js.map +1 -0
  127. package/lib/freedom.d.ts +23 -7
  128. package/lib/freedom.js +48 -59
  129. package/lib/freedom.js.map +1 -1
  130. package/lib/index.d.ts +5 -13
  131. package/lib/index.js +51 -30
  132. package/lib/index.js.map +1 -1
  133. package/lib/journal.d.ts +109 -0
  134. package/lib/journal.js +238 -0
  135. package/lib/journal.js.map +1 -0
  136. package/lib/locator.d.ts +82 -0
  137. package/lib/locator.js +190 -0
  138. package/lib/locator.js.map +1 -0
  139. package/lib/observe-script.d.ts +27 -0
  140. package/lib/observe-script.js +565 -0
  141. package/lib/observe-script.js.map +1 -0
  142. package/lib/observe.d.ts +179 -0
  143. package/lib/observe.js +368 -0
  144. package/lib/observe.js.map +1 -0
  145. package/lib/policy-keys.d.ts +30 -0
  146. package/lib/policy-keys.js +34 -0
  147. package/lib/policy-keys.js.map +1 -0
  148. package/lib/prompt-limits.d.ts +30 -0
  149. package/lib/prompt-limits.js +31 -0
  150. package/lib/prompt-limits.js.map +1 -0
  151. package/lib/prompts.d.ts +131 -0
  152. package/lib/prompts.js +295 -0
  153. package/lib/prompts.js.map +1 -0
  154. package/lib/skill.d.ts +100 -0
  155. package/lib/skill.js +181 -0
  156. package/lib/skill.js.map +1 -0
  157. package/lib/tool-defs.d.ts +72 -0
  158. package/lib/tool-defs.js +67 -0
  159. package/lib/tool-defs.js.map +1 -0
  160. package/lib/tools.d.ts +19 -4
  161. package/lib/tools.js +124 -922
  162. package/lib/tools.js.map +1 -1
  163. package/package.json +15 -11
  164. package/scripts/check-host-rpc.mjs +11 -4
  165. package/scripts/dump-prompts.mjs +29 -0
  166. package/scripts/measure-tool-surface.mjs +190 -0
  167. package/lib/settings-scope.d.ts +0 -56
  168. package/lib/settings-scope.js +0 -51
  169. package/lib/settings-scope.js.map +0 -1
@@ -20,11 +20,17 @@
20
20
  */
21
21
  import { type CliResult } from './deps.ts';
22
22
  import type { ResolvedConfig } from './config.ts';
23
- import { type BrowserRecipeStep, type RecipeStepResult } from './automation.ts';
23
+ import { type ResolvedAuthProfile } from './auth-profiles.ts';
24
+ import { type ResolvedRulePack } from './rule-packs.ts';
25
+ import { type AnyRecipeStep, type RecipeRunResult, type RecipeStepResult } from './automation.ts';
26
+ import type { OutputSpec, Postcondition } from './automation-v2.ts';
24
27
  import { type UserscriptValidation } from './scripts.ts';
25
28
  import { type AutomationMode } from './freedom.ts';
26
29
  import { type OpencliCatalogFilter, type OpencliCatalogItem } from './opencli-catalog.ts';
27
30
  import { UsageGovernor } from './usage-policy.ts';
31
+ import { type BrowserTarget } from './locator.ts';
32
+ import { SessionJournal } from './journal.ts';
33
+ import { type ObserveRegion, type ObserveSection } from './observe.ts';
28
34
  export interface RenderRule {
29
35
  hostname: string;
30
36
  contentSelectors: string[];
@@ -59,9 +65,21 @@ export interface InteractiveState {
59
65
  url: string;
60
66
  title: string;
61
67
  text: string;
68
+ /** The session page this state describes (`t1`, `t2`, ...). Absent only when the page is already gone. */
69
+ targetId?: string;
70
+ /**
71
+ * The page's generation when this state was read. It changes whenever the page's main frame navigates
72
+ * (see {@link SessionState.generationClock}); pass it back as `expectGeneration` to refuse acting on a page
73
+ * that moved on since it was observed.
74
+ */
75
+ generation?: number;
62
76
  screenshotPath?: string;
77
+ /** The page the action ran on closed itself (a popup's own close button): url/title/text describe the page the session fell back to, or are empty. */
78
+ closedPage?: true;
63
79
  }
64
- export interface RecipeRunResult extends InteractiveState {
80
+ /** The page state after a recipe, plus what the run did ({@link RecipeRunResult}). */
81
+ export interface RecipeServiceResult extends InteractiveState, RecipeRunResult {
82
+ /** Same array as `completedSteps`; kept for service consumers written before B2. */
65
83
  steps: RecipeStepResult[];
66
84
  }
67
85
  export interface ScriptRunResult {
@@ -82,21 +100,7 @@ export interface EvaluateResult {
82
100
  export interface FileUploadResult extends InteractiveState {
83
101
  files: string[];
84
102
  }
85
- export interface BrowserFrameSpec {
86
- selector?: string;
87
- name?: string;
88
- url?: string;
89
- }
90
- export interface BrowserLocatorSpec {
91
- selector?: string;
92
- role?: string;
93
- name?: string;
94
- text?: string;
95
- label?: string;
96
- exact?: boolean;
97
- frame?: BrowserFrameSpec;
98
- }
99
- export type BrowserTarget = string | BrowserLocatorSpec;
103
+ export type { BrowserFrameSpec, BrowserLocatorSpec, BrowserTarget } from './locator.ts';
100
104
  export interface BrowserConsoleRecord {
101
105
  type: string;
102
106
  text: string;
@@ -157,7 +161,10 @@ export interface BrowserStatus {
157
161
  opencliInstalled: boolean;
158
162
  opencliEntryPath?: string;
159
163
  automationMode: AutomationMode;
164
+ /** Tool names registered for the model under the configured toolSurface. */
160
165
  exposedTools: string[];
166
+ /** Browser actions (`group.action`) the model can run. */
167
+ exposedActions: string[];
161
168
  directInteractionPolicy: 'deny' | 'ask' | 'allow';
162
169
  mutatingRecipePolicy: 'deny' | 'ask' | 'allow';
163
170
  externalUserscriptPolicy: 'deny' | 'ask' | 'allow';
@@ -180,25 +187,154 @@ export interface BrowserStatus {
180
187
  activeUrl?: string;
181
188
  activeAuthProfile?: string;
182
189
  }
190
+ /**
191
+ * The interactive page state owned by ONE session.
192
+ *
193
+ * A single `BrowserService` instance is provided to every consumer, so before
194
+ * this existed two concurrent sessions shared one `activePage`: whichever
195
+ * session called `target.open` last owned the page, and the other session's
196
+ * `observe.read` / `script.evaluate` / `inspect.console` then operated on a
197
+ * page it never opened — including the other session's cookies and DOM.
198
+ *
199
+ * State is therefore keyed by session. The browser PROCESS stays shared on
200
+ * purpose: launching N Chromium processes costs hundreds of MB each, while one
201
+ * process with N `BrowserContext`s is what Chromium itself does for N profiles
202
+ * and is properly isolated (separate cookies, storage, cache, permissions).
203
+ * "Different tabs" would not be enough — pages in the SAME context share
204
+ * cookies and storage, so each session gets its own context, not just its own
205
+ * page.
206
+ */
207
+ interface SessionState {
208
+ context?: any;
209
+ /** The ACTIVE page: the one every action targets. Always one of `pages` while set. */
210
+ page?: any;
211
+ /** Every open page of this session's context (the page it opened plus popups), in the order they appeared. */
212
+ pages: SessionPage[];
213
+ /** Counter behind the `t1`, `t2`, ... target ids of this session. */
214
+ nextTarget: number;
215
+ /**
216
+ * Source of page generations. A page takes the next value when it is tracked and again on every main-frame
217
+ * navigation (cross-document, reload, history back/forward, `location` assignment, `pushState`/`replaceState`,
218
+ * hash changes), so values only grow and never repeat inside a session, not even across pages: an
219
+ * `expectGeneration` from one page can never match another page by coincidence. Subframe navigation and
220
+ * DOM re-rendering do not change it.
221
+ */
222
+ generationClock: number;
223
+ profile?: ResolvedAuthProfile;
224
+ rulePack?: ResolvedRulePack;
225
+ captureConsole: boolean;
226
+ captureNetwork: boolean;
227
+ capturedConsole: BrowserConsoleRecord[];
228
+ capturedRequests: BrowserRequestRecord[];
229
+ /** Monotonic tick of last use, for least-recently-used eviction. */
230
+ lastUsed: number;
231
+ }
232
+ /** One tracked page of a session. */
233
+ interface SessionPage {
234
+ id: string;
235
+ page: any;
236
+ generation: number;
237
+ }
238
+ /**
239
+ * Session key for a tool execution.
240
+ *
241
+ * `ToolExecution.agent` is documented as "the agent on whose behalf the call
242
+ * runs (set by the agent loop)". Two session identities hang off it and BOTH are
243
+ * the session, not the turn:
244
+ *
245
+ * - `agent.session.id` — the live `Agent` face exposes `readonly session:
246
+ * Session`, and `Session.id` is a `SessionId`. This is the path the
247
+ * automation-asset recorder already uses in production, so it is tried first.
248
+ * - `agent.id` — the base `Agent` contract documents `readonly id: SessionId`
249
+ * as "Session-backed Agent identity". Used as a fallback.
250
+ *
251
+ * On DSH 0.2.0-rc.2 both are present and equal (the agent loop documents `id` as the
252
+ * "shared agent/session identity"). `ToolExecution.agent` is typed as the base `Agent`,
253
+ * whose only guaranteed member is `id`, so reading the live face first and the base
254
+ * contract second is a type-level hedge rather than an old-host adaptation.
255
+ *
256
+ * A session therefore keeps ONE page across all of its turns, and two
257
+ * concurrent sessions never share one. Calls carrying no agent at all
258
+ * (automation-asset replays, non-agent dispatchers) land in one shared bucket,
259
+ * which is the pre-existing single-session behaviour rather than a new hazard.
260
+ */
261
+ export declare function sessionKeyFor(agent: SessionIdentity | undefined): string;
262
+ /** The two shapes a live Agent presents for session identity. */
263
+ export interface SessionIdentity {
264
+ readonly id?: unknown;
265
+ readonly session?: {
266
+ readonly id?: unknown;
267
+ };
268
+ }
183
269
  export declare class BrowserService {
184
270
  private readonly config;
185
271
  private browser;
186
272
  private launching?;
187
- private activeContext;
188
- private activePage;
189
- private activeProfile?;
190
- private activeRulePack?;
273
+ /** Per-session interactive state. Replaces the former global activePage. */
274
+ private readonly sessions;
275
+ /** The tracked entry (target id, generation) of every page the service created. */
276
+ private readonly pageEntries;
277
+ private clock;
191
278
  private readonly authProfiles;
192
279
  private readonly usageGovernor;
193
280
  private opencliCatalogCache?;
194
- private captureConsoleEnabled;
195
- private captureNetworkEnabled;
196
- private capturedConsole;
197
- private capturedRequests;
281
+ /** Per-session exploration journals (see src/journal.ts). Released with the session, never written to disk. */
282
+ private readonly journals;
283
+ /** How many sessions may hold a context+page at once before LRU eviction. */
284
+ private readonly sessionLimit;
198
285
  constructor(config: ResolvedConfig);
199
286
  available(): boolean;
287
+ /**
288
+ * This session's exploration journal, created on first use. It outlives `target.close` (the session is
289
+ * still the same) and is released when the session is evicted or the service closes. A session that never
290
+ * records anything costs nothing: journals are only created by the action dispatcher.
291
+ */
292
+ journalFor(session: string): SessionJournal;
293
+ /** The active page of a session as `{targetId, generation, url}`, without opening or creating anything. */
294
+ pageStamp(session: string): {
295
+ targetId?: string;
296
+ generation?: number;
297
+ url?: string;
298
+ } | undefined;
299
+ /**
300
+ * Whether the control a fill/type would hit is a secret field (password, one-time code, name that says so).
301
+ * Best effort and quick: any trouble (no page, several matches, a slow page) answers false, and the journal
302
+ * still treats secret-looking values and secret-looking locators as sensitive.
303
+ */
304
+ inputSensitivity(target: BrowserTarget, opts?: {
305
+ session?: string;
306
+ }): Promise<boolean>;
307
+ /**
308
+ * The state bucket belonging to a tool execution's session.
309
+ *
310
+ * Tools use this for the few operations that act on the bucket itself
311
+ * (`target.close`) rather than passing a key into a method.
312
+ */
313
+ sessionState(agent: SessionIdentity | undefined): SessionState | undefined;
200
314
  private assertEnabled;
201
315
  private browserConnected;
316
+ /**
317
+ * The state bucket for one session, created on demand.
318
+ *
319
+ * Eviction is least-recently-used and never touches the caller's own bucket,
320
+ * so a session cannot have its page closed out from under it by a burst of
321
+ * other sessions.
322
+ */
323
+ private state;
324
+ /**
325
+ * Look up an existing bucket WITHOUT creating one and WITHOUT evicting.
326
+ *
327
+ * Queries (`runtime.status`, `inspect.console`, `inspect.requests`) must go
328
+ * through this, not {@link state}. Creating a bucket for a session that only
329
+ * *asks* a question would both leak a slot and push a live session past the
330
+ * limit, so a read could evict the very page it was trying to describe.
331
+ */
332
+ private peek;
333
+ /** Close the least-recently-used sessions that exceed the limit. */
334
+ private evictSessions;
335
+ /** Close one session's page+context without persisting its auth state. */
336
+ private disposeState;
337
+ /** Drop all per-session state after a browser-level failure. */
202
338
  private clearActiveState;
203
339
  private handleBrowserDisconnected;
204
340
  private trackBrowser;
@@ -271,25 +407,65 @@ export declare class BrowserService {
271
407
  rulePack?: string;
272
408
  inputs?: Record<string, string>;
273
409
  }): Promise<ScriptRunResult>;
410
+ /**
411
+ * Return the calling session's live page, creating one when needed.
412
+ *
413
+ * `session` is the key from {@link sessionKeyFor}. Every read and write below
414
+ * is confined to that bucket, which is what stops one session from observing
415
+ * or mutating another's page, cookies, console or network log.
416
+ */
274
417
  private ensureActivePage;
275
- private attachCapture;
418
+ /**
419
+ * Add a page to the session's list and wire its capture and close handling.
420
+ * Idempotent: the context 'page' event and the explicit call both land here.
421
+ */
422
+ private trackPage;
276
423
  private resetCapture;
277
424
  private resolveTarget;
425
+ /**
426
+ * Run an action on a strict locator. More than one match performs nothing and
427
+ * fails with LOCATOR_AMBIGUOUS plus a summary of the first candidates.
428
+ */
429
+ private onTarget;
278
430
  private screenshotFile;
279
431
  private captureScreenshot;
432
+ /** `{targetId, generation}` of a tracked page (empty for a page the service does not track). */
433
+ private stamp;
434
+ /**
435
+ * Refuse to act when the page's generation is not the one the caller observed. Nothing has been done yet
436
+ * at this point, so the caller only needs to observe again.
437
+ */
438
+ private assertGeneration;
280
439
  private readState;
281
440
  open(url: string, opts?: {
282
441
  waitMs?: number;
283
442
  authProfile?: string;
284
443
  rulePack?: string;
285
444
  capture?: readonly ('console' | 'network')[];
445
+ session?: string;
286
446
  }): Promise<InteractiveState>;
287
447
  click(target: BrowserTarget, opts?: {
448
+ expectGeneration?: number;
288
449
  timeoutMs?: number;
289
450
  waitMs?: number;
451
+ session?: string;
290
452
  }): Promise<InteractiveState>;
291
453
  type(target: BrowserTarget, text: string, opts?: {
454
+ expectGeneration?: number;
455
+ timeoutMs?: number;
456
+ session?: string;
457
+ }): Promise<InteractiveState>;
458
+ /** Type key by key (pressSequentially), so per-key handlers fire; unlike `type`, which replaces the whole value. */
459
+ typeKeys(target: BrowserTarget, text: string, opts?: {
460
+ expectGeneration?: number;
461
+ delayMs?: number;
462
+ timeoutMs?: number;
463
+ session?: string;
464
+ }): Promise<InteractiveState>;
465
+ clear(target: BrowserTarget, opts?: {
466
+ expectGeneration?: number;
292
467
  timeoutMs?: number;
468
+ session?: string;
293
469
  }): Promise<InteractiveState>;
294
470
  wait(target: BrowserTarget | undefined, opts?: {
295
471
  urlPattern?: string;
@@ -297,34 +473,95 @@ export declare class BrowserService {
297
473
  timeMs?: number;
298
474
  state?: 'visible' | 'hidden' | 'attached' | 'detached';
299
475
  timeoutMs?: number;
476
+ session?: string;
300
477
  }): Promise<InteractiveState>;
301
478
  press(target: BrowserTarget | undefined, key: string, opts?: {
479
+ expectGeneration?: number;
302
480
  timeoutMs?: number;
481
+ session?: string;
303
482
  }): Promise<InteractiveState>;
304
483
  select(target: BrowserTarget, values: readonly string[], opts?: {
484
+ expectGeneration?: number;
305
485
  timeoutMs?: number;
486
+ session?: string;
306
487
  }): Promise<InteractiveState>;
307
488
  check(target: BrowserTarget, checked?: boolean, opts?: {
489
+ expectGeneration?: number;
308
490
  timeoutMs?: number;
491
+ session?: string;
309
492
  }): Promise<InteractiveState>;
310
493
  hover(target: BrowserTarget, opts?: {
494
+ expectGeneration?: number;
311
495
  timeoutMs?: number;
312
496
  waitMs?: number;
497
+ session?: string;
313
498
  }): Promise<InteractiveState>;
314
499
  setFiles(target: BrowserTarget, files: readonly string[], opts?: {
500
+ expectGeneration?: number;
315
501
  timeoutMs?: number;
502
+ session?: string;
316
503
  }): Promise<FileUploadResult>;
317
504
  evaluate(expression: string, opts?: {
318
505
  timeoutMs?: number;
506
+ session?: string;
319
507
  }): Promise<EvaluateResult>;
508
+ /**
509
+ * The pages this session holds: the one it opened plus any popup that page
510
+ * (or a later one) opened in the same context. Pure query: it never creates
511
+ * a bucket and never changes the active page.
512
+ */
513
+ listTargets(opts?: {
514
+ session?: string;
515
+ }): Promise<{
516
+ targets: {
517
+ id: string;
518
+ url: string;
519
+ title: string;
520
+ active: boolean;
521
+ generation: number;
522
+ }[];
523
+ }>;
524
+ /** Make one of this session's pages the active page: every later action, read, and screenshot targets it. */
525
+ selectTarget(id: string, opts?: {
526
+ session?: string;
527
+ }): Promise<InteractiveState & {
528
+ id: string;
529
+ }>;
320
530
  scroll(deltaY: number, opts?: {
531
+ expectGeneration?: number;
321
532
  waitMs?: number;
533
+ session?: string;
534
+ }): Promise<InteractiveState>;
535
+ read(opts?: {
536
+ session?: string;
322
537
  }): Promise<InteractiveState>;
323
- read(): Promise<InteractiveState>;
538
+ /**
539
+ * Run a read of the page. If the page navigates under it ("Execution context was destroyed"), the read is
540
+ * tried once more on the new document; if that fails the same way the page is still moving, which is a
541
+ * stale observation (TARGET_STALE), not a failed action. Nothing a read does changes the page.
542
+ */
543
+ private readingNavigation;
544
+ /**
545
+ * Structured observation of the active page (or of one element's subtree): readable text, controls, links,
546
+ * and tables, each section bounded by `maxItems` and the whole record by `maxBytes` (see src/observe.ts).
547
+ * With only the default `content` section, no scope and no limits it is exactly {@link read}.
548
+ */
549
+ observe(opts?: {
550
+ sections?: readonly ObserveSection[];
551
+ target?: BrowserTarget;
552
+ region?: ObserveRegion;
553
+ maxItems?: number;
554
+ maxBytes?: number;
555
+ includeValues?: boolean;
556
+ timeoutMs?: number;
557
+ session?: string;
558
+ }): Promise<Record<string, unknown>>;
559
+ private observeOnce;
324
560
  consoleMessages(opts?: {
325
561
  level?: string;
326
562
  limit?: number;
327
563
  clear?: boolean;
564
+ session?: string;
328
565
  }): {
329
566
  enabled: boolean;
330
567
  records: BrowserConsoleRecord[];
@@ -332,21 +569,61 @@ export declare class BrowserService {
332
569
  networkRequests(opts?: {
333
570
  limit?: number;
334
571
  clear?: boolean;
572
+ session?: string;
335
573
  }): {
336
574
  enabled: boolean;
337
575
  records: BrowserRequestRecord[];
338
576
  };
339
- screenshot(options?: BrowserScreenshotOptions): Promise<{
577
+ screenshot(options?: BrowserScreenshotOptions & {
578
+ session?: string;
579
+ }): Promise<{
340
580
  path: string;
341
581
  }>;
342
- recipe(steps: readonly BrowserRecipeStep[], opts?: {
582
+ /**
583
+ * Run a recipe on the session's page.
584
+ *
585
+ * A business failure comes back as a value (`executionStatus`, `failedStep`,
586
+ * `completedSteps`, `effects`), never as an exception; only problems before
587
+ * any step ran (no page, navigation failure, a malformed recipe) throw.
588
+ *
589
+ * Cancelling stops this recipe only: the session page stays open and usable.
590
+ * A step that is already running cannot be interrupted, so the call returns
591
+ * once that step has returned. The caller therefore still holds the session
592
+ * (browser_call is serialized per agent) until the page is quiet again.
593
+ */
594
+ recipe(steps: readonly AnyRecipeStep[], opts?: {
343
595
  url?: string;
344
596
  waitMs?: number;
345
597
  authProfile?: string;
346
598
  rulePack?: string;
347
599
  signal?: AbortSignal;
348
- }): Promise<RecipeRunResult>;
349
- closePage(): Promise<void>;
350
- status(): Promise<BrowserStatus>;
600
+ session?: string;
601
+ legacyRecipe?: boolean;
602
+ /** 2: strict LocatorSpec steps, goto/clear, postconditions (see src/automation-v2.ts). Default 1. */
603
+ schemaVersion?: 1 | 2;
604
+ postconditions?: readonly Postcondition[];
605
+ outputSchema?: readonly OutputSpec[];
606
+ /** v2 goto may land on these domains. */
607
+ allowedDomains?: readonly string[];
608
+ /** v2 goto may also stay on the origin the recipe started on (inline recipes; stored assets are limited to their domains). */
609
+ gotoSameOrigin?: boolean;
610
+ /** Run in a brand-new BrowserContext (its own cookies and storage) that is closed afterwards; the session's page is not touched. Needs `url`. */
611
+ isolated?: boolean;
612
+ }): Promise<RecipeServiceResult>;
613
+ /** A recipe in its own context: nothing of the caller's session (page, cookies, storage, journal) is shared or changed. */
614
+ private isolatedRecipe;
615
+ private runRecipeOn;
616
+ /**
617
+ * Close one session's page.
618
+ *
619
+ * `session` is the caller's bucket: a tool call closes only the page that
620
+ * session opened. Only a caller with no session (a service consumer outside
621
+ * the agent loop) reaches the shared bucket, which preserves the historical
622
+ * behaviour for those consumers.
623
+ */
624
+ closePage(session?: SessionState): Promise<void>;
625
+ status(opts?: {
626
+ session?: string;
627
+ }): Promise<BrowserStatus>;
351
628
  close(): Promise<void>;
352
629
  }