dsh-browser-plus 0.0.0-stage → 0.5.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 (89) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/LICENSE +22 -0
  3. package/NOTICE.md +7 -0
  4. package/README.en.md +119 -0
  5. package/README.md +118 -2
  6. package/assets/dsh-browser-plus-256.png +0 -0
  7. package/assets/dsh-browser-plus-512.png +0 -0
  8. package/assets/dsh-browser-plus-small.svg +9 -0
  9. package/assets/dsh-browser-plus.ico +0 -0
  10. package/assets/dsh-browser-plus.svg +11 -0
  11. package/assets/readme-workspace.png +0 -0
  12. package/client/index.js +185 -0
  13. package/cordis.patch.yml +41 -0
  14. package/docs/MIGRATION.md +48 -0
  15. package/docs/README.md +22 -0
  16. package/docs/SOAK-CHECKLIST.md +98 -0
  17. package/docs/architecture.md +88 -0
  18. package/docs/tool-reference.md +126 -0
  19. package/docs/user-guide.md +137 -0
  20. package/docs/why-browser.md +45 -0
  21. package/lib/browser/runtime.d.ts +238 -0
  22. package/lib/browser/runtime.js +330 -0
  23. package/lib/browser/types.d.ts +758 -0
  24. package/lib/browser/types.js +18 -0
  25. package/lib/browser-electron/auth-cookies.d.ts +54 -0
  26. package/lib/browser-electron/auth-cookies.js +83 -0
  27. package/lib/browser-electron/chrome-state.d.ts +211 -0
  28. package/lib/browser-electron/chrome-state.js +12 -0
  29. package/lib/browser-electron/entry.d.ts +73 -0
  30. package/lib/browser-electron/entry.js +65 -0
  31. package/lib/browser-electron/fingerprint.d.ts +29 -0
  32. package/lib/browser-electron/fingerprint.js +42 -0
  33. package/lib/browser-electron/host-main.d.ts +19 -0
  34. package/lib/browser-electron/host-main.js +2691 -0
  35. package/lib/browser-electron/icon.d.ts +11 -0
  36. package/lib/browser-electron/icon.js +23 -0
  37. package/lib/browser-electron/page-chrome.d.ts +21 -0
  38. package/lib/browser-electron/page-chrome.js +2269 -0
  39. package/lib/browser-electron/provider.d.ts +767 -0
  40. package/lib/browser-electron/provider.js +2825 -0
  41. package/lib/browser-electron/remote-host.d.ts +145 -0
  42. package/lib/browser-electron/remote-host.js +993 -0
  43. package/lib/browser-electron/task-summary.d.ts +2 -0
  44. package/lib/browser-electron/task-summary.js +12 -0
  45. package/lib/browser-electron/task-thumbnail.d.ts +11 -0
  46. package/lib/browser-electron/task-thumbnail.js +9 -0
  47. package/lib/browser-electron/write-guard.d.ts +41 -0
  48. package/lib/browser-electron/write-guard.js +123 -0
  49. package/lib/client.js +185 -0
  50. package/lib/command-browser/index.d.ts +20 -0
  51. package/lib/command-browser/index.js +35 -0
  52. package/lib/http-browser/index.d.ts +28 -0
  53. package/lib/http-browser/index.js +110 -0
  54. package/lib/index.d.ts +27 -0
  55. package/lib/index.js +25 -0
  56. package/lib/task-todos/index.d.ts +25 -0
  57. package/lib/task-todos/index.js +100 -0
  58. package/lib/tool-browser/index.d.ts +31 -0
  59. package/lib/tool-browser/index.js +2026 -0
  60. package/package.json +120 -4
  61. package/screenshots.json +3 -0
  62. package/scripts/build-client.mjs +20 -0
  63. package/scripts/build-icons.mjs +80 -0
  64. package/scripts/capture-window.ps1 +79 -0
  65. package/scripts/crop-image.ps1 +20 -0
  66. package/scripts/smoke-browser-tools.mjs +2051 -0
  67. package/scripts/smoke-chrome-world.mjs +65 -0
  68. package/scripts/smoke-electron-host.mjs +50 -0
  69. package/scripts/test-orb-drag.mjs +83 -0
  70. package/src/browser/runtime.ts +506 -0
  71. package/src/browser/types.ts +741 -0
  72. package/src/browser-electron/auth-cookies.ts +125 -0
  73. package/src/browser-electron/chrome-state.ts +192 -0
  74. package/src/browser-electron/entry.ts +125 -0
  75. package/src/browser-electron/fingerprint.ts +45 -0
  76. package/src/browser-electron/host-main.ts +2526 -0
  77. package/src/browser-electron/icon.ts +26 -0
  78. package/src/browser-electron/page-chrome.ts +2281 -0
  79. package/src/browser-electron/provider.ts +3366 -0
  80. package/src/browser-electron/remote-host.ts +1051 -0
  81. package/src/browser-electron/task-summary.ts +10 -0
  82. package/src/browser-electron/task-thumbnail.ts +17 -0
  83. package/src/browser-electron/write-guard.ts +134 -0
  84. package/src/command-browser/index.ts +61 -0
  85. package/src/http-browser/index.ts +139 -0
  86. package/src/index.ts +65 -0
  87. package/src/task-todos/index.ts +114 -0
  88. package/src/tool-browser/index.ts +2071 -0
  89. package/src/types/electron-shim.d.ts +143 -0
