@anweat/dsh-browser 0.1.17 → 0.2.0-rc.1

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 (162) hide show
  1. package/README.md +238 -71
  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 +4 -0
  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 +67 -6
  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 +304 -32
  91. package/lib/browser-service.js +631 -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.js +1914 -329
  117. package/lib/client.js.map +1 -1
  118. package/lib/config.d.ts +50 -1
  119. package/lib/config.js +31 -1
  120. package/lib/config.js.map +1 -1
  121. package/lib/dialogs.d.ts +37 -0
  122. package/lib/dialogs.js +73 -0
  123. package/lib/dialogs.js.map +1 -0
  124. package/lib/freedom.d.ts +23 -7
  125. package/lib/freedom.js +48 -59
  126. package/lib/freedom.js.map +1 -1
  127. package/lib/index.d.ts +4 -4
  128. package/lib/index.js +36 -10
  129. package/lib/index.js.map +1 -1
  130. package/lib/journal.d.ts +109 -0
  131. package/lib/journal.js +238 -0
  132. package/lib/journal.js.map +1 -0
  133. package/lib/locator.d.ts +82 -0
  134. package/lib/locator.js +190 -0
  135. package/lib/locator.js.map +1 -0
  136. package/lib/observe-script.d.ts +27 -0
  137. package/lib/observe-script.js +565 -0
  138. package/lib/observe-script.js.map +1 -0
  139. package/lib/observe.d.ts +179 -0
  140. package/lib/observe.js +368 -0
  141. package/lib/observe.js.map +1 -0
  142. package/lib/policy-keys.d.ts +30 -0
  143. package/lib/policy-keys.js +34 -0
  144. package/lib/policy-keys.js.map +1 -0
  145. package/lib/prompt-limits.d.ts +30 -0
  146. package/lib/prompt-limits.js +31 -0
  147. package/lib/prompt-limits.js.map +1 -0
  148. package/lib/prompts.d.ts +131 -0
  149. package/lib/prompts.js +295 -0
  150. package/lib/prompts.js.map +1 -0
  151. package/lib/skill.d.ts +100 -0
  152. package/lib/skill.js +181 -0
  153. package/lib/skill.js.map +1 -0
  154. package/lib/tool-defs.d.ts +72 -0
  155. package/lib/tool-defs.js +67 -0
  156. package/lib/tool-defs.js.map +1 -0
  157. package/lib/tools.d.ts +19 -4
  158. package/lib/tools.js +124 -922
  159. package/lib/tools.js.map +1 -1
  160. package/package.json +5 -1
  161. package/scripts/dump-prompts.mjs +29 -0
  162. package/scripts/measure-tool-surface.mjs +190 -0
