@particle-academy/agent-integrations 0.46.0 → 0.47.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 (96) hide show
  1. package/CHANGELOG.md +835 -0
  2. package/LICENSE +21 -0
  3. package/README.md +44 -3
  4. package/dist/bridges/artboard.d.cts +3 -3
  5. package/dist/bridges/artboard.d.ts +3 -3
  6. package/dist/bridges/catalog.d.cts +3 -3
  7. package/dist/bridges/catalog.d.ts +3 -3
  8. package/dist/bridges/charts.d.cts +3 -3
  9. package/dist/bridges/charts.d.ts +3 -3
  10. package/dist/bridges/cms.d.cts +3 -3
  11. package/dist/bridges/cms.d.ts +3 -3
  12. package/dist/bridges/code.d.cts +3 -3
  13. package/dist/bridges/code.d.ts +3 -3
  14. package/dist/bridges/doc.d.cts +3 -3
  15. package/dist/bridges/doc.d.ts +3 -3
  16. package/dist/bridges/features.d.cts +3 -3
  17. package/dist/bridges/features.d.ts +3 -3
  18. package/dist/bridges/files.d.cts +3 -3
  19. package/dist/bridges/files.d.ts +3 -3
  20. package/dist/bridges/flow.d.cts +3 -3
  21. package/dist/bridges/flow.d.ts +3 -3
  22. package/dist/bridges/forms.d.cts +3 -3
  23. package/dist/bridges/forms.d.ts +3 -3
  24. package/dist/bridges/git.d.cts +3 -3
  25. package/dist/bridges/git.d.ts +3 -3
  26. package/dist/bridges/grid.d.cts +3 -3
  27. package/dist/bridges/grid.d.ts +3 -3
  28. package/dist/bridges/map.d.cts +3 -3
  29. package/dist/bridges/map.d.ts +3 -3
  30. package/dist/bridges/navigation.d.cts +127 -0
  31. package/dist/bridges/navigation.d.ts +127 -0
  32. package/dist/bridges/passkeys.d.cts +4 -4
  33. package/dist/bridges/passkeys.d.ts +4 -4
  34. package/dist/bridges/scene.d.cts +3 -3
  35. package/dist/bridges/scene.d.ts +3 -3
  36. package/dist/bridges/screens.d.cts +3 -3
  37. package/dist/bridges/screens.d.ts +3 -3
  38. package/dist/bridges/sheets.d.cts +3 -3
  39. package/dist/bridges/sheets.d.ts +3 -3
  40. package/dist/bridges/slides.d.cts +3 -3
  41. package/dist/bridges/slides.d.ts +3 -3
  42. package/dist/bridges/terminal.d.cts +3 -3
  43. package/dist/bridges/terminal.d.ts +3 -3
  44. package/dist/bridges/tui.d.cts +2 -2
  45. package/dist/bridges/tui.d.ts +2 -2
  46. package/dist/bridges/whiteboard.d.cts +3 -3
  47. package/dist/bridges/whiteboard.d.ts +3 -3
  48. package/dist/bridges-navigation.cjs +391 -0
  49. package/dist/bridges-navigation.cjs.map +1 -0
  50. package/dist/bridges-navigation.js +7 -0
  51. package/dist/bridges-navigation.js.map +1 -0
  52. package/dist/bridges-passkeys.cjs.map +1 -1
  53. package/dist/bridges-passkeys.js +1 -1
  54. package/dist/{chunk-PWYEW6NL.js → chunk-G6CRHFWU.js} +2 -2
  55. package/dist/{chunk-PWYEW6NL.js.map → chunk-G6CRHFWU.js.map} +1 -1
  56. package/dist/chunk-JGJHPH65.js +271 -0
  57. package/dist/chunk-JGJHPH65.js.map +1 -0
  58. package/dist/dom-navigation/index.d.cts +164 -0
  59. package/dist/dom-navigation/index.d.ts +164 -0
  60. package/dist/dom-navigation/script.d.cts +3 -0
  61. package/dist/dom-navigation/script.d.ts +3 -0
  62. package/dist/dom-navigation-script.cjs +11 -0
  63. package/dist/dom-navigation-script.cjs.map +1 -0
  64. package/dist/dom-navigation-script.js +9 -0
  65. package/dist/dom-navigation-script.js.map +1 -0
  66. package/dist/dom-navigation.cjs +387 -0
  67. package/dist/dom-navigation.cjs.map +1 -0
  68. package/dist/dom-navigation.js +370 -0
  69. package/dist/dom-navigation.js.map +1 -0
  70. package/dist/index.cjs +31 -31
  71. package/dist/index.cjs.map +1 -1
  72. package/dist/index.d.cts +10 -125
  73. package/dist/index.d.ts +10 -125
  74. package/dist/index.js +12 -275
  75. package/dist/index.js.map +1 -1
  76. package/dist/mcp/index.d.cts +5 -5
  77. package/dist/mcp/index.d.ts +5 -5
  78. package/dist/mcp/stdio.d.cts +3 -3
  79. package/dist/mcp/stdio.d.ts +3 -3
  80. package/dist/presence/index.d.cts +1 -1
  81. package/dist/presence/index.d.ts +1 -1
  82. package/dist/{server-BIcA3ZtK.d.cts → server-1y0n61R5.d.cts} +2 -2
  83. package/dist/{server-DnUyeavY.d.ts → server-vq6qVl3T.d.ts} +2 -2
  84. package/dist/sharing/index.d.cts +3 -3
  85. package/dist/sharing/index.d.ts +3 -3
  86. package/dist/sheets-adapter.d.cts +3 -3
  87. package/dist/sheets-adapter.d.ts +3 -3
  88. package/dist/{tool-host-LVBgCN5m.d.cts → tool-host-D67N2a2h.d.cts} +1 -1
  89. package/dist/{tool-host-Wo0pkY83.d.ts → tool-host-LYfdPE8u.d.ts} +1 -1
  90. package/dist/{types-EcElKOlk.d.cts → types-DP5yQZQU.d.cts} +1 -1
  91. package/dist/{types-BxNRKSVd.d.ts → types-VX_9je7w.d.ts} +1 -1
  92. package/dist/{types-C2DtnHIn.d.cts → types-gzIpsUDE.d.cts} +1 -1
  93. package/dist/{types-C2DtnHIn.d.ts → types-gzIpsUDE.d.ts} +1 -1
  94. package/dist/undo/index.d.cts +2 -2
  95. package/dist/undo/index.d.ts +2 -2
  96. package/package.json +35 -3