@@ -0,0 +1,767 @@
1
+ /**
2
+ * Electron-backed browser provider: `WebContentsView` sessions driven over
3
+ * `webContents.debugger` (CDP). The provider itself does not import Electron — it operates through the {@link ElectronBrowserViewHost} seam, which the
4
+ * desktop shell implements with real Electron objects. That keeps this
5
+ * package testable under plain Node and leaves the Electron dependency to the
6
+ * shell that owns the `BrowserWindow`.
7
+ * @module dsh-browser-plus/browser-electron
8
+ */
9
+ import type { BrowserChallenge, BrowserClearAuthRequest, BrowserClearAuthResult, BrowserContentRequest, BrowserContentResult, BrowserDragRequest, BrowserDragResult, BrowserPointerResult, BrowserPointerTarget, BrowserExecuteRequest, BrowserExecuteResult, BrowserFillRequest, BrowserFillResult, BrowserHandoffState, BrowserHistoryEntry, BrowserOpenOptions, BrowserOpenRequest, BrowserPressKeyRequest, BrowserProvider, BrowserRefRequest, BrowserScrapeRequest, BrowserScrapeStatus, BrowserScrollIntoViewRequest, BrowserScrollRequest, BrowserScrollResult, BrowserSessionId, BrowserSnapshotResult, BrowserSpaceInfo, BrowserTab, BrowserTaskInfo, BrowserTaskUpdate, BrowserUploadFileRequest, BrowserUploadFileResult, BrowserPdfRequest, BrowserPdfResult, BrowserHighlightRequest, BrowserHighlightResult, BrowserWaitForRequest, BrowserWaitForResult, BrowserTaskTodo, ExportedCookie } from '../browser/types.ts';
10
+ /** Stable provider id registered with `ctx.browser`. */
11
+ export declare const ELECTRON_BROWSER_PROVIDER_ID = "electron";
12
+ /**
13
+ * One tab request raised by the injected chrome because a human used it.
14
+ *
15
+ * The host owns the pixels, the tab strip and the per-view secret, so it is the
16
+ * only party that can authenticate such a request; the provider owns the tab
17
+ * model, so it is the only party that may act on one. `tabId` is the host's own
18
+ * view id, which is also the provider's `ElectronViewHandle.id`, so no extra
19
+ * mapping table is needed.
20
+ */
21
+ export interface ChromeHostEvent {
22
+ /** For a 'move-tab' event: the index the tab was dropped at, after removal. */
23
+ readonly toIndex?: number;
24
+ readonly type: 'new-tab' | 'close-tab' | 'activate-tab' | 'move-tab';
25
+ /** Task key of the chrome that raised it. */
26
+ readonly taskKey: string;
27
+ /** Host view id of the tab; absent for `new-tab`. */
28
+ readonly tabId?: string;
29
+ /** For `new-tab`: open this http(s) url in the new tab (a bookmark click). */
30
+ readonly url?: string;
31
+ }
32
+ /**
33
+ * The minimal Electron surface this provider needs. Implemented by the
34
+ * desktop shell with a real `WebContentsView`; a fake implements it in tests.
35
+ */
36
+ export interface ElectronBrowserViewHost {
37
+ /**
38
+ * Subscribe to tab requests the host raises without being asked (a human
39
+ * clicking the chrome's own `+`, `×` or tab strip). Optional: a host whose
40
+ * chrome cannot speak first simply never raises one.
41
+ * @param listener - invoked once per authenticated chrome request.
42
+ */
43
+ onChromeEvent?(listener: (event: ChromeHostEvent) => void): void;
44
+ /**
45
+ * Create a new browser view and return a handle to its webContents-like
46
+ * surface. `key` (default 'default') identifies an isolated browser task in
47
+ * the shared BrowserWindow; `label` names that task. The host owns view
48
+ * attachment, sizing, task visibility, and removal; the provider owns
49
+ * CDP-driven behavior.
50
+ */
51
+ createView(key?: string, label?: string): ElectronViewHandle;
52
+ /**
53
+ * Destroy a view created by this host. Called on session close; idempotent
54
+ * for an already-destroyed view.
55
+ * @param handle - the handle returned by {@link createView}.
56
+ */
57
+ destroyView(handle: ElectronViewHandle): void;
58
+ /**
59
+ * Notify the host that this session selected a tab. In the shared-window
60
+ * host, a background task updates its active view without changing the
61
+ * human-selected visible task. Optional for headless/probe hosts.
62
+ * @param handle - the handle selected by its session.
63
+ */
64
+ showView?(handle: ElectronViewHandle): void;
65
+ /**
66
+ * Send a CDP `Input.*` command to the host's chrome frame view.
67
+ *
68
+ * The frame is not a tab and has no handle, so this is its own channel. It
69
+ * exists because the chrome can live in a view of its own (which is what lets
70
+ * the page viewport really shrink): input aimed at a page never reaches that
71
+ * view, and CDP input targets a webContents regardless of view stacking, so
72
+ * the page cannot stand in for it. Optional for hosts without a frame view.
73
+ * @param method - a CDP Input domain command, e.g. 'Input.dispatchMouseEvent'.
74
+ * @param params - that command's parameters.
75
+ */
76
+ chromeInput?(method: string, params?: Record<string, unknown>): Promise<void>;
77
+ /**
78
+ * Evaluate an expression inside the chrome frame's own document.
79
+ *
80
+ * The frame is a view of its own, so no page-directed call can read it. This is
81
+ * how the tests assert the toolbar's own animations instead of inferring them
82
+ * from the page's copy of the chrome. Optional for hosts without a frame view.
83
+ * @param expression - JavaScript evaluated in the frame, by value.
84
+ */
85
+ chromeEval?(expression: string): Promise<unknown>;
86
+ /**
87
+ * Append one operation to the human-facing trail for a view. Optional.
88
+ * @param viewId - the view to attribute the operation to.
89
+ * @param entry - the trail entry ({ action, params, ok, at }).
90
+ */
91
+ trace?(viewId: string, entry: unknown): void;
92
+ /**
93
+ * Cheap local usability probe for this host. MUST NOT make network calls and
94
+ * MUST NOT throw (a throw is reported as unavailable). Optional: a host that
95
+ * omits it is assumed usable, which keeps a desktop shell's shell-owned
96
+ * viewHost and test fakes working. A self-hosted host reports false when the
97
+ * pinned Electron binary cannot be resolved, so the seam can pick another
98
+ * provider (BROWSER_PROVIDER_UNAVAILABLE / BROWSER_PROVIDER_AMBIGUOUS)
99
+ * instead of failing later on the first open().
100
+ */
101
+ isAvailable?(): boolean;
102
+ /** List browser tasks with their labels. Legacy method name retained for compatibility. */
103
+ listWindows?(): Promise<Array<{
104
+ key: string;
105
+ label: string;
106
+ }>>;
107
+ /** List task summaries when the host exposes a visible workspace. */
108
+ listTasks?(): Promise<readonly BrowserTaskInfo[]>;
109
+ /** Read one task summary from the visible workspace. */
110
+ getTask?(key: string): Promise<BrowserTaskInfo | undefined>;
111
+ /** Apply a task status/control update to the visible workspace. */
112
+ updateTask?(key: string, update: BrowserTaskUpdate): Promise<BrowserTaskInfo | undefined>;
113
+ /** Mirror one task's Agent todo list into the visible workspace (floating orb). */
114
+ setTaskTodos?(key: string, todos: readonly BrowserTaskTodo[]): Promise<void>;
115
+ }
116
+ /**
117
+ * A CDP-capable view handle. This is the subset of Electron's
118
+ * `WebContents`/`WebContentsView` the provider drives; the shell's real
119
+ * implementation adapts `webContents.debugger` to it.
120
+ */
121
+ export interface ElectronViewHandle {
122
+ /** Unique id of the backing view, used for diagnostics. */
123
+ readonly id: string;
124
+ /**
125
+ * Send one CDP command and resolve with its result. Rejects when the
126
+ * debugger is not attached or the command fails.
127
+ * @param method - CDP method, e.g. `Page.navigate`.
128
+ * @param params - CDP command parameters.
129
+ * @returns the CDP `result` object.
130
+ */
131
+ sendCommand(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
132
+ /**
133
+ * Read the most recent auto-accepted JS dialog for this view (and clear it).
134
+ * Optional: hosts without JS-dialog supervision omit it.
135
+ * @returns the dialog detail ({ type, message, prompt? }) or null.
136
+ */
137
+ clearDialog?(): Promise<unknown>;
138
+ /** Optional: hosts without JS-dialog supervision omit it. */
139
+ setDialogPolicy?(policy: DialogPolicy): Promise<unknown>;
140
+ /** Optional: bounded console capture. */
141
+ readConsole?(clear?: boolean): Promise<unknown>;
142
+ /** Optional: bounded network capture. */
143
+ readNetwork?(clear?: boolean): Promise<unknown>;
144
+ /**
145
+ * Remove cookies matching a domain/name filter. Optional: hosts without a
146
+ * deletable cookie store omit it.
147
+ */
148
+ clearCookies?(filter: {
149
+ readonly domain?: string;
150
+ readonly name?: string;
151
+ readonly all?: boolean;
152
+ }): Promise<{
153
+ readonly removed: number;
154
+ readonly names: readonly string[];
155
+ }>;
156
+ /** Set this view's browser task label; it titles the shared window only when selected. Optional. */
157
+ label?(label: string): Promise<void>;
158
+ /**
159
+ * Re-apply the host's own page chrome to the current document. Optional: a host
160
+ * that does not own the chrome omits it, and the provider then injects its own
161
+ * tokenless copy as a fallback.
162
+ */
163
+ reinstallChrome?(): Promise<void>;
164
+ /**
165
+ * Print this view to a PDF and return it base64-encoded. Optional: a host
166
+ * with no native print path omits it, and the provider falls back to CDP.
167
+ *
168
+ * Electron's debugger does NOT implement CDP `Page.printToPDF` (it answers
169
+ * "'Page.printToPDF' wasn't found"), so the PDF path has to go through the
170
+ * host's own `webContents.printToPDF`.
171
+ * @param options - Electron PrintToPDFOptions (pageSize in microns).
172
+ */
173
+ printToPdf?(options: Record<string, unknown>): Promise<{
174
+ readonly base64: string;
175
+ }>;
176
+ /**
177
+ * Raise this view's window (show + restore + focus). Optional: a host that
178
+ * owns no window omits it. Distinct from the provider's own show/switch
179
+ * path, which deliberately leaves a human-selected visible task alone.
180
+ */
181
+ focusWindow?(): Promise<void>;
182
+ }
183
+ /**
184
+ * How the host answers a JS dialog (alert/confirm/prompt).
185
+ *
186
+ * A dialog freezes the renderer until it is answered, so the default stays
187
+ * `accept` - automation must never hang on one. `dismiss` is for pages whose
188
+ * confirmation is part of what is being driven (delete prompts and the like),
189
+ * and `promptText` supplies the value for a prompt.
190
+ */
191
+ /** One captured console message. */
192
+ export interface BrowserConsoleMessage {
193
+ level: string;
194
+ text: string;
195
+ at: string;
196
+ }
197
+ /** One captured network request. */
198
+ export interface BrowserNetworkRequest {
199
+ method: string;
200
+ url: string;
201
+ status?: number;
202
+ mime?: string;
203
+ kind?: string;
204
+ failed?: string;
205
+ ms?: number;
206
+ at: string;
207
+ }
208
+ /** Device/viewport/media emulation for one tab (browser_emulate). */
209
+ export interface EmulateOptions {
210
+ readonly width?: number;
211
+ readonly height?: number;
212
+ readonly deviceScaleFactor?: number;
213
+ readonly mobile?: boolean;
214
+ readonly userAgent?: string;
215
+ readonly colorScheme?: 'light' | 'dark' | 'no-preference';
216
+ /** Undo everything this tool set on the tab. */
217
+ readonly clear?: boolean;
218
+ }
219
+ export interface DialogPolicy {
220
+ readonly behavior: 'accept' | 'dismiss';
221
+ readonly promptText?: string;
222
+ }
223
+ /** Provider config: navigation admission defaults and snapshot caps. */
224
+ export interface ElectronBrowserProviderConfig {
225
+ /** Allow navigation only to HTTP(S) URLs; reject anything else. Default true. */
226
+ readonly httpOnly?: boolean;
227
+ /** Maximum snapshot elements before truncation. Default 60. */
228
+ readonly snapshotMaxElements?: number;
229
+ /** Maximum content characters before truncation when no maxChars is given. Default 100_000. */
230
+ readonly contentMaxChars?: number;
231
+ /**
232
+ * Absolute directories a screenshot or download may write into. Defaults to
233
+ * the workspace and the OS temp directory ({@link defaultWriteRoots}); an
234
+ * empty list denies every write.
235
+ */
236
+ readonly writeRoots?: readonly string[];
237
+ /**
238
+ * Absolute directories `browser_upload_file` may read from. Defaults to the
239
+ * same roots as {@link writeRoots}; an empty list denies every upload.
240
+ */
241
+ readonly readRoots?: readonly string[];
242
+ }
243
+ /**
244
+ * CDP method/params for `Page.navigate`, as sent to {@link ElectronViewHandle.sendCommand}.
245
+ */
246
+ export interface CdpNavigateParams {
247
+ readonly url: string;
248
+ }
249
+ /**
250
+ * CDP method/params for `Input.dispatchMouseEvent` (a click press+release pair).
251
+ */
252
+ export interface CdpMouseParams {
253
+ readonly type: 'mousePressed' | 'mouseReleased' | 'mouseMoved';
254
+ readonly x: number;
255
+ readonly y: number;
256
+ readonly button: 'left' | 'right' | 'middle' | 'none';
257
+ readonly clickCount?: number;
258
+ /** Buttons held during the event; 1 while a left drag is in flight. */
259
+ readonly buttons?: number;
260
+ /** CDP modifier bitmask (Alt 1, Ctrl 2, Meta 4, Shift 8); see modifierMask. */
261
+ readonly modifiers?: number;
262
+ }
263
+ /** CDP method/params for `Input.insertText`. */
264
+ export interface CdpInsertTextParams {
265
+ readonly text: string;
266
+ }
267
+ /** CDP method/params for `Runtime.evaluate`. */
268
+ export interface CdpEvaluateParams {
269
+ readonly expression: string;
270
+ readonly returnByValue: boolean;
271
+ readonly awaitPromise?: boolean;
272
+ }
273
+ /** CDP method for a full-page screenshot capture. */
274
+ export declare const CDP_PAGE_CAPTURE_SCREENSHOT = "Page.captureScreenshot";
275
+ /** CDP method that renders the current document to PDF. */
276
+ export declare const CDP_PAGE_PRINT_TO_PDF = "Page.printToPDF";
277
+ /** CDP methods that draw the DevTools highlight box without touching the DOM. */
278
+ export declare const CDP_OVERLAY_ENABLE = "Overlay.enable";
279
+ export declare const CDP_OVERLAY_HIGHLIGHT_NODE = "Overlay.highlightNode";
280
+ export declare const CDP_OVERLAY_HIDE_HIGHLIGHT = "Overlay.hideHighlight";
281
+ /** CDP method for runtime evaluation (the execute path). */
282
+ export declare const CDP_RUNTIME_EVALUATE = "Runtime.evaluate";
283
+ /**
284
+ * Decide how to hand a script to `Runtime.evaluate`.
285
+ *
286
+ * CDP evaluates an *expression*, so a script made of statements is a syntax
287
+ * error there. Try the expression form first - that keeps bare expressions and
288
+ * object literals returning their value, which is what the tool has always
289
+ * done - then fall back to a statement body. `const x = 1; return x` is the
290
+ * shape people actually type, and it used to come back as a bare SyntaxError.
291
+ */
292
+ export declare function buildEvaluateBody(script: string): {
293
+ body: string;
294
+ } | {
295
+ error: string;
296
+ };
297
+ /** CDP method for keyboard input. */
298
+ export declare const CDP_INPUT_DISPATCH_KEY_EVENT = "Input.dispatchKeyEvent";
299
+ /** CDP method for navigation. */
300
+ export declare const CDP_PAGE_NAVIGATE = "Page.navigate";
301
+ /** CDP methods used by native browser navigation controls. */
302
+ export declare const CDP_PAGE_GET_NAVIGATION_HISTORY = "Page.getNavigationHistory";
303
+ export declare const CDP_PAGE_NAVIGATE_TO_HISTORY_ENTRY = "Page.navigateToHistoryEntry";
304
+ export declare const CDP_PAGE_RELOAD = "Page.reload";
305
+ export declare const CDP_PAGE_STOP_LOADING = "Page.stopLoading";
306
+ /**
307
+ * Browser provider over Electron views. Sessions hold an ordered list of
308
+ * tabs; each tab is one view created by the host. The active tab receives
309
+ * every operation; switching tabs calls the host's optional `showView` and
310
+ * never loses state. Navigation is admitted only for HTTP(S) targets unless
311
+ * {@link ElectronBrowserProviderConfig.httpOnly} is disabled.
312
+ */
313
+ export declare class ElectronBrowserProvider implements BrowserProvider {
314
+ private readonly host;
315
+ readonly id = "electron";
316
+ private readonly sessions;
317
+ /** Stable task-key index so callers can recover a session after tool-layer state loss. */
318
+ private readonly sessionsByTask;
319
+ private readonly taskStates;
320
+ /**
321
+ * The Agent's todo list per task, kept here as well as in the host.
322
+ *
323
+ * The host's copy is in memory and dies with the host process, and the bridge
324
+ * only pushes on a todo change — so without this cache a host restart left the
325
+ * floating orb with no plan until the Agent happened to write its list again.
326
+ */
327
+ private readonly taskTodos;
328
+ private readonly httpOnly;
329
+ private readonly snapshotMaxElements;
330
+ private readonly contentMaxChars;
331
+ private readonly writeRoots;
332
+ private readonly readRoots;
333
+ /** Background scrape batches, keyed by id; rows live on disk, not here. */
334
+ private readonly scrapes;
335
+ constructor(host: ElectronBrowserViewHost, config?: ElectronBrowserProviderConfig);
336
+ /**
337
+ * Apply one authenticated chrome request to the session that owns its task.
338
+ *
339
+ * Every field is re-checked here: the host authenticates the sender, this
340
+ * method decides whether the request still makes sense against the live tab
341
+ * model. A request that resolves to nothing (a stale strip, a tab closed a
342
+ * moment ago, a task with no session) is dropped rather than thrown, because
343
+ * a human click must never surface as an error inside a running tool call.
344
+ */
345
+ private handleChromeEvent;
346
+ /**
347
+ * Usable whenever the host can create views. A host that exposes a local
348
+ * {@link ElectronBrowserViewHost.isAvailable} probe is believed; a host that
349
+ * omits it (a desktop shell's known-good viewHost, or a test fake) is assumed
350
+ * usable. The probe is cheap and local, so this stays callable from the seam's
351
+ * provider-selection path; the host owns any caching it needs.
352
+ */
353
+ available(): boolean;
354
+ /**
355
+ * Open or recover the browser session for a task key. The tool layer normally
356
+ * caches this id, but the Provider is authoritative so a scoped tool reload or
357
+ * a lost cache cannot create a second task session with a different tab set.
358
+ * Sessions keep isolated tabs, active tab, and history while the host keeps one
359
+ * human-selected task view visible in the shared BrowserWindow.
360
+ */
361
+ open(options?: BrowserOpenOptions): Promise<BrowserSessionId>;
362
+ /** Open a URL in the active tab (default) or a new tab. */
363
+ openUrl(session: BrowserSessionId, request: BrowserOpenRequest, signal?: AbortSignal): Promise<void>;
364
+ /** List the session's tabs with their titles. */
365
+ listTabs(session: BrowserSessionId): Promise<readonly BrowserTab[]>;
366
+ /** Switch to a tab by id; background task tabs stay hidden until user-selected. */
367
+ switchTab(session: BrowserSessionId, tabId: string): Promise<void>;
368
+ /**
369
+ * Close one tab; closing the active tab activates the next. Resolves false when
370
+ * the id is not open in this session, so a miss is distinguishable from a close.
371
+ */
372
+ closeTab(session: BrowserSessionId, tabId: string): Promise<boolean>;
373
+ /** Close every tab and reset to one blank tab. */
374
+ reset(session: BrowserSessionId): Promise<void>;
375
+ /**
376
+ * Dispatch one input command under the same hang guard as the CDP reads. A
377
+ * renderer blocked in synchronous JS never acknowledges, so an unbounded await
378
+ * here would hang the tool call until the caller's budget expired.
379
+ * @param handle - the view to dispatch into.
380
+ * @param method - the CDP input method.
381
+ * @param params - its parameters.
382
+ * @param signal - optional caller signal.
383
+ */
384
+ private dispatchInput;
385
+ /**
386
+ * Tell a renderer it is focused, once, before synthesized input.
387
+ *
388
+ * Chromium drops a synthesized mouse *press* when the renderer does not
389
+ * believe it has focus — which is the normal state for a background task's
390
+ * view, and on a real page even for the visible one while its window is not
391
+ * active. Moves are not gated, so hover looked fine while every click
392
+ * resolved its target, reported success, and left the page untouched.
393
+ *
394
+ * Focus emulation keeps this on the trusted CDP input path: no synthetic
395
+ * DOM click, so the events stay isTrusted and nothing about the page's
396
+ * view of the browser changes.
397
+ */
398
+ private ensureInputFocus;
399
+ /**
400
+ * Admit one URL for a provider-driven fetch (navigation or download).
401
+ * The whole check is gated by `httpOnly`: when it is disabled, callers are
402
+ * trusted with any scheme. When it is enabled, only HTTP(S) is admitted and
403
+ * URL-embedded credentials are refused, so a target can never be reached
404
+ * with in-URL auth.
405
+ * @param url - the candidate URL.
406
+ * @param subject - the operation name used in the error text.
407
+ */
408
+ private admitUrl;
409
+ /** Navigate the active tab's view to a URL, honoring HTTP(S)-only admission. */
410
+ navigate(session: BrowserSessionId, request: {
411
+ readonly url: string;
412
+ }, signal?: AbortSignal): Promise<void>;
413
+ /**
414
+ * Navigate one tab. A scrape worker passes its own tab so a batch never races
415
+ * a tool call for the session's active tab.
416
+ * @param show - bring the tab to the front; a background worker passes false.
417
+ * @param settleMs - post-ready paint delay; a DOM-only reader passes 0.
418
+ */
419
+ private navigateTab;
420
+ /** Navigate to the previous history entry when one exists. */
421
+ back(session: BrowserSessionId, signal?: AbortSignal): Promise<boolean>;
422
+ /** Navigate to the next history entry when one exists. */
423
+ forward(session: BrowserSessionId, signal?: AbortSignal): Promise<boolean>;
424
+ /** Reload the active page and restore the browser chrome afterwards. */
425
+ reload(session: BrowserSessionId, signal?: AbortSignal): Promise<void>;
426
+ /** Stop loading the active page. */
427
+ stopLoading(session: BrowserSessionId, signal?: AbortSignal): Promise<void>;
428
+ /** Execute JS in the active tab's page context. */
429
+ execute(session: BrowserSessionId, request: BrowserExecuteRequest, signal?: AbortSignal): Promise<BrowserExecuteResult>;
430
+ /** Evaluate in one tab's page context. */
431
+ private executeTab;
432
+ /** Produce an AI-friendly snapshot of the active tab. */
433
+ snapshot(session: BrowserSessionId, options?: {
434
+ query?: string;
435
+ limit?: number;
436
+ }, signal?: AbortSignal): Promise<BrowserSnapshotResult>;
437
+ /** Click one element that belongs to a retained exact page snapshot. */
438
+ clickRef(session: BrowserSessionId, request: BrowserRefRequest, signal?: AbortSignal): Promise<void>;
439
+ /** Scroll one element that belongs to a retained exact page snapshot into view. */
440
+ scrollIntoView(session: BrowserSessionId, request: BrowserScrollIntoViewRequest, signal?: AbortSignal): Promise<BrowserScrollResult>;
441
+ /** Check whether a human-verification challenge is blocking the active tab. */
442
+ detectChallenge(session: BrowserSessionId, signal?: AbortSignal): Promise<BrowserChallenge>;
443
+ /** Fetch page content in a requested format. */
444
+ content(session: BrowserSessionId, request: BrowserContentRequest, signal?: AbortSignal): Promise<BrowserContentResult>;
445
+ /** Click at viewport coordinates (CDP mousePressed + mouseReleased). */
446
+ click(session: BrowserSessionId, target: BrowserPointerTarget, signal?: AbortSignal): Promise<BrowserPointerResult>;
447
+ /** Double-click a target (physical input; clickCount 2). */
448
+ doubleClick(session: BrowserSessionId, target: BrowserPointerTarget, signal?: AbortSignal): Promise<BrowserPointerResult>;
449
+ /** Move the pointer over a target (no click). */
450
+ hover(session: BrowserSessionId, target: BrowserPointerTarget, signal?: AbortSignal): Promise<BrowserPointerResult>;
451
+ /**
452
+ * Press on one target, move to another, release.
453
+ *
454
+ * A hand does not teleport: the intermediate moves are what pointer-based
455
+ * sliders and sortable libraries listen for, so a press straight onto the
456
+ * destination would be ignored. Note this drives *pointer* drags only —
457
+ * HTML5 drag-and-drop needs dragstart/drop, which synthesized mouse moves do
458
+ * not produce; use the page's own controls, or a click-based reorder, there.
459
+ */
460
+ drag(session: BrowserSessionId, request: BrowserDragRequest, signal?: AbortSignal): Promise<BrowserDragResult>;
461
+ /** Scroll the active page by CSS-pixel deltas and return the final position. */
462
+ scroll(session: BrowserSessionId, request: BrowserScrollRequest, signal?: AbortSignal): Promise<BrowserScrollResult>;
463
+ /**
464
+ * Attach a local file to the first matching file input. Uses the CDP DOM
465
+ * domain (nodeId path), which — unlike a synthetic change event — makes the
466
+ * input's files list true (real file selection), so pages that read
467
+ * input.files or upload on change behave exactly like a real pick.
468
+ */
469
+ uploadFile(session: BrowserSessionId, request: BrowserUploadFileRequest, signal?: AbortSignal): Promise<BrowserUploadFileResult>;
470
+ /**
471
+ * Poll until an element matching the selector exists (and is visible).
472
+ * Bounds the total wait; a timeout surfaces as BROWSER_WAIT_TIMEOUT.
473
+ */
474
+ waitForElement(session: BrowserSessionId, request: BrowserWaitForRequest, signal?: AbortSignal): Promise<BrowserWaitForResult>;
475
+ /** Poll one tab until the selector and/or text reaches the requested state. */
476
+ private waitForElementTab;
477
+ /** Type into the focused element. */
478
+ type(session: BrowserSessionId, request: {
479
+ readonly text: string;
480
+ }, signal?: AbortSignal): Promise<void>;
481
+ /**
482
+ * Drive the host's chrome frame view with a raw CDP command.
483
+ *
484
+ * The chrome can live in a view of its own so the page viewport can really
485
+ * shrink; that view is not a tab, so this is the only way to click the toolbar
486
+ * (the click tests use it, and so does anything that needs to exercise the
487
+ * chrome the way a person does).
488
+ */
489
+ chromeInput(method: string, params?: Record<string, unknown>): Promise<void>;
490
+ /**
491
+ * Read a value back out of the chrome frame's own document.
492
+ *
493
+ * The frame is not a tab, so a page-directed evaluate cannot see it; without
494
+ * this the toolbar's animations could only be inferred from the page's copy.
495
+ */
496
+ chromeEval(expression: string): Promise<unknown>;
497
+ /** Press a key into the page (keyDown + keyUp), as a physical-input path
498
+ * for shortcuts and keyboard-driven UI. */
499
+ pressKey(session: BrowserSessionId, request: BrowserPressKeyRequest, signal?: AbortSignal): Promise<void>;
500
+ /**
501
+ * Fill a form's fields in one batch. Runs one page-context script that
502
+ * resolves each field (selector, or name/label/placeholder among visible
503
+ * controls), sets its value with the native prototype setter (React/Vue
504
+ * controlled inputs included) plus input/change events, handles
505
+ * select/checkbox/radio/contenteditable, and optionally submits the form.
506
+ */
507
+ fillForm(session: BrowserSessionId, request: BrowserFillRequest, signal?: AbortSignal): Promise<BrowserFillResult>;
508
+ /**
509
+ * Download a URL to a local file, keeping the session's cookies/login.
510
+ * Requires the self-hosted host (which implements view-level download); the
511
+ * desktop shell's embedded views delegate downloads to the real browser UI.
512
+ */
513
+ download(session: BrowserSessionId, request: {
514
+ readonly url: string;
515
+ readonly savePath: string;
516
+ }, signal?: AbortSignal): Promise<{
517
+ readonly path: string;
518
+ }>;
519
+ /**
520
+ * Export the session's cookies (login state) as serializable objects.
521
+ * Self-hosted only; the desktop shell's embedded views use the real profile.
522
+ */
523
+ flushAuth(session: BrowserSessionId): Promise<readonly ExportedCookie[]>;
524
+ /**
525
+ * Remove cookies for one site scope. Challenge cookies that rotate their names
526
+ * (WAF challenges) otherwise pile up generation after generation, and two live
527
+ * generations in one request can be rejected by the site. Self-hosted only.
528
+ */
529
+ clearAuth(session: BrowserSessionId, request: BrowserClearAuthRequest): Promise<BrowserClearAuthResult>;
530
+ /**
531
+ * Import cookies from a JSON export on disk.
532
+ *
533
+ * The path is read-guarded exactly like browser_upload_file: a prompt-injected
534
+ * path must not turn this into a way to read a file the operator never allowed.
535
+ * A browser cookie export cannot be produced automatically — Chrome and Edge
536
+ * 127+ encrypt cookie values with App-Bound Encryption, so a copied profile
537
+ * yields nothing — which is why this takes a file the user exported.
538
+ */
539
+ importAuth(session: BrowserSessionId, path: string): Promise<{
540
+ restored: number;
541
+ failed: number;
542
+ }>;
543
+ /** Import cookies into the session (restore login state). Self-hosted only. */
544
+ restoreAuth(session: BrowserSessionId, cookies: readonly ExportedCookie[]): Promise<number>;
545
+ /**
546
+ * Start a background scrape batch.
547
+ *
548
+ * It runs detached on purpose: one tool call has a ~60s budget while a large
549
+ * batch takes minutes. Progress is polled with scrapeStatus, and each row is
550
+ * appended the moment it is produced, so a stopped or interrupted batch keeps
551
+ * everything it managed. `outPath` is write-guarded like any other browser
552
+ * write, and truncated up front so a re-run never mixes two batches.
553
+ */
554
+ startScrape(session: BrowserSessionId, request: BrowserScrapeRequest): Promise<BrowserScrapeStatus>;
555
+ /** Progress of one batch. */
556
+ scrapeStatus(id: string): Promise<BrowserScrapeStatus>;
557
+ /** Ask a running batch to stop; rows already written stay. */
558
+ stopScrape(id: string): Promise<BrowserScrapeStatus>;
559
+ /** Every batch this process knows about, oldest first. */
560
+ listScrapes(): Promise<readonly BrowserScrapeStatus[]>;
561
+ private scrapeJob;
562
+ /**
563
+ * Visit each URL once, appending one JSONL row per page.
564
+ *
565
+ * Workers pull from one shared index, so `concurrency` sets the throughput
566
+ * without changing the work. Rows therefore land in completion order; each row
567
+ * carries its URL's index as `seq` so the caller can restore the original.
568
+ */
569
+ private runScrape;
570
+ /** Drop a batch's private tabs (and their views) once the batch is over. */
571
+ private destroyScrapeTabs;
572
+ /** Capture the current page, optionally full-page. PNG only (CDP JPEG hangs on Electron 43). */
573
+ /**
574
+ * Draw the DevTools highlight box over the first match of a selector, or clear it.
575
+ * Uses CDP Overlay, so the page DOM is never touched.
576
+ */
577
+ highlight(session: BrowserSessionId, request: BrowserHighlightRequest, signal?: AbortSignal): Promise<BrowserHighlightResult>;
578
+ /** Print the active tab to a PDF file (Chrome's "Save as PDF"). */
579
+ pdf(session: BrowserSessionId, request: BrowserPdfRequest, signal?: AbortSignal): Promise<BrowserPdfResult>;
580
+ screenshot(session: BrowserSessionId, request?: {
581
+ readonly fullPage?: boolean;
582
+ readonly savePath?: string;
583
+ }, signal?: AbortSignal): Promise<{
584
+ readonly dataUrl: string;
585
+ readonly path?: string;
586
+ }>;
587
+ /** Build the data URL and optionally write the PNG to disk. */
588
+ private saveScreenshot;
589
+ /**
590
+ * Pick up (and forget) any JS dialog the host auto-accepted, so the
591
+ * operation trail shows the human/agent what the page asked. Best-effort.
592
+ */
593
+ private drainDialog;
594
+ /**
595
+ * Set how the host answers the next JS dialog, and report the resulting state.
596
+ *
597
+ * The policy lives in the host (it answers the CDP event there, where a
598
+ * round-trip would already be too late), so this is a push, not a pull.
599
+ */
600
+ setDialogPolicy(session: BrowserSessionId, policy: DialogPolicy): Promise<{
601
+ dialog: unknown;
602
+ policy: DialogPolicy;
603
+ }>;
604
+ /**
605
+ * Console messages the host captured for the active tab.
606
+ *
607
+ * Reading does NOT clear by default: debugging is usually a look-again loop, so
608
+ * `clear: true` is explicit. The host keeps a bounded ring, so old entries fall
609
+ * off on their own.
610
+ */
611
+ consoleMessages(session: BrowserSessionId, options?: {
612
+ limit?: number;
613
+ level?: string;
614
+ clear?: boolean;
615
+ }): Promise<{
616
+ messages: BrowserConsoleMessage[];
617
+ }>;
618
+ /** Network requests the host captured for the active tab (bounded ring). */
619
+ networkRequests(session: BrowserSessionId, options?: {
620
+ limit?: number;
621
+ failedOnly?: boolean;
622
+ urlContains?: string;
623
+ clear?: boolean;
624
+ }): Promise<{
625
+ requests: BrowserNetworkRequest[];
626
+ }>;
627
+ /**
628
+ * Apply device/viewport/media emulation to the active tab.
629
+ *
630
+ * Plain CDP through the existing command path, so no host change was needed.
631
+ * `clear` undoes all three: metrics, user agent, and emulated media.
632
+ */
633
+ emulate(session: BrowserSessionId, options?: EmulateOptions): Promise<{
634
+ applied: string[];
635
+ }>;
636
+ /**
637
+ * Drain any dialog that opened since the last input call, then report the last
638
+ * one and the current policy.
639
+ *
640
+ * `dialogState` alone is not enough for the tool: the host only hands a dialog
641
+ * over when something drains it, so an inspect that does not drain misses exactly
642
+ * the dialog the caller just triggered. (Found on the real machine.)
643
+ */
644
+ inspectDialog(session: BrowserSessionId): Promise<{
645
+ dialog: unknown;
646
+ policy: DialogPolicy;
647
+ }>;
648
+ /** The last JS dialog the host reported, plus the current policy. */
649
+ dialogState(session: BrowserSessionId): {
650
+ dialog: unknown;
651
+ policy: DialogPolicy;
652
+ };
653
+ /** Name this browser task (space). */
654
+ setSpace(session: BrowserSessionId, label: string): Promise<void>;
655
+ /** List every browser task (space) with its label. */
656
+ listSpaces(): Promise<readonly BrowserSpaceInfo[]>;
657
+ /** List browser tasks with live collaboration status. */
658
+ listTasks(): Promise<readonly BrowserTaskInfo[]>;
659
+ /** Read the collaboration state for one session's task. */
660
+ getTask(session: BrowserSessionId): Promise<BrowserTaskInfo>;
661
+ /** Apply one visible task state update and mirror it to a supporting host. */
662
+ updateTask(session: BrowserSessionId, update: BrowserTaskUpdate): Promise<BrowserTaskInfo>;
663
+ /**
664
+ * Mirror one task's Agent todo list into the host so the floating orb can
665
+ * render it. The list is the DSH session projection for that task's session;
666
+ * only this side can read it, and only the host can reach the chrome.
667
+ *
668
+ * Unknown task keys are still stored: an Agent usually writes its plan before
669
+ * it opens a page, and the orb should show that plan the moment the task has a
670
+ * view rather than wait for the next write.
671
+ */
672
+ pushTaskTodos(taskKey: string, todos: readonly BrowserTaskTodo[]): Promise<void>;
673
+ /** Hand control to the user or return it to Agent-driven actions. */
674
+ setHandoff(session: BrowserSessionId, state: BrowserHandoffState): Promise<BrowserTaskInfo>;
675
+ /** Append one operation to the session's history. */
676
+ private record;
677
+ /** Return the session's chronological operation log (newest last). */
678
+ history(session: BrowserSessionId): Promise<readonly BrowserHistoryEntry[]>;
679
+ /**
680
+ * Replay one recorded operation by sequence number. Navigate/click/type are
681
+ * re-issued against the current page; execute re-runs its script. The
682
+ * replayed step is appended to history as a new entry.
683
+ * @param session - the session id.
684
+ * @param seq - the recorded entry's sequence number to replay.
685
+ */
686
+ replay(session: BrowserSessionId, seq: number): Promise<void>;
687
+ /**
688
+ * Put the shared window on screen and raise it. With no session yet this
689
+ * opens the default one, which spawns the host and creates the first view —
690
+ * that is what makes the window appear. With a session already open the
691
+ * window exists, so it only has to be raised.
692
+ */
693
+ ensureWindowVisible(): Promise<void>;
694
+ /** Close the session and destroy all its views. Idempotent. */
695
+ close(session: BrowserSessionId): Promise<void>;
696
+ /** Recover the live session associated with a stable task key. */
697
+ private sessionForTask;
698
+ /** Look up a session or throw the unknown-session error. */
699
+ private session;
700
+ /** The active tab of a session. */
701
+ private activeTab;
702
+ /** Navigate through the browser history while preserving page readiness behavior. */
703
+ private navigateHistory;
704
+ /** Drop every reference that was captured before a document transition. */
705
+ private invalidateSnapshots;
706
+ /** Resolve one exact snapshot reference, rejecting any changed or missing target. */
707
+ private resolveSnapshotTarget;
708
+ /** Sync provider fallback cache from the host's authoritative workspace state. */
709
+ private rememberHostedTask;
710
+ /** Build the provider-side task summary when a host has no richer workspace. */
711
+ private localTaskInfo;
712
+ /** Create a tab with its short-lived snapshot reference store. */
713
+ private createTab;
714
+ /** Append a fresh tab and make it active. */
715
+ private newTab;
716
+ /** Notify the host of the active tab; it preserves the human-selected task view. */
717
+ /**
718
+ * Fire-and-forget host call: these run while the provider keeps going, so a rejection
719
+ * must never escape. Electron's host answers `unknown view` for a handle it no longer
720
+ * knows (a host restart leaves the provider holding stale ones), and an unhandled
721
+ * rejection surfaces as *some other* tool call failing - Ctrl+W did exactly that.
722
+ * The desired end state (view gone) holds either way, so swallowing is right here.
723
+ */
724
+ private ignoreHostFailure;
725
+ private showActive;
726
+ /** Read the current URL of a view through CDP. */
727
+ private currentUrl;
728
+ }
729
+ /**
730
+ * The in-page half of {@link resolvePointerTarget}: resolve the element, scroll
731
+ * it into view, and return its centre.
732
+ *
733
+ * Exported so it can be exercised. The matching rule — the innermost visible
734
+ * element whose label contains the text wins — is the part most likely to be
735
+ * wrong, and Node has no DOM to check it against.
736
+ */
737
+ export declare function pointerTargetScript(selector: string | undefined, text: string | undefined): string;
738
+ /**
739
+ * Minimal DOM shape {@link renderMarkdown} reads; a real DOM node fits it.
740
+ */
741
+ export interface MarkdownNode {
742
+ readonly nodeType?: number;
743
+ readonly tagName?: string | null;
744
+ readonly textContent?: string | null;
745
+ readonly childNodes?: ArrayLike<MarkdownNode> | null;
746
+ readonly href?: string | null;
747
+ readonly src?: string | null;
748
+ readonly alt?: string | null;
749
+ }
750
+ /**
751
+ * Best-effort markdown rendering of a DOM subtree, used by
752
+ * {@link ElectronBrowserProvider.content} for `format: 'markdown'`.
753
+ *
754
+ * Deliberately self-contained (no closures over module state, no imports):
755
+ * the provider embeds this function's source in the page with
756
+ * `Function.prototype.toString`, so the tests exercise the very code the page
757
+ * runs.
758
+ *
759
+ * Block containers (div/p/section/article/li/headings/...) recurse into their
760
+ * children and are joined with newlines, while adjacent inline runs are
761
+ * concatenated — text split by <b>/<span> stays one paragraph, and a container
762
+ * never emits its own `textContent` on top of its children (the old walker did,
763
+ * which flattened real pages — everything is wrapped in divs — to plain text).
764
+ * @param root - the subtree root (an element, or a text node).
765
+ * @returns the markdown text.
766
+ */
767
+ export declare function renderMarkdown(root: MarkdownNode): string;