@@ -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,149 @@ 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
+ * A session therefore keeps ONE page across all of its turns, and two
252
+ * concurrent sessions never share one. Calls carrying no agent at all
253
+ * (automation-asset replays, non-agent dispatchers) land in one shared bucket,
254
+ * which is the pre-existing single-session behaviour rather than a new hazard.
255
+ */
256
+ export declare function sessionKeyFor(agent: SessionIdentity | undefined): string;
257
+ /** The two shapes a live Agent presents for session identity. */
258
+ export interface SessionIdentity {
259
+ readonly id?: unknown;
260
+ readonly session?: {
261
+ readonly id?: unknown;
262
+ };
263
+ }
183
264
  export declare class BrowserService {
184
265
  private readonly config;
185
266
  private browser;
186
267
  private launching?;
187
- private activeContext;
188
- private activePage;
189
- private activeProfile?;
190
- private activeRulePack?;
268
+ /** Per-session interactive state. Replaces the former global activePage. */
269
+ private readonly sessions;
270
+ /** The tracked entry (target id, generation) of every page the service created. */
271
+ private readonly pageEntries;
272
+ private clock;
191
273
  private readonly authProfiles;
192
274
  private readonly usageGovernor;
193
275
  private opencliCatalogCache?;
194
- private captureConsoleEnabled;
195
- private captureNetworkEnabled;
196
- private capturedConsole;
197
- private capturedRequests;
276
+ /** Per-session exploration journals (see src/journal.ts). Released with the session, never written to disk. */
277
+ private readonly journals;
278
+ /** How many sessions may hold a context+page at once before LRU eviction. */
279
+ private readonly sessionLimit;
198
280
  constructor(config: ResolvedConfig);
199
281
  available(): boolean;
282
+ /**
283
+ * This session's exploration journal, created on first use. It outlives `target.close` (the session is
284
+ * still the same) and is released when the session is evicted or the service closes. A session that never
285
+ * records anything costs nothing: journals are only created by the action dispatcher.
286
+ */
287
+ journalFor(session: string): SessionJournal;
288
+ /** The active page of a session as `{targetId, generation, url}`, without opening or creating anything. */
289
+ pageStamp(session: string): {
290
+ targetId?: string;
291
+ generation?: number;
292
+ url?: string;
293
+ } | undefined;
294
+ /**
295
+ * Whether the control a fill/type would hit is a secret field (password, one-time code, name that says so).
296
+ * Best effort and quick: any trouble (no page, several matches, a slow page) answers false, and the journal
297
+ * still treats secret-looking values and secret-looking locators as sensitive.
298
+ */
299
+ inputSensitivity(target: BrowserTarget, opts?: {
300
+ session?: string;
301
+ }): Promise<boolean>;
302
+ /**
303
+ * The state bucket belonging to a tool execution's session.
304
+ *
305
+ * Tools use this for the few operations that act on the bucket itself
306
+ * (`target.close`) rather than passing a key into a method.
307
+ */
308
+ sessionState(agent: SessionIdentity | undefined): SessionState | undefined;
200
309
  private assertEnabled;
201
310
  private browserConnected;
311
+ /**
312
+ * The state bucket for one session, created on demand.
313
+ *
314
+ * Eviction is least-recently-used and never touches the caller's own bucket,
315
+ * so a session cannot have its page closed out from under it by a burst of
316
+ * other sessions.
317
+ */
318
+ private state;
319
+ /**
320
+ * Look up an existing bucket WITHOUT creating one and WITHOUT evicting.
321
+ *
322
+ * Queries (`runtime.status`, `inspect.console`, `inspect.requests`) must go
323
+ * through this, not {@link state}. Creating a bucket for a session that only
324
+ * *asks* a question would both leak a slot and push a live session past the
325
+ * limit, so a read could evict the very page it was trying to describe.
326
+ */
327
+ private peek;
328
+ /** Close the least-recently-used sessions that exceed the limit. */
329
+ private evictSessions;
330
+ /** Close one session's page+context without persisting its auth state. */
331
+ private disposeState;
332
+ /** Drop all per-session state after a browser-level failure. */
202
333
  private clearActiveState;
203
334
  private handleBrowserDisconnected;
204
335
  private trackBrowser;
@@ -271,25 +402,65 @@ export declare class BrowserService {
271
402
  rulePack?: string;
272
403
  inputs?: Record<string, string>;
273
404
  }): Promise<ScriptRunResult>;
405
+ /**
406
+ * Return the calling session's live page, creating one when needed.
407
+ *
408
+ * `session` is the key from {@link sessionKeyFor}. Every read and write below
409
+ * is confined to that bucket, which is what stops one session from observing
410
+ * or mutating another's page, cookies, console or network log.
411
+ */
274
412
  private ensureActivePage;
275
- private attachCapture;
413
+ /**
414
+ * Add a page to the session's list and wire its capture and close handling.
415
+ * Idempotent: the context 'page' event and the explicit call both land here.
416
+ */
417
+ private trackPage;
276
418
  private resetCapture;
277
419
  private resolveTarget;
420
+ /**
421
+ * Run an action on a strict locator. More than one match performs nothing and
422
+ * fails with LOCATOR_AMBIGUOUS plus a summary of the first candidates.
423
+ */
424
+ private onTarget;
278
425
  private screenshotFile;
279
426
  private captureScreenshot;
427
+ /** `{targetId, generation}` of a tracked page (empty for a page the service does not track). */
428
+ private stamp;
429
+ /**
430
+ * Refuse to act when the page's generation is not the one the caller observed. Nothing has been done yet
431
+ * at this point, so the caller only needs to observe again.
432
+ */
433
+ private assertGeneration;
280
434
  private readState;
281
435
  open(url: string, opts?: {
282
436
  waitMs?: number;
283
437
  authProfile?: string;
284
438
  rulePack?: string;
285
439
  capture?: readonly ('console' | 'network')[];
440
+ session?: string;
286
441
  }): Promise<InteractiveState>;