@@ -0,0 +1,271 @@
1
+ import { ensureUndoToolsRegistered, pushUndoEntry } from './chunk-R4XXOGQH.js';
2
+ import { wrapToolWithActivity } from './chunk-2VENWGLZ.js';
3
+ import { textResult, errorResult } from './chunk-CZV3WUW5.js';
4
+
5
+ // src/bridges/navigation.ts
6
+ var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
7
+ function registerNavigationBridge(host, options) {
8
+ const { adapter } = options;
9
+ const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
10
+ const pendingMode = options.pendingMode ?? true;
11
+ const disposers = [];
12
+ let groundedRevision = null;
13
+ const staleIntent = async () => {
14
+ const current = await adapter.getRevision?.();
15
+ if (current === void 0) return null;
16
+ if (groundedRevision === null || groundedRevision !== current) {
17
+ return errorResult(
18
+ `Human took control at page revision ${current}. This agent action was cancelled; call page_describe before acting again.`
19
+ );
20
+ }
21
+ return null;
22
+ };
23
+ ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
24
+ const target = (label, elementId) => ({
25
+ kind: "navigation",
26
+ screenId: adapter.screenId,
27
+ elementId,
28
+ label
29
+ });
30
+ const reg = (name, description, properties, required, handler, activity) => {
31
+ const wrapped = async (args) => {
32
+ try {
33
+ return await handler(args);
34
+ } catch (e) {
35
+ return errorResult(e instanceof Error ? e.message : String(e));
36
+ }
37
+ };
38
+ const final = activity ? wrapToolWithActivity(wrapped, {
39
+ toolName: name,
40
+ agent: { id: agent.id, name: agent.name, color: agent.color },
41
+ kind: "navigation",
42
+ screenId: adapter.screenId,
43
+ resolveTarget: ({ args }) => activity(args)
44
+ }) : wrapped;
45
+ disposers.push(
46
+ host.registerTool(
47
+ {
48
+ name,
49
+ description,
50
+ inputSchema: { type: "object", properties, required, additionalProperties: false }
51
+ },
52
+ final
53
+ )
54
+ );
55
+ };
56
+ reg(
57
+ "page_describe",
58
+ "Describe the current page: its URL, title, and the interactive elements you can act on (each with a stable `handle`, role, and label). Call this first, and again after navigating.",
59
+ {},
60
+ [],
61
+ async () => {
62
+ const snap = await adapter.describe();
63
+ const revision = await adapter.getRevision?.();
64
+ if (revision !== void 0) {
65
+ snap.revision = revision;
66
+ groundedRevision = revision;
67
+ }
68
+ const text = [
69
+ `URL: ${snap.url}`,
70
+ `Title: ${snap.title}`,
71
+ "",
72
+ ...snap.actions.map((a) => `[${a.handle}] ${a.role}: ${a.label}${a.destructive ? " (destructive)" : ""}`)
73
+ ].join("\n");
74
+ return textResult(text, snap);
75
+ },
76
+ () => target("Inspect page")
77
+ );
78
+ reg(
79
+ "page_read",
80
+ "Read the page's visible text / heading outline for grounding.",
81
+ {},
82
+ [],
83
+ async () => textResult(adapter.read ? await adapter.read() : "(host did not provide page text)"),
84
+ () => target("Read page")
85
+ );
86
+ reg(
87
+ "page_focus",
88
+ "Move the agent's visible focus to an interactive element by stable handle without activating it.",
89
+ { handle: { type: "string" } },
90
+ ["handle"],
91
+ async (args) => {
92
+ const stale = await staleIntent();
93
+ if (stale) return stale;
94
+ const handle = String(args.handle ?? "");
95
+ const action = (await adapter.describe()).actions.find((candidate) => candidate.handle === handle);
96
+ if (!action) return errorResult(`No element for handle "${handle}"`);
97
+ return textResult(`Focused ${handle}`, {
98
+ handle,
99
+ rect: await adapter.rectFor?.(handle) ?? void 0
100
+ });
101
+ },
102
+ (args) => target(`Focus ${String(args.handle ?? "")}`, String(args.handle ?? ""))
103
+ );
104
+ reg(
105
+ "nav_visit",
106
+ "Navigate to a URL (same-site path or absolute). The human watches the page change.",
107
+ { url: { type: "string", description: "Path like /packages or an absolute URL." } },
108
+ ["url"],
109
+ async (args) => {
110
+ const stale = await staleIntent();
111
+ if (stale) return stale;
112
+ const url = String(args.url ?? "");
113
+ if (!url) return errorResult("url is required");
114
+ const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase();
115
+ if (scheme && scheme !== "http" && scheme !== "https") {
116
+ return errorResult(
117
+ `Refusing to navigate to a '${scheme}:' URL. Only http(s) URLs or same-site paths are allowed.`
118
+ );
119
+ }
120
+ const from = (await adapter.getLocation()).url;
121
+ await adapter.visit(url);
122
+ pushUndoEntry(agent.id, {
123
+ timestamp: Date.now(),
124
+ bridgeId: "navigation",
125
+ action: "nav_visit",
126
+ label: `Navigate to ${url}`,
127
+ undo: () => {
128
+ adapter.visit(from);
129
+ },
130
+ redo: () => {
131
+ adapter.visit(url);
132
+ }
133
+ });
134
+ return textResult(`Navigated to ${url}`, { url });
135
+ },
136
+ (args) => target(`Navigate \u2192 ${String(args.url ?? "")}`)
137
+ );
138
+ reg(
139
+ "nav_back",
140
+ "Go back to the previous page.",
141
+ {},
142
+ [],
143
+ async () => {
144
+ const stale = await staleIntent();
145
+ if (stale) return stale;
146
+ if (!adapter.back) return errorResult("Host did not provide back navigation.");
147
+ await adapter.back();
148
+ return textResult("Went back");
149
+ },
150
+ () => target("Back")
151
+ );
152
+ reg(
153
+ "nav_forward",
154
+ "Go forward to the next page.",
155
+ {},
156
+ [],
157
+ async () => {
158
+ const stale = await staleIntent();
159
+ if (stale) return stale;
160
+ if (!adapter.forward) return errorResult("Host did not provide forward navigation.");
161
+ await adapter.forward();
162
+ return textResult("Went forward");
163
+ },
164
+ () => target("Forward")
165
+ );
166
+ reg(
167
+ "nav_scroll_to",
168
+ "Scroll the page to absolute coordinates, or to a specific element by its handle.",
169
+ {
170
+ handle: { type: "string", description: "Scroll this element into view." },
171
+ x: { type: "number" },
172
+ y: { type: "number" }
173
+ },
174
+ [],
175
+ async (args) => {
176
+ const stale = await staleIntent();
177
+ if (stale) return stale;
178
+ const handle = typeof args.handle === "string" ? args.handle : void 0;
179
+ await adapter.scrollTo({
180
+ handle,
181
+ x: typeof args.x === "number" ? args.x : void 0,
182
+ y: typeof args.y === "number" ? args.y : void 0
183
+ });
184
+ return textResult("Scrolled", handle ? { handle, rect: await adapter.rectFor?.(handle) ?? void 0 } : void 0);
185
+ },
186
+ () => target("Scroll")
187
+ );
188
+ reg(
189
+ "nav_scroll_by",
190
+ "Scroll the page by a vertical delta in pixels (negative scrolls up).",
191
+ { dy: { type: "number" } },
192
+ ["dy"],
193
+ async (args) => {
194
+ const stale = await staleIntent();
195
+ if (stale) return stale;
196
+ await adapter.scrollBy(Number(args.dy ?? 0));
197
+ return textResult(`Scrolled by ${Number(args.dy ?? 0)}px`);
198
+ },
199
+ () => target("Scroll")
200
+ );
201
+ reg(
202
+ "page_set_field",
203
+ "Set a form field's value by handle. The host updates the controlled input and the human sees it change.",
204
+ {
205
+ handle: { type: "string" },
206
+ value: { description: "Value to set; type matches the field." }
207
+ },
208
+ ["handle", "value"],
209
+ async (args) => {
210
+ const stale = await staleIntent();
211
+ if (stale) return stale;
212
+ const handle = String(args.handle ?? "");
213
+ const res = await adapter.setField(handle, args.value);
214
+ if (!res.ok) return errorResult(res.error ?? `Could not set ${handle}`);
215
+ return textResult(`${handle} \u2190 ${JSON.stringify(args.value)}`, { handle, value: args.value, rect: await adapter.rectFor?.(handle) ?? void 0 });
216
+ },
217
+ (args) => target(`Set ${String(args.handle ?? "")}`, String(args.handle ?? ""))
218
+ );
219
+ reg(
220
+ "page_click",
221
+ "Activate an element by handle (link, button, checkbox\u2026). Destructive elements are staged for the human to confirm.",
222
+ { handle: { type: "string" } },
223
+ ["handle"],
224
+ async (args) => {
225
+ const stale = await staleIntent();
226
+ if (stale) return stale;
227
+ const handle = String(args.handle ?? "");
228
+ const action = (await adapter.describe()).actions.find((a) => a.handle === handle);
229
+ const rect = await adapter.rectFor?.(handle) ?? void 0;
230
+ if (pendingMode && action?.destructive && adapter.confirm) {
231
+ const ok = await adapter.confirm({ action: "click", handle, label: action.label });
232
+ if (!ok) return errorResult("Declined by user");
233
+ }
234
+ const res = await adapter.click(handle);
235
+ if (!res.ok) return errorResult(res.error ?? `Could not click ${handle}`);
236
+ return textResult(`Clicked ${handle}`, { handle, rect });
237
+ },
238
+ (args) => target(`Click ${String(args.handle ?? "")}`, String(args.handle ?? ""))
239
+ );
240
+ reg(
241
+ "page_submit",
242
+ "Submit a form by handle. Always staged for the human to confirm when pendingMode is on.",
243
+ { handle: { type: "string" } },
244
+ ["handle"],
245
+ async (args) => {
246
+ const stale = await staleIntent();
247
+ if (stale) return stale;
248
+ const handle = String(args.handle ?? "");
249
+ const rect = await adapter.rectFor?.(handle) ?? void 0;
250
+ if (pendingMode && adapter.confirm) {
251
+ const ok = await adapter.confirm({ action: "submit", handle, label: handle });
252
+ if (!ok) return errorResult("Declined by user");
253
+ }
254
+ const res = await adapter.submit(handle);
255
+ if (!res.ok) return errorResult(res.error ?? "Submit failed");
256
+ return textResult(`Submitted ${handle}`, { handle, rect });
257
+ },
258
+ (args) => target(`Submit ${String(args.handle ?? "")}`, String(args.handle ?? ""))
259
+ );
260
+ return {
261
+ id: "navigation",
262
+ title: "Co-browsing",
263
+ dispose: () => {
264
+ for (const d of disposers) d();
265
+ }
266
+ };
267
+ }
268
+
269
+ export { registerNavigationBridge };
270
+ //# sourceMappingURL=chunk-JGJHPH65.js.map
271
+ //# sourceMappingURL=chunk-JGJHPH65.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridges/navigation.ts"],"names":[],"mappings":"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","file":"chunk-JGJHPH65.js","sourcesContent":["import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\nimport { wrapToolWithActivity } from \"../presence/wrap-tool-with-activity\";\nimport type { AgentTarget } from \"../presence/types\";\nimport { pushUndoEntry } from \"../undo/undo-stack\";\nimport { ensureUndoToolsRegistered } from \"../undo/undo-tools\";\n\n/**\n * One interactive element the agent can act on, addressed by a STABLE handle\n * (not a CSS selector) so the agent drives the page the Human+ way — never DOM\n * scraping. The host builds these in `describe()` (data-co-handle → name/id →\n * ARIA role + accessible name).\n */\nexport type PageAction = {\n /** Stable, opaque handle the agent passes back to act on this element. */\n handle: string;\n /** ARIA-ish role: \"link\" | \"button\" | \"textbox\" | \"checkbox\" | \"select\" | … */\n role: string;\n /** Accessible name / label. */\n label: string;\n /** Current value for inputs (omitted/masked for sensitive fields). */\n value?: unknown;\n /** True when activating this is destructive / submits (agent should stage). */\n destructive?: boolean;\n};\n\n/** The page as the agent sees it: where it is + what it can do. */\nexport type PageSnapshot = {\n url: string;\n title: string;\n actions: PageAction[];\n /** Monotonic host revision. Human input advances it and invalidates stale agent intent. */\n revision?: number;\n};\n\n/** A write the host may want the human to confirm (trust-but-verify). */\nexport type NavigationConfirmRequest = {\n action: \"submit\" | \"click\";\n handle: string;\n label: string;\n};\n\ntype MaybePromise<T> = T | Promise<T>;\n\n/**\n * Host-provided adapter. In the sandbox this is backed by Inertia's `router` +\n * the DOM walker from `@particle-academy/agent-integrations/dom-navigation`.\n * Every method works on stable handles, never raw selectors.\n *\n * Any method may return a Promise: a host driving a page it does not run in\n * (an Electron WebContentsView, via `createRemoteNavigationAdapter`) can only\n * answer asynchronously. The bridge awaits every call.\n */\nexport type NavigationBridgeAdapter = {\n /** Optional fancy-screens screen id for presence targeting. */\n screenId?: string;\n /** Current location. */\n getLocation: () => MaybePromise<{ url: string; title: string }>;\n /** Current human-control revision. Omit to disable stale-intent protection. */\n getRevision?: () => MaybePromise<number>;\n /** Snapshot of the page's actionable elements (stable handles + labels). */\n describe: () => MaybePromise<PageSnapshot>;\n /** Visible text / heading outline for grounding (optional). */\n read?: () => MaybePromise<string>;\n /** Navigate to a URL (host wires to router.visit). */\n visit: (url: string) => void | Promise<void>;\n back?: () => void | Promise<void>;\n forward?: () => void | Promise<void>;\n /** Scroll to coords or to a handle's element. */\n scrollTo: (opts: { x?: number; y?: number; handle?: string }) => void | Promise<void>;\n scrollBy: (dy: number) => void | Promise<void>;\n /** Set a field's value by handle (host dispatches input/change for React). */\n setField: (handle: string, value: unknown) => MaybePromise<{ ok: boolean; error?: string }>;\n /** Activate an element by handle. */\n click: (handle: string) => MaybePromise<{ ok: boolean; error?: string }>;\n /** Submit a form by handle. */\n submit: (handle: string) => Promise<{ ok: boolean; error?: string }> | { ok: boolean; error?: string };\n /**\n * Viewport rect of a handle's element, for agent-presence rendering (cursor +\n * highlight). Returned in tool `meta.rect` so `<CoBrowseCursorLayer>` can show\n * the agent acting on the page. Optional — omit and there's just no cursor.\n */\n rectFor?: (handle: string) => MaybePromise<{ x: number; y: number; width: number; height: number } | null>;\n /**\n * Trust-but-verify hook. When `pendingMode` is on, `page_submit` and\n * destructive `page_click` route through this; the host shows a prompt and\n * resolves true (proceed) / false (declined).\n */\n confirm?: (req: NavigationConfirmRequest) => Promise<boolean>;\n};\n\nexport type NavigationBridgeOptions = {\n adapter: NavigationBridgeAdapter;\n /** Identity tagged into activity events (so the human sees who's driving). */\n agent?: { id: string; name?: string; color?: string };\n /** Route submit + destructive clicks through `adapter.confirm`. Default true. */\n pendingMode?: boolean;\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\n\n/**\n * registerNavigationBridge — site-wide co-browsing. Lets a connected agent\n * navigate, scroll, and (with staged confirm) fill + click any page, addressed\n * by stable handles. Pairs with `useCoBrowseSession` (server + relay) and\n * `<CoBrowsePresence>` (the human's view of the agent). The 12th Fancy bridge.\n */\nexport function registerNavigationBridge(\n host: ToolHost,\n options: NavigationBridgeOptions,\n): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const pendingMode = options.pendingMode ?? true;\n const disposers: Array<() => void> = [];\n let groundedRevision: number | null = null;\n\n const staleIntent = async () => {\n const current = await adapter.getRevision?.();\n if (current === undefined) return null;\n if (groundedRevision === null || groundedRevision !== current) {\n return errorResult(\n `Human took control at page revision ${current}. This agent action was cancelled; call page_describe before acting again.`,\n );\n }\n return null;\n };\n\n ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });\n\n const target = (label: string, elementId?: string): AgentTarget => ({\n kind: \"navigation\",\n screenId: adapter.screenId,\n elementId,\n label,\n });\n\n const reg = (\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n handler: (args: JsonObject) => Promise<any> | any,\n activity: false | ((args: JsonObject) => AgentTarget),\n ) => {\n const wrapped = async (args: JsonObject) => {\n try {\n return await handler(args);\n } catch (e) {\n return errorResult(e instanceof Error ? e.message : String(e));\n }\n };\n const final = activity\n ? wrapToolWithActivity(wrapped, {\n toolName: name,\n agent: { id: agent.id, name: agent.name, color: agent.color },\n kind: \"navigation\",\n screenId: adapter.screenId,\n resolveTarget: ({ args }) => activity(args),\n })\n : wrapped;\n disposers.push(\n host.registerTool(\n {\n name,\n description,\n inputSchema: { type: \"object\", properties: properties as any, required, additionalProperties: false },\n },\n final as any,\n ),\n );\n };\n\n // ───────────── Read ─────────────\n\n reg(\n \"page_describe\",\n \"Describe the current page: its URL, title, and the interactive elements you can act on (each with a stable `handle`, role, and label). Call this first, and again after navigating.\",\n {},\n [],\n async () => {\n const snap = await adapter.describe();\n const revision = await adapter.getRevision?.();\n if (revision !== undefined) {\n snap.revision = revision;\n groundedRevision = revision;\n }\n const text = [\n `URL: ${snap.url}`,\n `Title: ${snap.title}`,\n \"\",\n ...snap.actions.map((a) => `[${a.handle}] ${a.role}: ${a.label}${a.destructive ? \" (destructive)\" : \"\"}`),\n ].join(\"\\n\");\n return textResult(text, snap);\n },\n () => target(\"Inspect page\"),\n );\n\n reg(\n \"page_read\",\n \"Read the page's visible text / heading outline for grounding.\",\n {},\n [],\n async () => textResult(adapter.read ? await adapter.read() : \"(host did not provide page text)\"),\n () => target(\"Read page\"),\n );\n\n reg(\n \"page_focus\",\n \"Move the agent's visible focus to an interactive element by stable handle without activating it.\",\n { handle: { type: \"string\" } },\n [\"handle\"],\n async (args) => {\n const stale = await staleIntent();\n if (stale) return stale;\n const handle = String(args.handle ?? \"\");\n const action = (await adapter.describe()).actions.find((candidate) => candidate.handle === handle);\n if (!action) return errorResult(`No element for handle \"${handle}\"`);\n return textResult(`Focused ${handle}`, {\n handle,\n rect: (await adapter.rectFor?.(handle)) ?? undefined,\n });\n },\n (args) => target(`Focus ${String(args.handle ?? \"\")}`, String(args.handle ?? \"\")),\n );\n\n // ───────────── Navigate / scroll ─────────────\n\n reg(\n \"nav_visit\",\n \"Navigate to a URL (same-site path or absolute). The human watches the page change.\",\n { url: { type: \"string\", description: \"Path like /packages or an absolute URL.\" } },\n [\"url\"],\n async (args) => {\n const stale = await staleIntent();\n if (stale) return stale;\n const url = String(args.url ?? \"\");\n if (!url) return errorResult(\"url is required\");\n // Scheme allow-list: permit only same-site/relative paths and http(s)\n // absolutes. A `javascript:` / `data:` / `file:` / `vbscript:` URL handed\n // to a host that wires visit() to window.location would execute as the\n // human — a semi-trusted agent (steerable via page_read prompt injection)\n // must not reach those.\n const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase();\n if (scheme && scheme !== \"http\" && scheme !== \"https\") {\n return errorResult(\n `Refusing to navigate to a '${scheme}:' URL. Only http(s) URLs or same-site paths are allowed.`,\n );\n }\n const from = (await adapter.getLocation()).url;\n await adapter.visit(url);\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: \"navigation\",\n action: \"nav_visit\",\n label: `Navigate to ${url}`,\n undo: () => {\n adapter.visit(from);\n },\n redo: () => {\n adapter.visit(url);\n },\n });\n return textResult(`Navigated to ${url}`, { url });\n },\n (args) => target(`Navigate → ${String(args.url ?? \"\")}`),\n );\n\n reg(\n \"nav_back\",\n \"Go back to the previous page.\",\n {},\n [],\n async () => {\n const stale = await staleIntent();\n if (stale) return stale;\n if (!adapter.back) return errorResult(\"Host did not provide back navigation.\");\n await adapter.back();\n return textResult(\"Went back\");\n },\n () => target(\"Back\"),\n );\n\n reg(\n \"nav_forward\",\n \"Go forward to the next page.\",\n {},\n [],\n async () => {\n const stale = await staleIntent();\n if (stale) return stale;\n if (!adapter.forward) return errorResult(\"Host did not provide forward navigation.\");\n await adapter.forward();\n return textResult(\"Went forward\");\n },\n () => target(\"Forward\"),\n );\n\n reg(\n \"nav_scroll_to\",\n \"Scroll the page to absolute coordinates, or to a specific element by its handle.\",\n {\n handle: { type: \"string\", description: \"Scroll this element into view.\" },\n x: { type: \"number\" },\n y: { type: \"number\" },\n },\n [],\n async (args) => {\n const stale = await staleIntent();\n if (stale) return stale;\n const handle = typeof args.handle === \"string\" ? args.handle : undefined;\n await adapter.scrollTo({\n handle,\n x: typeof args.x === \"number\" ? args.x : undefined,\n y: typeof args.y === \"number\" ? args.y : undefined,\n });\n return textResult(\"Scrolled\", handle ? { handle, rect: (await adapter.rectFor?.(handle)) ?? undefined } : undefined);\n },\n () => target(\"Scroll\"),\n );\n\n reg(\n \"nav_scroll_by\",\n \"Scroll the page by a vertical delta in pixels (negative scrolls up).\",\n { dy: { type: \"number\" } },\n [\"dy\"],\n async (args) => {\n const stale = await staleIntent();\n if (stale) return stale;\n await adapter.scrollBy(Number(args.dy ?? 0));\n return textResult(`Scrolled by ${Number(args.dy ?? 0)}px`);\n },\n () => target(\"Scroll\"),\n );\n\n // ───────────── Co-drive (fill / click / submit) ─────────────\n\n reg(\n \"page_set_field\",\n \"Set a form field's value by handle. The host updates the controlled input and the human sees it change.\",\n {\n handle: { type: \"string\" },\n value: { description: \"Value to set; type matches the field.\" },\n },\n [\"handle\", \"value\"],\n async (args) => {\n const stale = await staleIntent();\n if (stale) return stale;\n const handle = String(args.handle ?? \"\");\n const res = await adapter.setField(handle, args.value);\n if (!res.ok) return errorResult(res.error ?? `Could not set ${handle}`);\n return textResult(`${handle} ← ${JSON.stringify(args.value)}`, { handle, value: args.value, rect: (await adapter.rectFor?.(handle)) ?? undefined });\n },\n (args) => target(`Set ${String(args.handle ?? \"\")}`, String(args.handle ?? \"\")),\n );\n\n reg(\n \"page_click\",\n \"Activate an element by handle (link, button, checkbox…). Destructive elements are staged for the human to confirm.\",\n { handle: { type: \"string\" } },\n [\"handle\"],\n async (args) => {\n const stale = await staleIntent();\n if (stale) return stale;\n const handle = String(args.handle ?? \"\");\n const action = (await adapter.describe()).actions.find((a) => a.handle === handle);\n const rect = (await adapter.rectFor?.(handle)) ?? undefined; // capture before the click may navigate away\n if (pendingMode && action?.destructive && adapter.confirm) {\n const ok = await adapter.confirm({ action: \"click\", handle, label: action.label });\n if (!ok) return errorResult(\"Declined by user\");\n }\n const res = await adapter.click(handle);\n if (!res.ok) return errorResult(res.error ?? `Could not click ${handle}`);\n return textResult(`Clicked ${handle}`, { handle, rect });\n },\n (args) => target(`Click ${String(args.handle ?? \"\")}`, String(args.handle ?? \"\")),\n );\n\n reg(\n \"page_submit\",\n \"Submit a form by handle. Always staged for the human to confirm when pendingMode is on.\",\n { handle: { type: \"string\" } },\n [\"handle\"],\n async (args) => {\n const stale = await staleIntent();\n if (stale) return stale;\n const handle = String(args.handle ?? \"\");\n const rect = (await adapter.rectFor?.(handle)) ?? undefined; // capture before the submit navigates\n if (pendingMode && adapter.confirm) {\n const ok = await adapter.confirm({ action: \"submit\", handle, label: handle });\n if (!ok) return errorResult(\"Declined by user\");\n }\n const res = await adapter.submit(handle);\n if (!res.ok) return errorResult(res.error ?? \"Submit failed\");\n return textResult(`Submitted ${handle}`, { handle, rect });\n },\n (args) => target(`Submit ${String(args.handle ?? \"\")}`, String(args.handle ?? \"\")),\n );\n\n return {\n id: \"navigation\",\n title: \"Co-browsing\",\n dispose: () => {\n for (const d of disposers) d();\n },\n };\n}\n"]}
@@ -0,0 +1,164 @@
1
+ import { PageSnapshot, NavigationConfirmRequest, NavigationBridgeAdapter } from '../bridges/navigation.cjs';
2
+ export { PageAction } from '../bridges/navigation.cjs';
3
+ import '../tool-host-D67N2a2h.cjs';
4
+ import '../types-gzIpsUDE.cjs';
5
+ import '../types-DP5yQZQU.cjs';
6
+
7
+ /**
8
+ * The in-page half of DOM co-browsing: a framework-free walker that turns a
9
+ * live document into a {@link PageSnapshot} of stable handles, and acts on
10
+ * those handles.
11
+ *
12
+ * Extracted from px-ui-sandbox's CoBrowseProvider so a host that does NOT own
13
+ * the page (Genie's browser, showing sites through an Electron
14
+ * WebContentsView) can run the same walker, injected into an isolated world.
15
+ *
16
+ * Constraints this file holds, because the injected IIFE is built from it:
17
+ *
18
+ * - NO runtime imports. Type-only imports are erased; anything else would be
19
+ * bundled into a script that must run with no module loader.
20
+ * - Nothing touches `document` / `window` until a method is called, so the
21
+ * script can be evaluated in a context with no DOM at all.
22
+ * - Every result is plain, structured-cloneable data.
23
+ *
24
+ * Trust boundary: the page is hostile input. It controls every attribute,
25
+ * label and text node read here. Two consequences:
26
+ * - Sensitive values (passwords, card fields, one-time codes, anything marked
27
+ * `data-private`) are masked HERE, before anything leaves the page — the host
28
+ * cannot mask what it has already received.
29
+ * - Handles are compared as plain strings, never spliced into a CSS selector,
30
+ * so a crafted handle cannot change what a lookup matches.
31
+ */
32
+
33
+ /** Elements the walker considers actionable. */
34
+ declare const INTERACTIVE = "a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]";
35
+ /** Every method the in-page agent answers. The wire protocol is exactly this list. */
36
+ declare const DOM_NAVIGATION_METHODS: readonly ["getLocation", "describe", "read", "visit", "back", "forward", "scrollTo", "scrollBy", "setField", "click", "submit", "rectFor"];
37
+ type DomNavigationMethod = (typeof DOM_NAVIGATION_METHODS)[number];
38
+ type ActionResult = {
39
+ ok: boolean;
40
+ error?: string;
41
+ };
42
+ type HandleRect = {
43
+ x: number;
44
+ y: number;
45
+ width: number;
46
+ height: number;
47
+ };
48
+ type FieldElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
49
+ declare function isVisible(el: Element): boolean;
50
+ /** Sensitive fields whose value is never read into the snapshot. */
51
+ declare function isSensitive(el: Element): boolean;
52
+ declare function roleOf(el: Element): string;
53
+ declare function accessibleName(el: Element): string;
54
+ declare function isDestructive(el: Element, label: string): boolean;
55
+ /**
56
+ * The element's own stable handle: data-co-handle → name → id, or "" when it
57
+ * has none. An EMPTY attribute counts as absent — `button.name` is `""`, not
58
+ * null, on every button, anchor and input without one, so a plain `??` chain
59
+ * never reaches the id.
60
+ */
61
+ declare function attributeHandle(el: Element): string;
62
+ /**
63
+ * Build a fresh snapshot + a handle→element registry. Handle preference:
64
+ * data-co-handle → name → id → `role#index` (stable within a snapshot).
65
+ */
66
+ declare function walk(doc?: Document): {
67
+ snapshot: PageSnapshot;
68
+ registry: Map<string, Element>;
69
+ };
70
+ /**
71
+ * Set a field's value so a React-controlled input sees the change: call the
72
+ * PROTOTYPE setter (React shadows `value` on the instance with its value
73
+ * tracker), then dispatch bubbling `input` + `change`.
74
+ */
75
+ declare function setNativeValue(el: FieldElement, value: unknown): void;
76
+ /** Visible text / heading outline, with sensitive text removed. */
77
+ declare function readPage(doc?: Document): string;
78
+ type DomNavigationAgentOptions = {
79
+ /** Document to drive. Default: the global `document`, read lazily. */
80
+ document?: Document;
81
+ /**
82
+ * Called just before the agent acts on the page — before resolving a field
83
+ * for `setField`, and after resolving (only when the element exists) for
84
+ * `click` / `submit`. The sandbox uses it to attribute the request an action
85
+ * causes to the agent.
86
+ */
87
+ beforeAction?: (action: "setField" | "click" | "submit") => void;
88
+ };
89
+ type DomNavigationAgent = {
90
+ getLocation(): {
91
+ url: string;
92
+ title: string;
93
+ };
94
+ describe(): PageSnapshot;
95
+ read(): string;
96
+ /** Navigate. Only http(s) or same-site paths; deferred a tick so a remote call returns first. */
97
+ visit(url: string): void;
98
+ back(): void;
99
+ forward(): void;
100
+ scrollTo(opts: {
101
+ x?: number;
102
+ y?: number;
103
+ handle?: string;
104
+ }): void;
105
+ scrollBy(dy: number): void;
106
+ setField(handle: string, value: unknown): ActionResult;
107
+ click(handle: string): ActionResult;
108
+ submit(handle: string): ActionResult;
109
+ rectFor(handle: string): HandleRect | null;
110
+ /** Element for a handle: this snapshot's registry, then a live attribute lookup. */
111
+ resolve(handle: string): Element | null;
112
+ /** Serialisable dispatch — what the injected script exposes to a host. */
113
+ call(method: DomNavigationMethod, args?: unknown[]): unknown;
114
+ };
115
+ declare function createDomNavigationAgent(options?: DomNavigationAgentOptions): DomNavigationAgent;
116
+
117
+ /**
118
+ * The host half: a {@link NavigationBridgeAdapter} that drives a page it does
119
+ * not run in, through any transport that can carry a method name and plain
120
+ * arguments into the page and a plain result back — Electron's
121
+ * `executeJavaScript` / `executeJavaScriptInIsolatedWorld`, a MessagePort, a
122
+ * WebSocket to a remote browser.
123
+ *
124
+ * Every result is treated as hostile: the page owns the DOM the in-page walker
125
+ * reads, and if the script was injected into the MAIN world the page can
126
+ * replace the walker outright. So results are coerced to the declared shapes
127
+ * here, and anything malformed is dropped rather than passed on to the agent.
128
+ */
129
+
130
+ /** The global the injected script installs in the world it runs in. */
131
+ declare const DOM_NAVIGATION_GLOBAL = "__fancyDomNavigation";
132
+ /** Bumped only if the wire protocol changes incompatibly. */
133
+ declare const DOM_NAVIGATION_PROTOCOL = 1;
134
+ /**
135
+ * Carries one call into the page. `args` and the resolved value are plain,
136
+ * structured-cloneable data. Reject (or throw) when the call could not be
137
+ * delivered — e.g. the page navigated and the script must be re-injected.
138
+ */
139
+ type DomNavigationTransport = {
140
+ call(method: DomNavigationMethod, args: unknown[]): Promise<unknown>;
141
+ };
142
+ type RemoteNavigationAdapterOptions = {
143
+ transport: DomNavigationTransport;
144
+ /** Optional fancy-screens screen id for presence targeting. */
145
+ screenId?: string;
146
+ /** Human-control revision, if the host tracks one. Omit to disable stale-intent protection. */
147
+ getRevision?: () => number | Promise<number>;
148
+ /**
149
+ * Trust-but-verify. Without it, `page_submit` and destructive `page_click`
150
+ * are NOT staged — they run as soon as the agent asks. A host showing pages
151
+ * it does not own should always supply one.
152
+ */
153
+ confirm?: (req: NavigationConfirmRequest) => Promise<boolean>;
154
+ };
155
+ /**
156
+ * A JavaScript expression that performs one call against the injected global —
157
+ * what a host passes to `executeJavaScript`. Arguments are embedded as JSON,
158
+ * which is a valid JS expression, so no argument can break out of its literal.
159
+ */
160
+ declare function domNavigationCallScript(method: DomNavigationMethod, args?: unknown[]): string;
161
+ /** Build a NavigationBridgeAdapter backed by a transport into the page. */
162
+ declare function createRemoteNavigationAdapter(options: RemoteNavigationAdapterOptions): NavigationBridgeAdapter;
163
+
164
+ export { type ActionResult, DOM_NAVIGATION_GLOBAL, DOM_NAVIGATION_METHODS, DOM_NAVIGATION_PROTOCOL, type DomNavigationAgent, type DomNavigationAgentOptions, type DomNavigationMethod, type DomNavigationTransport, type HandleRect, INTERACTIVE, NavigationBridgeAdapter, NavigationConfirmRequest, PageSnapshot, type RemoteNavigationAdapterOptions, accessibleName, attributeHandle, createDomNavigationAgent, createRemoteNavigationAdapter, domNavigationCallScript, isDestructive, isSensitive, isVisible, readPage, roleOf, setNativeValue, walk };
@@ -0,0 +1,164 @@
1
+ import { PageSnapshot, NavigationConfirmRequest, NavigationBridgeAdapter } from '../bridges/navigation.js';
2
+ export { PageAction } from '../bridges/navigation.js';
3
+ import '../tool-host-LYfdPE8u.js';
4
+ import '../types-gzIpsUDE.js';
5
+ import '../types-VX_9je7w.js';
6
+
7
+ /**
8
+ * The in-page half of DOM co-browsing: a framework-free walker that turns a
9
+ * live document into a {@link PageSnapshot} of stable handles, and acts on
10
+ * those handles.
11
+ *
12
+ * Extracted from px-ui-sandbox's CoBrowseProvider so a host that does NOT own
13
+ * the page (Genie's browser, showing sites through an Electron
14
+ * WebContentsView) can run the same walker, injected into an isolated world.
15
+ *
16
+ * Constraints this file holds, because the injected IIFE is built from it:
17
+ *
18
+ * - NO runtime imports. Type-only imports are erased; anything else would be
19
+ * bundled into a script that must run with no module loader.
20
+ * - Nothing touches `document` / `window` until a method is called, so the
21
+ * script can be evaluated in a context with no DOM at all.
22
+ * - Every result is plain, structured-cloneable data.
23
+ *
24
+ * Trust boundary: the page is hostile input. It controls every attribute,
25
+ * label and text node read here. Two consequences:
26
+ * - Sensitive values (passwords, card fields, one-time codes, anything marked
27
+ * `data-private`) are masked HERE, before anything leaves the page — the host
28
+ * cannot mask what it has already received.
29
+ * - Handles are compared as plain strings, never spliced into a CSS selector,
30
+ * so a crafted handle cannot change what a lookup matches.
31
+ */
32
+
33
+ /** Elements the walker considers actionable. */
34
+ declare const INTERACTIVE = "a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]";
35
+ /** Every method the in-page agent answers. The wire protocol is exactly this list. */
36
+ declare const DOM_NAVIGATION_METHODS: readonly ["getLocation", "describe", "read", "visit", "back", "forward", "scrollTo", "scrollBy", "setField", "click", "submit", "rectFor"];
37
+ type DomNavigationMethod = (typeof DOM_NAVIGATION_METHODS)[number];
38
+ type ActionResult = {
39
+ ok: boolean;
40
+ error?: string;
41
+ };
42
+ type HandleRect = {
43
+ x: number;
44
+ y: number;
45
+ width: number;
46
+ height: number;
47
+ };
48
+ type FieldElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
49
+ declare function isVisible(el: Element): boolean;
50
+ /** Sensitive fields whose value is never read into the snapshot. */
51
+ declare function isSensitive(el: Element): boolean;
52
+ declare function roleOf(el: Element): string;
53
+ declare function accessibleName(el: Element): string;
54
+ declare function isDestructive(el: Element, label: string): boolean;
55
+ /**
56
+ * The element's own stable handle: data-co-handle → name → id, or "" when it
57
+ * has none. An EMPTY attribute counts as absent — `button.name` is `""`, not
58
+ * null, on every button, anchor and input without one, so a plain `??` chain
59
+ * never reaches the id.
60
+ */
61
+ declare function attributeHandle(el: Element): string;
62
+ /**
63
+ * Build a fresh snapshot + a handle→element registry. Handle preference:
64
+ * data-co-handle → name → id → `role#index` (stable within a snapshot).
65
+ */
66
+ declare function walk(doc?: Document): {
67
+ snapshot: PageSnapshot;
68
+ registry: Map<string, Element>;
69
+ };
70
+ /**
71
+ * Set a field's value so a React-controlled input sees the change: call the
72
+ * PROTOTYPE setter (React shadows `value` on the instance with its value
73
+ * tracker), then dispatch bubbling `input` + `change`.
74
+ */
75
+ declare function setNativeValue(el: FieldElement, value: unknown): void;
76
+ /** Visible text / heading outline, with sensitive text removed. */
77
+ declare function readPage(doc?: Document): string;
78
+ type DomNavigationAgentOptions = {
79
+ /** Document to drive. Default: the global `document`, read lazily. */
80
+ document?: Document;
81
+ /**
82
+ * Called just before the agent acts on the page — before resolving a field
83
+ * for `setField`, and after resolving (only when the element exists) for
84
+ * `click` / `submit`. The sandbox uses it to attribute the request an action
85
+ * causes to the agent.
86
+ */
87
+ beforeAction?: (action: "setField" | "click" | "submit") => void;
88
+ };
89
+ type DomNavigationAgent = {
90
+ getLocation(): {
91
+ url: string;
92
+ title: string;
93
+ };
94
+ describe(): PageSnapshot;
95
+ read(): string;
96
+ /** Navigate. Only http(s) or same-site paths; deferred a tick so a remote call returns first. */
97
+ visit(url: string): void;
98
+ back(): void;
99
+ forward(): void;
100
+ scrollTo(opts: {
101
+ x?: number;
102
+ y?: number;
103
+ handle?: string;
104
+ }): void;
105
+ scrollBy(dy: number): void;
106
+ setField(handle: string, value: unknown): ActionResult;
107
+ click(handle: string): ActionResult;
108
+ submit(handle: string): ActionResult;
109
+ rectFor(handle: string): HandleRect | null;
110
+ /** Element for a handle: this snapshot's registry, then a live attribute lookup. */
111
+ resolve(handle: string): Element | null;
112
+ /** Serialisable dispatch — what the injected script exposes to a host. */
113
+ call(method: DomNavigationMethod, args?: unknown[]): unknown;
114
+ };
115
+ declare function createDomNavigationAgent(options?: DomNavigationAgentOptions): DomNavigationAgent;
116
+
117
+ /**
118
+ * The host half: a {@link NavigationBridgeAdapter} that drives a page it does
119
+ * not run in, through any transport that can carry a method name and plain
120
+ * arguments into the page and a plain result back — Electron's
121
+ * `executeJavaScript` / `executeJavaScriptInIsolatedWorld`, a MessagePort, a
122
+ * WebSocket to a remote browser.
123
+ *
124
+ * Every result is treated as hostile: the page owns the DOM the in-page walker
125
+ * reads, and if the script was injected into the MAIN world the page can
126
+ * replace the walker outright. So results are coerced to the declared shapes
127
+ * here, and anything malformed is dropped rather than passed on to the agent.
128
+ */
129
+
130
+ /** The global the injected script installs in the world it runs in. */
131
+ declare const DOM_NAVIGATION_GLOBAL = "__fancyDomNavigation";
132
+ /** Bumped only if the wire protocol changes incompatibly. */
133
+ declare const DOM_NAVIGATION_PROTOCOL = 1;
134
+ /**
135
+ * Carries one call into the page. `args` and the resolved value are plain,
136
+ * structured-cloneable data. Reject (or throw) when the call could not be
137
+ * delivered — e.g. the page navigated and the script must be re-injected.
138
+ */
139
+ type DomNavigationTransport = {
140
+ call(method: DomNavigationMethod, args: unknown[]): Promise<unknown>;
141
+ };
142
+ type RemoteNavigationAdapterOptions = {
143
+ transport: DomNavigationTransport;
144
+ /** Optional fancy-screens screen id for presence targeting. */
145
+ screenId?: string;
146
+ /** Human-control revision, if the host tracks one. Omit to disable stale-intent protection. */
147
+ getRevision?: () => number | Promise<number>;
148
+ /**
149
+ * Trust-but-verify. Without it, `page_submit` and destructive `page_click`
150
+ * are NOT staged — they run as soon as the agent asks. A host showing pages
151
+ * it does not own should always supply one.
152
+ */
153
+ confirm?: (req: NavigationConfirmRequest) => Promise<boolean>;
154
+ };
155
+ /**
156
+ * A JavaScript expression that performs one call against the injected global —
157
+ * what a host passes to `executeJavaScript`. Arguments are embedded as JSON,
158
+ * which is a valid JS expression, so no argument can break out of its literal.
159
+ */
160
+ declare function domNavigationCallScript(method: DomNavigationMethod, args?: unknown[]): string;
161
+ /** Build a NavigationBridgeAdapter backed by a transport into the page. */
162
+ declare function createRemoteNavigationAdapter(options: RemoteNavigationAdapterOptions): NavigationBridgeAdapter;
163
+
164
+ export { type ActionResult, DOM_NAVIGATION_GLOBAL, DOM_NAVIGATION_METHODS, DOM_NAVIGATION_PROTOCOL, type DomNavigationAgent, type DomNavigationAgentOptions, type DomNavigationMethod, type DomNavigationTransport, type HandleRect, INTERACTIVE, NavigationBridgeAdapter, NavigationConfirmRequest, PageSnapshot, type RemoteNavigationAdapterOptions, accessibleName, attributeHandle, createDomNavigationAgent, createRemoteNavigationAdapter, domNavigationCallScript, isDestructive, isSensitive, isVisible, readPage, roleOf, setNativeValue, walk };
@@ -0,0 +1,3 @@
1
+ declare const DOM_NAVIGATION_SCRIPT: string;
2
+
3
+ export { DOM_NAVIGATION_SCRIPT };
@@ -0,0 +1,3 @@
1
+ declare const DOM_NAVIGATION_SCRIPT: string;
2
+
3
+ export { DOM_NAVIGATION_SCRIPT };