287
442
  click(target: BrowserTarget, opts?: {
443
+ expectGeneration?: number;
288
444
  timeoutMs?: number;
289
445
  waitMs?: number;
446
+ session?: string;
290
447
  }): Promise<InteractiveState>;
291
448
  type(target: BrowserTarget, text: string, opts?: {
449
+ expectGeneration?: number;
450
+ timeoutMs?: number;
451
+ session?: string;
452
+ }): Promise<InteractiveState>;
453
+ /** Type key by key (pressSequentially), so per-key handlers fire; unlike `type`, which replaces the whole value. */
454
+ typeKeys(target: BrowserTarget, text: string, opts?: {
455
+ expectGeneration?: number;
456
+ delayMs?: number;
457
+ timeoutMs?: number;
458
+ session?: string;
459
+ }): Promise<InteractiveState>;
460
+ clear(target: BrowserTarget, opts?: {
461
+ expectGeneration?: number;
292
462
  timeoutMs?: number;
463
+ session?: string;
293
464
  }): Promise<InteractiveState>;
294
465
  wait(target: BrowserTarget | undefined, opts?: {
295
466
  urlPattern?: string;
@@ -297,34 +468,95 @@ export declare class BrowserService {
297
468
  timeMs?: number;
298
469
  state?: 'visible' | 'hidden' | 'attached' | 'detached';
299
470
  timeoutMs?: number;
471
+ session?: string;
300
472
  }): Promise<InteractiveState>;
301
473
  press(target: BrowserTarget | undefined, key: string, opts?: {
474
+ expectGeneration?: number;
302
475
  timeoutMs?: number;
476
+ session?: string;
303
477
  }): Promise<InteractiveState>;
304
478
  select(target: BrowserTarget, values: readonly string[], opts?: {
479
+ expectGeneration?: number;
305
480
  timeoutMs?: number;
481
+ session?: string;
306
482
  }): Promise<InteractiveState>;
307
483
  check(target: BrowserTarget, checked?: boolean, opts?: {
484
+ expectGeneration?: number;
308
485
  timeoutMs?: number;
486
+ session?: string;
309
487
  }): Promise<InteractiveState>;
310
488
  hover(target: BrowserTarget, opts?: {
489
+ expectGeneration?: number;
311
490
  timeoutMs?: number;
312
491
  waitMs?: number;
492
+ session?: string;
313
493
  }): Promise<InteractiveState>;
314
494
  setFiles(target: BrowserTarget, files: readonly string[], opts?: {
495
+ expectGeneration?: number;
315
496
  timeoutMs?: number;
497
+ session?: string;
316
498
  }): Promise<FileUploadResult>;
317
499
  evaluate(expression: string, opts?: {
318
500
  timeoutMs?: number;
501
+ session?: string;
319
502
  }): Promise<EvaluateResult>;
503
+ /**
504
+ * The pages this session holds: the one it opened plus any popup that page
505
+ * (or a later one) opened in the same context. Pure query: it never creates
506
+ * a bucket and never changes the active page.
507
+ */
508
+ listTargets(opts?: {
509
+ session?: string;
510
+ }): Promise<{
511
+ targets: {
512
+ id: string;
513
+ url: string;
514
+ title: string;
515
+ active: boolean;
516
+ generation: number;
517
+ }[];
518
+ }>;
519
+ /** Make one of this session's pages the active page: every later action, read, and screenshot targets it. */
520
+ selectTarget(id: string, opts?: {
521
+ session?: string;
522
+ }): Promise<InteractiveState & {
523
+ id: string;
524
+ }>;
320
525
  scroll(deltaY: number, opts?: {
526
+ expectGeneration?: number;
321
527
  waitMs?: number;
528
+ session?: string;
529
+ }): Promise<InteractiveState>;
530
+ read(opts?: {
531
+ session?: string;
322
532
  }): Promise<InteractiveState>;
323
- read(): Promise<InteractiveState>;
533
+ /**
534
+ * Run a read of the page. If the page navigates under it ("Execution context was destroyed"), the read is
535
+ * tried once more on the new document; if that fails the same way the page is still moving, which is a
536
+ * stale observation (TARGET_STALE), not a failed action. Nothing a read does changes the page.
537
+ */
538
+ private readingNavigation;
539
+ /**
540
+ * Structured observation of the active page (or of one element's subtree): readable text, controls, links,
541
+ * and tables, each section bounded by `maxItems` and the whole record by `maxBytes` (see src/observe.ts).
542
+ * With only the default `content` section, no scope and no limits it is exactly {@link read}.
543
+ */
544
+ observe(opts?: {
545
+ sections?: readonly ObserveSection[];
546
+ target?: BrowserTarget;
547
+ region?: ObserveRegion;
548
+ maxItems?: number;
549
+ maxBytes?: number;
550
+ includeValues?: boolean;
551
+ timeoutMs?: number;
552
+ session?: string;
553
+ }): Promise<Record<string, unknown>>;
554
+ private observeOnce;
324
555
  consoleMessages(opts?: {
325
556
  level?: string;
326
557
  limit?: number;
327
558
  clear?: boolean;
559
+ session?: string;
328
560
  }): {
329
561
  enabled: boolean;
330
562
  records: BrowserConsoleRecord[];
@@ -332,21 +564,61 @@ export declare class BrowserService {
332
564
  networkRequests(opts?: {
333
565
  limit?: number;
334
566
  clear?: boolean;
567
+ session?: string;
335
568
  }): {
336
569
  enabled: boolean;
337
570
  records: BrowserRequestRecord[];
338
571
  };
339
- screenshot(options?: BrowserScreenshotOptions): Promise<{
572
+ screenshot(options?: BrowserScreenshotOptions & {
573
+ session?: string;
574
+ }): Promise<{
340
575
  path: string;
341
576
  }>;
342
- recipe(steps: readonly BrowserRecipeStep[], opts?: {
577
+ /**
578
+ * Run a recipe on the session's page.
579
+ *
580
+ * A business failure comes back as a value (`executionStatus`, `failedStep`,
581
+ * `completedSteps`, `effects`), never as an exception; only problems before
582
+ * any step ran (no page, navigation failure, a malformed recipe) throw.
583
+ *
584
+ * Cancelling stops this recipe only: the session page stays open and usable.
585
+ * A step that is already running cannot be interrupted, so the call returns
586
+ * once that step has returned. The caller therefore still holds the session
587
+ * (browser_call is serialized per agent) until the page is quiet again.
588
+ */
589
+ recipe(steps: readonly AnyRecipeStep[], opts?: {
343
590
  url?: string;
344
591
  waitMs?: number;
345
592
  authProfile?: string;
346
593
  rulePack?: string;
347
594
  signal?: AbortSignal;
348
- }): Promise<RecipeRunResult>;
349
- closePage(): Promise<void>;
350
- status(): Promise<BrowserStatus>;
595
+ session?: string;
596
+ legacyRecipe?: boolean;
597
+ /** 2: strict LocatorSpec steps, goto/clear, postconditions (see src/automation-v2.ts). Default 1. */
598
+ schemaVersion?: 1 | 2;
599
+ postconditions?: readonly Postcondition[];
600
+ outputSchema?: readonly OutputSpec[];
601
+ /** v2 goto may land on these domains. */
602
+ allowedDomains?: readonly string[];
603
+ /** v2 goto may also stay on the origin the recipe started on (inline recipes; stored assets are limited to their domains). */
604
+ gotoSameOrigin?: boolean;
605
+ /** Run in a brand-new BrowserContext (its own cookies and storage) that is closed afterwards; the session's page is not touched. Needs `url`. */
606
+ isolated?: boolean;
607
+ }): Promise<RecipeServiceResult>;
608
+ /** A recipe in its own context: nothing of the caller's session (page, cookies, storage, journal) is shared or changed. */
609
+ private isolatedRecipe;
610
+ private runRecipeOn;
611
+ /**
612
+ * Close one session's page.
613
+ *
614
+ * `session` is the caller's bucket: a tool call closes only the page that
615
+ * session opened. Only a caller with no session (a service consumer outside
616
+ * the agent loop) reaches the shared bucket, which preserves the historical
617
+ * behaviour for those consumers.
618
+ */
619
+ closePage(session?: SessionState): Promise<void>;
620
+ status(opts?: {
621
+ session?: string;
622
+ }): Promise<BrowserStatus>;
351
623
  close(): Promise<void>;
352
624
  }