@particle-academy/agent-integrations 0.47.0 → 0.48.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.
@@ -0,0 +1,92 @@
1
+ import { registerDocBridge } from './chunk-3HVYUMQF.js';
2
+ import { textResult } from './chunk-CZV3WUW5.js';
3
+ import { childrenOf, appendOrder } from '@particle-academy/fancy-doc-commons';
4
+
5
+ var SCREEN_TEXT_NODE_TYPE = "#text";
6
+ function registerScreenDocBridge(host, options) {
7
+ const { adapter } = options;
8
+ const surface = options.surface ?? "screen";
9
+ const setText = {
10
+ verb: "set_text",
11
+ description: "Replace the literal text of an element node \u2014 the natural way to change a label, heading or paragraph. Pass the ELEMENT's id, not the text node's.",
12
+ properties: {
13
+ id: { type: "string", description: "Id of the element whose text to set." },
14
+ text: { type: "string" }
15
+ },
16
+ required: ["id", "text"],
17
+ build: (args, tree) => {
18
+ const id = String(args.id);
19
+ const node = tree.nodes[id];
20
+ if (!node) return { error: `No node ${id}` };
21
+ if (node.type === SCREEN_TEXT_NODE_TYPE) {
22
+ return { t: "set_props", id, patch: { value: String(args.text) } };
23
+ }
24
+ const texts = childrenOf(tree, id).filter((c) => c.type === SCREEN_TEXT_NODE_TYPE);
25
+ if (texts.length === 1) {
26
+ return { t: "set_props", id: texts[0].id, patch: { value: String(args.text) } };
27
+ }
28
+ if (texts.length === 0) {
29
+ return {
30
+ t: "insert",
31
+ node: {
32
+ id: `${id}-text`,
33
+ type: SCREEN_TEXT_NODE_TYPE,
34
+ parent: id,
35
+ order: appendOrder(tree, id),
36
+ props: { value: String(args.text) }
37
+ }
38
+ };
39
+ }
40
+ return {
41
+ error: `Node ${id} has ${texts.length} text children (${texts.map((t) => t.id).join(", ")}) \u2014 set_text on the one you mean`
42
+ };
43
+ },
44
+ targetId: (args) => String(args.id)
45
+ };
46
+ const bridge = registerDocBridge(host, {
47
+ adapter,
48
+ surface,
49
+ agent: options.agent,
50
+ stagePolicy: options.stagePolicy,
51
+ onPending: options.onPending,
52
+ // Anything the AGENT adds is authored by definition — it chose or was given
53
+ // the id — so it must not inherit the synthetic flag from its neighbours.
54
+ makeNode: ({ id, type, parent, order }, props) => ({ id, type, parent, order, props }),
55
+ describeNode: (n) => ({
56
+ id: n.id,
57
+ type: n.type,
58
+ parent: n.parent,
59
+ ...n.synthetic ? { synthetic: true } : {},
60
+ ...n.type === SCREEN_TEXT_NODE_TYPE ? { text: String(n.props?.value ?? "") } : { props: Object.keys(n.props ?? {}) }
61
+ }),
62
+ opTools: [setText]
63
+ });
64
+ const disposeAddressable = host.registerTool(
65
+ {
66
+ name: `${surface}_addressable`,
67
+ description: "List the node ids that are safe to remember and use later. Ids NOT listed here were minted from a node's position: they resolve today and silently point at a different node once a sibling is inserted above them. Address those in the same turn you read them, and never store one.",
68
+ inputSchema: { type: "object", properties: {}, required: [], additionalProperties: false }
69
+ },
70
+ (async () => {
71
+ const nodes = Object.values(adapter.get().nodes);
72
+ const authored = nodes.filter((n) => !n.synthetic).map((n) => ({ id: n.id, type: n.type }));
73
+ const syntheticCount = nodes.length - authored.length;
74
+ return textResult(
75
+ authored.length ? `${authored.length} addressable, ${syntheticCount} positional` : `no addressable ids \u2014 all ${syntheticCount} nodes are positional`,
76
+ { addressable: authored, syntheticCount }
77
+ );
78
+ })
79
+ );
80
+ return {
81
+ id: surface,
82
+ title: "Screen content",
83
+ dispose: () => {
84
+ disposeAddressable();
85
+ bridge.dispose();
86
+ }
87
+ };
88
+ }
89
+
90
+ export { SCREEN_TEXT_NODE_TYPE, registerScreenDocBridge };
91
+ //# sourceMappingURL=chunk-5JLTXHX6.js.map
92
+ //# sourceMappingURL=chunk-5JLTXHX6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridges/screen-doc.ts"],"names":[],"mappings":"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","file":"chunk-5JLTXHX6.js","sourcesContent":["/**\n * registerScreenDocBridge — an agent bridge over the CONTENT of one rendered\n * `<Screen doc={…}>` (fancy-screens ≥ 0.6), built on {@link registerDocBridge}.\n *\n * This is the other half of {@link registerScreensBridge}, and the halves are\n * not interchangeable. That one moves *between* screens and creates them from\n * host templates, treating a screen's contents as an opaque `config` blob. This\n * one addresses the nodes *inside* one screen: read them, patch a prop, retext a\n * label, reparent a card.\n *\n * Until fancy-screens 0.6 there was no way to do that at all — `ScreenSchema`\n * nodes had no `id`, so an agent could emit a whole surface and then not touch\n * anything in it. That was a standing violation of the component contract's\n * stable-handles requirement, on the one shipped \"agent emits a UI\" path.\n *\n * A screen document is a plain `DocTree`, so the generic bridge supplies\n * describe/get/list/get_node + add/update/remove/move + undo + agent activity +\n * staged writes for free. What this layer adds is everything that is specific to\n * a screen being a rendered React tree:\n *\n * - **Synthetic ids are reported as such.** fancy-screens mints a position-derived\n * id for any node the author left anonymous. It looks like a handle and is not\n * one: insert a sibling above it and it points at a different node, silently.\n * Every read here says which kind of id it handed back, and `screen_addressable`\n * lists only the durable ones.\n * - **Literal text is a node**, not a prop, so that a string and an element can be\n * siblings without losing their order. `screen_set_text` hides that from the\n * agent, which otherwise has to know the reserved `#text` type to change a label.\n *\n * No dependency on fancy-screens — the contract is the `DocTree`, mirroring how\n * `bridges/screens.ts` keeps its own snapshot types loose.\n */\nimport { textResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { Bridge } from \"./types\";\nimport { registerDocBridge, type DocAdapter, type DocOpTool } from \"./doc\";\nexport type { DocAdapter } from \"./doc\";\nimport {\n childrenOf,\n appendOrder,\n type DocNode,\n type StagePolicy,\n type TreeOp,\n} from \"@particle-academy/fancy-doc-commons\";\n\n/**\n * Reserved node type for a literal string child. Must match fancy-screens'\n * `TEXT_NODE_TYPE`; duplicated rather than imported to keep this bridge free of\n * a fancy-screens dependency.\n */\nexport const SCREEN_TEXT_NODE_TYPE = \"#text\";\n\n/** A screen document node — a `DocNode` plus the minted-id flag. */\nexport interface ScreenDocNode extends DocNode {\n /**\n * True when fancy-screens minted this id because the author supplied none.\n * A synthetic id is derived from position, so it is not a durable handle.\n */\n synthetic?: boolean;\n}\n\nexport type ScreenDocOp = TreeOp<ScreenDocNode>;\n\nexport interface ScreenDocBridgeOptions {\n adapter: DocAdapter<ScreenDocNode>;\n /**\n * Tool-name prefix. Default `\"screen\"` → `screen_add`, `screen_set_text`, …\n *\n * Tool names are global, so a host wiring more than one doc-driven screen at\n * once must give each a distinct prefix (`\"screen_checkout\"`).\n */\n surface?: string;\n agent?: { id: string; name?: string; color?: string };\n /** Auto-apply vs. confirm per op (trust-but-verify). Default: auto. */\n stagePolicy?: StagePolicy<ScreenDocOp>;\n onPending?: (staged: { id: string; op: ScreenDocOp; label: string }) => void;\n}\n\n/** Register the screen-content bridge (prefix `screen_`) over a screen's `DocTree`. */\nexport function registerScreenDocBridge(host: ToolHost, options: ScreenDocBridgeOptions): Bridge {\n const { adapter } = options;\n const surface = options.surface ?? \"screen\";\n\n const setText: DocOpTool<ScreenDocNode, ScreenDocOp> = {\n verb: \"set_text\",\n description:\n \"Replace the literal text of an element node — the natural way to change a label, heading or paragraph. Pass the ELEMENT's id, not the text node's.\",\n properties: {\n id: { type: \"string\", description: \"Id of the element whose text to set.\" },\n text: { type: \"string\" },\n },\n required: [\"id\", \"text\"],\n build: (args, tree) => {\n const id = String(args.id);\n const node = tree.nodes[id];\n if (!node) return { error: `No node ${id}` };\n if (node.type === SCREEN_TEXT_NODE_TYPE) {\n return { t: \"set_props\", id, patch: { value: String(args.text) } };\n }\n\n const texts = childrenOf(tree, id).filter((c) => c.type === SCREEN_TEXT_NODE_TYPE);\n if (texts.length === 1) {\n return { t: \"set_props\", id: texts[0]!.id, patch: { value: String(args.text) } };\n }\n if (texts.length === 0) {\n // Nothing to retext, so give it text. The minted child is addressable in\n // its own right, which is what makes the follow-up edit possible.\n return {\n t: \"insert\",\n node: {\n id: `${id}-text`,\n type: SCREEN_TEXT_NODE_TYPE,\n parent: id,\n order: appendOrder(tree, id),\n props: { value: String(args.text) },\n },\n };\n }\n // Ambiguous on purpose: picking one would look like it worked.\n return {\n error: `Node ${id} has ${texts.length} text children (${texts.map((t) => t.id).join(\", \")}) — set_text on the one you mean`,\n };\n },\n targetId: (args) => String(args.id),\n };\n\n const bridge = registerDocBridge<ScreenDocNode, ScreenDocOp>(host, {\n adapter,\n surface,\n agent: options.agent,\n stagePolicy: options.stagePolicy,\n onPending: options.onPending,\n // Anything the AGENT adds is authored by definition — it chose or was given\n // the id — so it must not inherit the synthetic flag from its neighbours.\n makeNode: ({ id, type, parent, order }, props) => ({ id, type, parent, order, props }),\n describeNode: (n) => ({\n id: n.id,\n type: n.type,\n parent: n.parent,\n ...(n.synthetic ? { synthetic: true } : {}),\n ...(n.type === SCREEN_TEXT_NODE_TYPE ? { text: String(n.props?.value ?? \"\") } : { props: Object.keys(n.props ?? {}) }),\n }),\n opTools: [setText],\n });\n\n // A read tool, so it lives here rather than in `opTools` (which are mutations).\n const disposeAddressable = host.registerTool(\n {\n name: `${surface}_addressable`,\n description:\n \"List the node ids that are safe to remember and use later. Ids NOT listed here were minted from a node's position: they resolve today and silently point at a different node once a sibling is inserted above them. Address those in the same turn you read them, and never store one.\",\n inputSchema: { type: \"object\", properties: {} as never, required: [], additionalProperties: false },\n },\n (async () => {\n const nodes = Object.values(adapter.get().nodes);\n const authored = nodes.filter((n) => !n.synthetic).map((n) => ({ id: n.id, type: n.type }));\n const syntheticCount = nodes.length - authored.length;\n\n return textResult(\n authored.length\n ? `${authored.length} addressable, ${syntheticCount} positional`\n : `no addressable ids — all ${syntheticCount} nodes are positional`,\n { addressable: authored, syntheticCount },\n );\n }) as never,\n );\n\n return {\n id: surface,\n title: \"Screen content\",\n dispose: () => {\n disposeAddressable();\n bridge.dispose();\n },\n };\n}\n"]}
@@ -4,6 +4,22 @@ import { textResult, errorResult } from './chunk-CZV3WUW5.js';
4
4
 
5
5
  // src/bridges/navigation.ts
6
6
  var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
7
+ var isControl = (code) => code < 32 || code >= 127 && code <= 159 || code === 8232 || code === 8233;
8
+ function oneLine(value, max) {
9
+ const raw = typeof value === "string" ? value : value == null ? "" : String(value);
10
+ const bounded = raw.length > max * 4 ? raw.slice(0, max * 4) : raw;
11
+ let out = "";
12
+ for (const c of bounded) out += isControl(c.charCodeAt(0)) ? " " : c;
13
+ out = out.replace(/\s+/g, " ").trim();
14
+ return out.length > max ? out.slice(0, max - 3) + "..." : out;
15
+ }
16
+ var SAFE_HANDLE = /^[A-Za-z0-9_.:#@/-]{1,80}$/;
17
+ var CAP = { url: 500, title: 200, role: 40, label: 200, heading: 200, text: 4e3, error: 300, headings: 50 };
18
+ function formatReadout(r) {
19
+ if (typeof r === "string") return oneLine(r, CAP.text);
20
+ const headings = (Array.isArray(r.headings) ? r.headings : []).slice(0, CAP.headings).map((h) => `H${Math.min(6, Math.max(1, Math.trunc(Number(h?.level)) || 1))}: ${oneLine(h?.text, CAP.heading)}`);
21
+ return [oneLine(r.title, CAP.title), ...headings, "", oneLine(r.text, CAP.text)].join("\n");
22
+ }
7
23
  function registerNavigationBridge(host, options) {
8
24
  const { adapter } = options;
9
25
  const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
@@ -20,6 +36,9 @@ function registerNavigationBridge(host, options) {
20
36
  }
21
37
  return null;
22
38
  };
39
+ let aliases = /* @__PURE__ */ new Map();
40
+ const pageHandle = (handle) => aliases.get(handle) ?? handle;
41
+ const failure = (message, fallback) => errorResult(oneLine(message ?? fallback, CAP.error) || fallback);
23
42
  ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
24
43
  const target = (label, elementId) => ({
25
44
  kind: "navigation",
@@ -32,7 +51,7 @@ function registerNavigationBridge(host, options) {
32
51
  try {
33
52
  return await handler(args);
34
53
  } catch (e) {
35
- return errorResult(e instanceof Error ? e.message : String(e));
54
+ return failure(e instanceof Error ? e.message : String(e), `${name} failed`);
36
55
  }
37
56
  };
38
57
  const final = activity ? wrapToolWithActivity(wrapped, {
@@ -59,17 +78,29 @@ function registerNavigationBridge(host, options) {
59
78
  {},
60
79
  [],
61
80
  async () => {
62
- const snap = await adapter.describe();
81
+ const raw = await adapter.describe();
82
+ const next = /* @__PURE__ */ new Map();
83
+ const actions = (Array.isArray(raw.actions) ? raw.actions : []).map((a) => {
84
+ const handle = String(a.handle ?? "");
85
+ if (SAFE_HANDLE.test(handle)) return a;
86
+ const alias = `~${next.size}`;
87
+ next.set(alias, handle);
88
+ return { ...a, handle: alias };
89
+ });
90
+ aliases = next;
91
+ const snap = { ...raw, actions };
63
92
  const revision = await adapter.getRevision?.();
64
93
  if (revision !== void 0) {
65
94
  snap.revision = revision;
66
95
  groundedRevision = revision;
67
96
  }
68
97
  const text = [
69
- `URL: ${snap.url}`,
70
- `Title: ${snap.title}`,
98
+ `URL: ${oneLine(snap.url, CAP.url)}`,
99
+ `Title: ${oneLine(snap.title, CAP.title)}`,
71
100
  "",
72
- ...snap.actions.map((a) => `[${a.handle}] ${a.role}: ${a.label}${a.destructive ? " (destructive)" : ""}`)
101
+ ...actions.map(
102
+ (a) => `[${a.handle}] ${oneLine(a.role, CAP.role)}: ${oneLine(a.label, CAP.label)}${a.destructive ? " (destructive)" : ""}`
103
+ )
73
104
  ].join("\n");
74
105
  return textResult(text, snap);
75
106
  },
@@ -80,7 +111,10 @@ function registerNavigationBridge(host, options) {
80
111
  "Read the page's visible text / heading outline for grounding.",
81
112
  {},
82
113
  [],
83
- async () => textResult(adapter.read ? await adapter.read() : "(host did not provide page text)"),
114
+ async () => {
115
+ const r = adapter.read ? await adapter.read() : void 0;
116
+ return textResult(r == null ? "(host did not provide page text)" : formatReadout(r));
117
+ },
84
118
  () => target("Read page")
85
119
  );
86
120
  reg(
@@ -91,11 +125,12 @@ function registerNavigationBridge(host, options) {
91
125
  async (args) => {
92
126
  const stale = await staleIntent();
93
127
  if (stale) return stale;
94
- const handle = String(args.handle ?? "");
128
+ const shown = String(args.handle ?? "");
129
+ const handle = pageHandle(shown);
95
130
  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,
131
+ if (!action) return failure(`No element for handle "${shown}"`, "No element for that handle");
132
+ return textResult(`Focused ${oneLine(shown, CAP.label)}`, {
133
+ handle: shown,
99
134
  rect: await adapter.rectFor?.(handle) ?? void 0
100
135
  });
101
136
  },
@@ -175,13 +210,17 @@ function registerNavigationBridge(host, options) {
175
210
  async (args) => {
176
211
  const stale = await staleIntent();
177
212
  if (stale) return stale;
178
- const handle = typeof args.handle === "string" ? args.handle : void 0;
213
+ const shown = typeof args.handle === "string" ? args.handle : void 0;
214
+ const handle = shown === void 0 ? void 0 : pageHandle(shown);
179
215
  await adapter.scrollTo({
180
216
  handle,
181
217
  x: typeof args.x === "number" ? args.x : void 0,
182
218
  y: typeof args.y === "number" ? args.y : void 0
183
219
  });
184
- return textResult("Scrolled", handle ? { handle, rect: await adapter.rectFor?.(handle) ?? void 0 } : void 0);
220
+ return textResult(
221
+ "Scrolled",
222
+ handle ? { handle: shown, rect: await adapter.rectFor?.(handle) ?? void 0 } : void 0
223
+ );
185
224
  },
186
225
  () => target("Scroll")
187
226
  );
@@ -209,10 +248,15 @@ function registerNavigationBridge(host, options) {
209
248
  async (args) => {
210
249
  const stale = await staleIntent();
211
250
  if (stale) return stale;
212
- const handle = String(args.handle ?? "");
251
+ const shown = String(args.handle ?? "");
252
+ const handle = pageHandle(shown);
213
253
  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 });
254
+ if (!res.ok) return failure(res.error, `Could not set ${oneLine(shown, CAP.label)}`);
255
+ return textResult(`${oneLine(shown, CAP.label)} \u2190 ${JSON.stringify(args.value)}`, {
256
+ handle: shown,
257
+ value: args.value,
258
+ rect: await adapter.rectFor?.(handle) ?? void 0
259
+ });
216
260
  },
217
261
  (args) => target(`Set ${String(args.handle ?? "")}`, String(args.handle ?? ""))
218
262
  );
@@ -224,7 +268,8 @@ function registerNavigationBridge(host, options) {
224
268
  async (args) => {
225
269
  const stale = await staleIntent();
226
270
  if (stale) return stale;
227
- const handle = String(args.handle ?? "");
271
+ const shown = String(args.handle ?? "");
272
+ const handle = pageHandle(shown);
228
273
  const action = (await adapter.describe()).actions.find((a) => a.handle === handle);
229
274
  const rect = await adapter.rectFor?.(handle) ?? void 0;
230
275
  if (pendingMode && action?.destructive && adapter.confirm) {
@@ -232,8 +277,8 @@ function registerNavigationBridge(host, options) {
232
277
  if (!ok) return errorResult("Declined by user");
233
278
  }
234
279
  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 });
280
+ if (!res.ok) return failure(res.error, `Could not click ${oneLine(shown, CAP.label)}`);
281
+ return textResult(`Clicked ${oneLine(shown, CAP.label)}`, { handle: shown, rect });
237
282
  },
238
283
  (args) => target(`Click ${String(args.handle ?? "")}`, String(args.handle ?? ""))
239
284
  );
@@ -245,15 +290,16 @@ function registerNavigationBridge(host, options) {
245
290
  async (args) => {
246
291
  const stale = await staleIntent();
247
292
  if (stale) return stale;
248
- const handle = String(args.handle ?? "");
293
+ const shown = String(args.handle ?? "");
294
+ const handle = pageHandle(shown);
249
295
  const rect = await adapter.rectFor?.(handle) ?? void 0;
250
296
  if (pendingMode && adapter.confirm) {
251
297
  const ok = await adapter.confirm({ action: "submit", handle, label: handle });
252
298
  if (!ok) return errorResult("Declined by user");
253
299
  }
254
300
  const res = await adapter.submit(handle);
255
- if (!res.ok) return errorResult(res.error ?? "Submit failed");
256
- return textResult(`Submitted ${handle}`, { handle, rect });
301
+ if (!res.ok) return failure(res.error, "Submit failed");
302
+ return textResult(`Submitted ${oneLine(shown, CAP.label)}`, { handle: shown, rect });
257
303
  },
258
304
  (args) => target(`Submit ${String(args.handle ?? "")}`, String(args.handle ?? ""))
259
305
  );
@@ -267,5 +313,5 @@ function registerNavigationBridge(host, options) {
267
313
  }
268
314
 
269
315
  export { registerNavigationBridge };
270
- //# sourceMappingURL=chunk-JGJHPH65.js.map
271
- //# sourceMappingURL=chunk-JGJHPH65.js.map
316
+ //# sourceMappingURL=chunk-OHNGURUJ.js.map
317
+ //# sourceMappingURL=chunk-OHNGURUJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridges/navigation.ts"],"names":[],"mappings":"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","file":"chunk-OHNGURUJ.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/**\n * Structured page text. Preferred over a plain string from `read()`: the bridge\n * can then put every page-supplied string on its own single line and keep the\n * outline. A plain string is untrusted as a whole and is rendered as ONE line.\n */\nexport type PageReadout = {\n title: string;\n headings: Array<{ level: number; text: string }>;\n text: string;\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). Prefer a {@link PageReadout}. */\n read?: () => MaybePromise<string | PageReadout>;\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// ───────────── Text safety ─────────────\n//\n// Every string below the adapter is page-supplied, and the page may be hostile.\n// The tool TEXT is what lands in the agent's context, so a line break inside a\n// label would let the page write its own lines there: a forged action, a forged\n// heading, forged tool framing. Each page string is therefore rendered on ONE\n// line, with C0/C1 controls and the Unicode line/paragraph separators replaced,\n// and capped. (Control characters are tested by code point, so no escape\n// sequence is involved.)\n\nconst isControl = (code: number) =>\n code < 0x20 || (code >= 0x7f && code <= 0x9f) || code === 0x2028 || code === 0x2029;\n\n/** One line, controls replaced by spaces, whitespace collapsed, capped at `max`. */\nfunction oneLine(value: unknown, max: number): string {\n const raw = typeof value === \"string\" ? value : value == null ? \"\" : String(value);\n // Bound the work on a hostile multi-megabyte string before scanning it.\n const bounded = raw.length > max * 4 ? raw.slice(0, max * 4) : raw;\n let out = \"\";\n for (const c of bounded) out += isControl(c.charCodeAt(0)) ? \" \" : c;\n out = out.replace(/\\s+/g, \" \").trim();\n return out.length > max ? out.slice(0, max - 3) + \"...\" : out;\n}\n\n/**\n * A handle shown verbatim must not be able to imitate the `[handle] role:`\n * syntax, so only this charset is shown as-is. Anything else — spaces,\n * brackets, controls, or a `~`, which is reserved so a page cannot squat on an\n * alias — is shown as a bridge-minted `~N` alias that resolves back to it.\n */\nconst SAFE_HANDLE = /^[A-Za-z0-9_.:#@/-]{1,80}$/;\n\nconst CAP = { url: 500, title: 200, role: 40, label: 200, heading: 200, text: 4000, error: 300, headings: 50 };\n\nfunction formatReadout(r: string | PageReadout): string {\n if (typeof r === \"string\") return oneLine(r, CAP.text);\n const headings = (Array.isArray(r.headings) ? r.headings : [])\n .slice(0, CAP.headings)\n .map((h) => `H${Math.min(6, Math.max(1, Math.trunc(Number(h?.level)) || 1))}: ${oneLine(h?.text, CAP.heading)}`);\n return [oneLine(r.title, CAP.title), ...headings, \"\", oneLine(r.text, CAP.text)].join(\"\\n\");\n}\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 /** Alias → page handle, from the latest page_describe. */\n let aliases = new Map<string, string>();\n /** The handle the adapter knows, for a handle the agent passed back. */\n const pageHandle = (handle: string) => aliases.get(handle) ?? handle;\n /** A failure message that may carry page-supplied text. */\n const failure = (message: unknown, fallback: string) => errorResult(oneLine(message ?? fallback, CAP.error) || fallback);\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 // A transport into the page can surface the page's own error text.\n return failure(e instanceof Error ? e.message : String(e), `${name} failed`);\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 raw = await adapter.describe();\n const next = new Map<string, string>();\n const actions = (Array.isArray(raw.actions) ? raw.actions : []).map((a) => {\n const handle = String(a.handle ?? \"\");\n if (SAFE_HANDLE.test(handle)) return a;\n const alias = `~${next.size}`;\n next.set(alias, handle);\n return { ...a, handle: alias };\n });\n aliases = next;\n const snap: PageSnapshot = { ...raw, actions };\n const revision = await adapter.getRevision?.();\n if (revision !== undefined) {\n snap.revision = revision;\n groundedRevision = revision;\n }\n const text = [\n `URL: ${oneLine(snap.url, CAP.url)}`,\n `Title: ${oneLine(snap.title, CAP.title)}`,\n \"\",\n ...actions.map(\n (a) =>\n `[${a.handle}] ${oneLine(a.role, CAP.role)}: ${oneLine(a.label, CAP.label)}${a.destructive ? \" (destructive)\" : \"\"}`,\n ),\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 () => {\n const r = adapter.read ? await adapter.read() : undefined;\n return textResult(r == null ? \"(host did not provide page text)\" : formatReadout(r));\n },\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 shown = String(args.handle ?? \"\");\n const handle = pageHandle(shown);\n const action = (await adapter.describe()).actions.find((candidate) => candidate.handle === handle);\n if (!action) return failure(`No element for handle \"${shown}\"`, \"No element for that handle\");\n return textResult(`Focused ${oneLine(shown, CAP.label)}`, {\n handle: shown,\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 shown = typeof args.handle === \"string\" ? args.handle : undefined;\n const handle = shown === undefined ? undefined : pageHandle(shown);\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(\n \"Scrolled\",\n handle ? { handle: shown, rect: (await adapter.rectFor?.(handle)) ?? undefined } : undefined,\n );\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 shown = String(args.handle ?? \"\");\n const handle = pageHandle(shown);\n const res = await adapter.setField(handle, args.value);\n if (!res.ok) return failure(res.error, `Could not set ${oneLine(shown, CAP.label)}`);\n return textResult(`${oneLine(shown, CAP.label)} ← ${JSON.stringify(args.value)}`, {\n handle: shown,\n value: args.value,\n rect: (await adapter.rectFor?.(handle)) ?? undefined,\n });\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 shown = String(args.handle ?? \"\");\n const handle = pageHandle(shown);\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 failure(res.error, `Could not click ${oneLine(shown, CAP.label)}`);\n return textResult(`Clicked ${oneLine(shown, CAP.label)}`, { handle: shown, 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 shown = String(args.handle ?? \"\");\n const handle = pageHandle(shown);\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 failure(res.error, \"Submit failed\");\n return textResult(`Submitted ${oneLine(shown, CAP.label)}`, { handle: shown, 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"]}
@@ -1,4 +1,4 @@
1
- import { PageSnapshot, NavigationConfirmRequest, NavigationBridgeAdapter } from '../bridges/navigation.cjs';
1
+ import { PageSnapshot, PageReadout, NavigationConfirmRequest, NavigationBridgeAdapter } from '../bridges/navigation.cjs';
2
2
  export { PageAction } from '../bridges/navigation.cjs';
3
3
  import '../tool-host-D67N2a2h.cjs';
4
4
  import '../types-gzIpsUDE.cjs';
@@ -68,13 +68,24 @@ declare function walk(doc?: Document): {
68
68
  registry: Map<string, Element>;
69
69
  };
70
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`.
71
+ * Set a field's value so a React-controlled input sees the change.
72
+ *
73
+ * - Text, textarea, select: call the PROTOTYPE `value` setter (React shadows
74
+ * `value` on the instance with its value tracker), then dispatch bubbling
75
+ * `input` + `change`.
76
+ * - Checkbox, radio: React's onChange for these listens to `click`, not
77
+ * `input` / `change`, so the write is a real `click()` — the browser toggles
78
+ * the box and fires click, input and change itself. Only when the state
79
+ * actually differs, so setting the current value is a no-op rather than a
80
+ * toggle. A radio cannot be unchecked by clicking it; that one case falls
81
+ * back to the setter.
74
82
  */
75
83
  declare function setNativeValue(el: FieldElement, value: unknown): void;
76
- /** Visible text / heading outline, with sensitive text removed. */
77
- declare function readPage(doc?: Document): string;
84
+ /**
85
+ * Visible text / heading outline, with sensitive text removed. Structured, so
86
+ * the bridge can render each page-supplied string on its own single line.
87
+ */
88
+ declare function readPage(doc?: Document): PageReadout;
78
89
  type DomNavigationAgentOptions = {
79
90
  /** Document to drive. Default: the global `document`, read lazily. */
80
91
  document?: Document;
@@ -92,7 +103,7 @@ type DomNavigationAgent = {
92
103
  title: string;
93
104
  };
94
105
  describe(): PageSnapshot;
95
- read(): string;
106
+ read(): PageReadout;
96
107
  /** Navigate. Only http(s) or same-site paths; deferred a tick so a remote call returns first. */
97
108
  visit(url: string): void;
98
109
  back(): void;
@@ -161,4 +172,4 @@ declare function domNavigationCallScript(method: DomNavigationMethod, args?: unk
161
172
  /** Build a NavigationBridgeAdapter backed by a transport into the page. */
162
173
  declare function createRemoteNavigationAdapter(options: RemoteNavigationAdapterOptions): NavigationBridgeAdapter;
163
174
 
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 };
175
+ 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, PageReadout, PageSnapshot, type RemoteNavigationAdapterOptions, accessibleName, attributeHandle, createDomNavigationAgent, createRemoteNavigationAdapter, domNavigationCallScript, isDestructive, isSensitive, isVisible, readPage, roleOf, setNativeValue, walk };
@@ -1,4 +1,4 @@
1
- import { PageSnapshot, NavigationConfirmRequest, NavigationBridgeAdapter } from '../bridges/navigation.js';
1
+ import { PageSnapshot, PageReadout, NavigationConfirmRequest, NavigationBridgeAdapter } from '../bridges/navigation.js';
2
2
  export { PageAction } from '../bridges/navigation.js';
3
3
  import '../tool-host-LYfdPE8u.js';
4
4
  import '../types-gzIpsUDE.js';
@@ -68,13 +68,24 @@ declare function walk(doc?: Document): {
68
68
  registry: Map<string, Element>;
69
69
  };
70
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`.
71
+ * Set a field's value so a React-controlled input sees the change.
72
+ *
73
+ * - Text, textarea, select: call the PROTOTYPE `value` setter (React shadows
74
+ * `value` on the instance with its value tracker), then dispatch bubbling
75
+ * `input` + `change`.
76
+ * - Checkbox, radio: React's onChange for these listens to `click`, not
77
+ * `input` / `change`, so the write is a real `click()` — the browser toggles
78
+ * the box and fires click, input and change itself. Only when the state
79
+ * actually differs, so setting the current value is a no-op rather than a
80
+ * toggle. A radio cannot be unchecked by clicking it; that one case falls
81
+ * back to the setter.
74
82
  */
75
83
  declare function setNativeValue(el: FieldElement, value: unknown): void;
76
- /** Visible text / heading outline, with sensitive text removed. */
77
- declare function readPage(doc?: Document): string;
84
+ /**
85
+ * Visible text / heading outline, with sensitive text removed. Structured, so
86
+ * the bridge can render each page-supplied string on its own single line.
87
+ */
88
+ declare function readPage(doc?: Document): PageReadout;
78
89
  type DomNavigationAgentOptions = {
79
90
  /** Document to drive. Default: the global `document`, read lazily. */
80
91
  document?: Document;
@@ -92,7 +103,7 @@ type DomNavigationAgent = {
92
103
  title: string;
93
104
  };
94
105
  describe(): PageSnapshot;
95
- read(): string;
106
+ read(): PageReadout;
96
107
  /** Navigate. Only http(s) or same-site paths; deferred a tick so a remote call returns first. */
97
108
  visit(url: string): void;
98
109
  back(): void;
@@ -161,4 +172,4 @@ declare function domNavigationCallScript(method: DomNavigationMethod, args?: unk
161
172
  /** Build a NavigationBridgeAdapter backed by a transport into the page. */
162
173
  declare function createRemoteNavigationAdapter(options: RemoteNavigationAdapterOptions): NavigationBridgeAdapter;
163
174
 
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 };
175
+ 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, PageReadout, PageSnapshot, type RemoteNavigationAdapterOptions, accessibleName, attributeHandle, createDomNavigationAgent, createRemoteNavigationAdapter, domNavigationCallScript, isDestructive, isSensitive, isVisible, readPage, roleOf, setNativeValue, walk };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // dom-navigation-inject:virtual:dom-navigation-inject
4
- var virtual_dom_navigation_inject_default = '"use strict";\n(() => {\n // src/dom-navigation/agent.ts\n var INTERACTIVE = "a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]";\n var tag = (el) => el.tagName.toUpperCase();\n function isVisible(el) {\n const he = el;\n if (el.type === "hidden") return false;\n if (he.hidden || he.getAttribute("aria-hidden") === "true") return false;\n return he.offsetParent !== null || he.getClientRects().length > 0;\n }\n function isSensitive(el) {\n const input = el;\n const ac = (el.getAttribute("autocomplete") ?? "").toLowerCase();\n return input.type === "password" || ac.startsWith("cc-") || ac === "one-time-code" || el.hasAttribute("data-private");\n }\n function insideSensitive(node) {\n for (let el = node.parentElement; el; el = el.parentElement) {\n if (isSensitive(el)) return true;\n }\n return false;\n }\n function safeText(root) {\n const doc = root.ownerDocument ?? root;\n const walker = doc.createTreeWalker(root, 4);\n let out = "";\n for (let n = walker.nextNode(); n; n = walker.nextNode()) {\n if (!insideSensitive(n)) out += n.nodeValue ?? "";\n }\n return out;\n }\n function roleOf(el) {\n const explicit = el.getAttribute("role");\n if (explicit) return explicit;\n const t = tag(el);\n if (t === "A") return "link";\n if (t === "BUTTON") return "button";\n if (t === "SELECT") return "select";\n if (t === "TEXTAREA") return "textbox";\n if (t === "INPUT") {\n const type = el.type;\n if (type === "checkbox") return "checkbox";\n if (type === "radio") return "radio";\n if (type === "submit" || type === "button") return "button";\n return "textbox";\n }\n return "generic";\n }\n function labelFor(doc, id) {\n for (const lbl of Array.from(doc.querySelectorAll("label[for]"))) {\n if (lbl.getAttribute("for") === id) return lbl;\n }\n return null;\n }\n function accessibleName(el) {\n const doc = el.ownerDocument;\n const aria = el.getAttribute("aria-label");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute("aria-labelledby");\n if (labelledby) {\n const txt = labelledby.split(/\\s+/).map((id2) => doc.getElementById(id2)?.textContent ?? "").join(" ").trim();\n if (txt) return txt;\n }\n const id = el.getAttribute("id");\n if (id) {\n const lbl = labelFor(doc, id);\n if (lbl?.textContent?.trim()) return lbl.textContent.trim();\n }\n const wrapping = el.closest("label");\n if (wrapping) {\n const txt = safeText(wrapping).trim();\n if (txt) return txt;\n }\n const placeholder = el.getAttribute("placeholder");\n if (placeholder) return placeholder.trim();\n const text = isSensitive(el) ? "" : el.textContent?.trim();\n if (text) return text.replace(/\\s+/g, " ").slice(0, 80);\n return el.getAttribute("name") ?? el.getAttribute("id") ?? roleOf(el);\n }\n function isDestructive(el, label) {\n if (el.hasAttribute("data-co-confirm")) return true;\n if (el.type === "submit") return true;\n if (tag(el) === "BUTTON" && el.type === "submit") return true;\n return /\\b(delete|remove|destroy|sign out|log ?out|cancel subscription|deactivate)\\b/i.test(label);\n }\n function attributeHandle(el) {\n return el.getAttribute("data-co-handle") || el.getAttribute("name") || el.getAttribute("id") || "";\n }\n function walk(doc = document) {\n const registry = /* @__PURE__ */ new Map();\n const actions = [];\n const seen = /* @__PURE__ */ new Set();\n let synthetic = 0;\n for (const el of Array.from(doc.querySelectorAll(INTERACTIVE))) {\n if (seen.has(el) || !isVisible(el)) continue;\n seen.add(el);\n const label = accessibleName(el);\n const role = roleOf(el);\n let handle = attributeHandle(el);\n if (!handle || registry.has(handle)) handle = `${role}#${synthetic++}`;\n registry.set(handle, el);\n let value;\n if (role === "textbox" || role === "select") {\n value = isSensitive(el) ? void 0 : el.value;\n } else if (role === "checkbox" || role === "radio") {\n value = el.checked;\n }\n const action = { handle, role, label, destructive: isDestructive(el, label) };\n if (value !== void 0) action.value = value;\n actions.push(action);\n }\n const loc = doc.defaultView?.location;\n return {\n snapshot: { url: loc ? loc.pathname + loc.search : "", title: doc.title, actions },\n registry\n };\n }\n function isField(el) {\n const t = tag(el);\n return t === "INPUT" || t === "TEXTAREA" || t === "SELECT";\n }\n function setNativeValue(el, value) {\n const view = el.ownerDocument.defaultView ?? globalThis;\n const fire = () => {\n el.dispatchEvent(new view.Event("input", { bubbles: true }));\n el.dispatchEvent(new view.Event("change", { bubbles: true }));\n };\n const t = tag(el);\n if (t === "INPUT" && (el.type === "checkbox" || el.type === "radio")) {\n const setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, "checked")?.set;\n setter2?.call(el, Boolean(value));\n fire();\n return;\n }\n const proto = t === "TEXTAREA" ? view.HTMLTextAreaElement.prototype : t === "SELECT" ? view.HTMLSelectElement.prototype : view.HTMLInputElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;\n setter?.call(el, String(value));\n fire();\n }\n function readPage(doc = document) {\n const main = doc.querySelector("main") ?? doc.body;\n if (!main) return doc.title;\n const heads = Array.from(main.querySelectorAll("h1, h2, h3")).map((h) => `${h.tagName}: ${safeText(h).trim()}`).filter(Boolean);\n const text = safeText(main).replace(/\\s+/g, " ").trim().slice(0, 4e3);\n return [doc.title, ...heads, "", text].join("\\n");\n }\n function findByAttribute(doc, attr, value) {\n for (const el of Array.from(doc.querySelectorAll(`[${attr}]`))) {\n if (el.getAttribute(attr) === value) return el;\n }\n return null;\n }\n var num = (v) => typeof v === "number" ? v : void 0;\n function createDomNavigationAgent(options = {}) {\n let registry = /* @__PURE__ */ new Map();\n const doc = () => options.document ?? document;\n const win = () => doc().defaultView ?? window;\n const before = (action) => options.beforeAction?.(action);\n const resolve = (handle) => {\n const hit = registry.get(handle);\n if (hit && hit.isConnected) return hit;\n const d = doc();\n return findByAttribute(d, "data-co-handle", handle) ?? findByAttribute(d, "name", handle) ?? d.getElementById(handle);\n };\n const agent = {\n getLocation: () => {\n const loc = win().location;\n return { url: loc.pathname + loc.search, title: doc().title };\n },\n describe: () => {\n const res = walk(doc());\n registry = res.registry;\n return res.snapshot;\n },\n read: () => readPage(doc()),\n visit: (url) => {\n const w = win();\n const target = new URL(String(url), w.location.href);\n if (target.protocol !== "http:" && target.protocol !== "https:") {\n throw new Error(`Refusing to navigate to a \'${target.protocol}\' URL.`);\n }\n w.setTimeout(() => w.location.assign(target.href), 0);\n },\n back: () => win().history.back(),\n forward: () => win().history.forward(),\n scrollTo: ({ x, y, handle }) => {\n const w = win();\n if (handle) {\n resolve(handle)?.scrollIntoView({ behavior: "smooth", block: "center" });\n return;\n }\n w.scrollTo({ left: x ?? w.scrollX, top: y ?? w.scrollY, behavior: "smooth" });\n },\n scrollBy: (dy) => win().scrollBy({ top: dy, behavior: "smooth" }),\n setField: (handle, value) => {\n before("setField");\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n if (isField(el)) {\n setNativeValue(el, value);\n return { ok: true };\n }\n return { ok: false, error: `Handle "${handle}" is not a form field` };\n },\n click: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n before("click");\n el.click();\n return { ok: true };\n },\n submit: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n const form = tag(el) === "FORM" ? el : el.closest("form");\n if (!form) return { ok: false, error: `Handle "${handle}" is not inside a form` };\n before("submit");\n if (typeof form.requestSubmit === "function") form.requestSubmit();\n else form.submit();\n return { ok: true };\n },\n rectFor: (handle) => {\n const el = resolve(handle);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, width: r.width, height: r.height };\n },\n resolve,\n call: (method, args = []) => {\n const a = Array.isArray(args) ? args : [];\n const handle = String(a[0] ?? "");\n switch (method) {\n case "getLocation":\n return agent.getLocation();\n case "describe":\n return agent.describe();\n case "read":\n return agent.read();\n case "visit":\n agent.visit(String(a[0] ?? ""));\n return void 0;\n case "back":\n agent.back();\n return void 0;\n case "forward":\n agent.forward();\n return void 0;\n case "scrollTo": {\n const o = a[0] && typeof a[0] === "object" ? a[0] : {};\n agent.scrollTo({\n x: num(o.x),\n y: num(o.y),\n handle: typeof o.handle === "string" ? o.handle : void 0\n });\n return void 0;\n }\n case "scrollBy":\n agent.scrollBy(Number(a[0] ?? 0));\n return void 0;\n case "setField":\n return agent.setField(handle, a[1]);\n case "click":\n return agent.click(handle);\n case "submit":\n return agent.submit(handle);\n case "rectFor":\n return agent.rectFor(handle);\n default:\n throw new Error(`Unknown dom-navigation method: ${String(method)}`);\n }\n }\n };\n return agent;\n }\n\n // src/dom-navigation/inject.ts\n var KEY = "__fancyDomNavigation";\n var PROTOCOL = 1;\n var g = globalThis;\n var existing = g[KEY];\n if (!existing || existing.protocol !== PROTOCOL) {\n const agent = createDomNavigationAgent();\n Object.defineProperty(globalThis, KEY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: Object.freeze({\n protocol: PROTOCOL,\n call: (method, args) => agent.call(method, args)\n })\n });\n }\n})();\n';
4
+ var virtual_dom_navigation_inject_default = '"use strict";\n(() => {\n // src/dom-navigation/agent.ts\n var INTERACTIVE = "a[href], button, input, select, textarea, [role=button], [role=link], [role=checkbox], [role=tab], [data-co-handle]";\n var tag = (el) => el.tagName.toUpperCase();\n function isVisible(el) {\n const he = el;\n if (el.type === "hidden") return false;\n if (he.hidden || he.getAttribute("aria-hidden") === "true") return false;\n return he.offsetParent !== null || he.getClientRects().length > 0;\n }\n function isSensitive(el) {\n const input = el;\n const ac = (el.getAttribute("autocomplete") ?? "").toLowerCase();\n return input.type === "password" || ac.startsWith("cc-") || ac === "one-time-code" || el.hasAttribute("data-private");\n }\n function insideSensitive(node) {\n for (let el = node.parentElement; el; el = el.parentElement) {\n if (isSensitive(el)) return true;\n }\n return false;\n }\n function safeText(root) {\n const doc = root.ownerDocument ?? root;\n const walker = doc.createTreeWalker(root, 4);\n let out = "";\n for (let n = walker.nextNode(); n; n = walker.nextNode()) {\n if (!insideSensitive(n)) out += n.nodeValue ?? "";\n }\n return out;\n }\n function roleOf(el) {\n const explicit = el.getAttribute("role");\n if (explicit) return explicit;\n const t = tag(el);\n if (t === "A") return "link";\n if (t === "BUTTON") return "button";\n if (t === "SELECT") return "select";\n if (t === "TEXTAREA") return "textbox";\n if (t === "INPUT") {\n const type = el.type;\n if (type === "checkbox") return "checkbox";\n if (type === "radio") return "radio";\n if (type === "submit" || type === "button") return "button";\n return "textbox";\n }\n return "generic";\n }\n function labelFor(doc, id) {\n for (const lbl of Array.from(doc.querySelectorAll("label[for]"))) {\n if (lbl.getAttribute("for") === id) return lbl;\n }\n return null;\n }\n function accessibleName(el) {\n const doc = el.ownerDocument;\n const aria = el.getAttribute("aria-label");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute("aria-labelledby");\n if (labelledby) {\n const txt = labelledby.split(/\\s+/).map((id2) => doc.getElementById(id2)?.textContent ?? "").join(" ").trim();\n if (txt) return txt;\n }\n const id = el.getAttribute("id");\n if (id) {\n const lbl = labelFor(doc, id);\n if (lbl?.textContent?.trim()) return lbl.textContent.trim();\n }\n const wrapping = el.closest("label");\n if (wrapping) {\n const txt = safeText(wrapping).trim();\n if (txt) return txt;\n }\n const placeholder = el.getAttribute("placeholder");\n if (placeholder) return placeholder.trim();\n const text = isSensitive(el) ? "" : el.textContent?.trim();\n if (text) return text.replace(/\\s+/g, " ").slice(0, 80);\n return el.getAttribute("name") ?? el.getAttribute("id") ?? roleOf(el);\n }\n function isDestructive(el, label) {\n if (el.hasAttribute("data-co-confirm")) return true;\n if (el.type === "submit") return true;\n if (tag(el) === "BUTTON" && el.type === "submit") return true;\n return /\\b(delete|remove|destroy|sign out|log ?out|cancel subscription|deactivate)\\b/i.test(label);\n }\n function attributeHandle(el) {\n return el.getAttribute("data-co-handle") || el.getAttribute("name") || el.getAttribute("id") || "";\n }\n function walk(doc = document) {\n const registry = /* @__PURE__ */ new Map();\n const actions = [];\n const seen = /* @__PURE__ */ new Set();\n let synthetic = 0;\n for (const el of Array.from(doc.querySelectorAll(INTERACTIVE))) {\n if (seen.has(el) || !isVisible(el)) continue;\n seen.add(el);\n const label = accessibleName(el);\n const role = roleOf(el);\n let handle = attributeHandle(el);\n if (!handle || registry.has(handle)) handle = `${role}#${synthetic++}`;\n registry.set(handle, el);\n let value;\n if (role === "textbox" || role === "select") {\n value = isSensitive(el) ? void 0 : el.value;\n } else if (role === "checkbox" || role === "radio") {\n value = el.checked;\n }\n const action = { handle, role, label, destructive: isDestructive(el, label) };\n if (value !== void 0) action.value = value;\n actions.push(action);\n }\n const loc = doc.defaultView?.location;\n return {\n snapshot: { url: loc ? loc.pathname + loc.search : "", title: doc.title, actions },\n registry\n };\n }\n function isField(el) {\n const t = tag(el);\n return t === "INPUT" || t === "TEXTAREA" || t === "SELECT";\n }\n function setNativeValue(el, value) {\n const view = el.ownerDocument.defaultView ?? globalThis;\n const fire = () => {\n el.dispatchEvent(new view.Event("input", { bubbles: true }));\n el.dispatchEvent(new view.Event("change", { bubbles: true }));\n };\n const t = tag(el);\n if (t === "INPUT" && (el.type === "checkbox" || el.type === "radio")) {\n const input = el;\n const want = Boolean(value);\n if (input.checked === want) return;\n if (input.type === "checkbox" || want) {\n input.click();\n return;\n }\n const setter2 = Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, "checked")?.set;\n setter2?.call(el, false);\n fire();\n return;\n }\n const proto = t === "TEXTAREA" ? view.HTMLTextAreaElement.prototype : t === "SELECT" ? view.HTMLSelectElement.prototype : view.HTMLInputElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;\n setter?.call(el, String(value));\n fire();\n }\n function readPage(doc = document) {\n const main = doc.querySelector("main") ?? doc.body;\n if (!main) return { title: doc.title, headings: [], text: "" };\n const headings = Array.from(main.querySelectorAll("h1, h2, h3")).map((h) => ({\n level: Number(h.tagName.slice(1)),\n text: safeText(h).trim()\n }));\n const text = safeText(main).replace(/\\s+/g, " ").trim().slice(0, 4e3);\n return { title: doc.title, headings, text };\n }\n function findByAttribute(doc, attr, value) {\n for (const el of Array.from(doc.querySelectorAll(`[${attr}]`))) {\n if (el.getAttribute(attr) === value) return el;\n }\n return null;\n }\n var num = (v) => typeof v === "number" ? v : void 0;\n function createDomNavigationAgent(options = {}) {\n let registry = /* @__PURE__ */ new Map();\n const doc = () => options.document ?? document;\n const win = () => doc().defaultView ?? window;\n const before = (action) => options.beforeAction?.(action);\n const resolve = (handle) => {\n const hit = registry.get(handle);\n if (hit && hit.isConnected) return hit;\n const d = doc();\n return findByAttribute(d, "data-co-handle", handle) ?? findByAttribute(d, "name", handle) ?? d.getElementById(handle);\n };\n const agent = {\n getLocation: () => {\n const loc = win().location;\n return { url: loc.pathname + loc.search, title: doc().title };\n },\n describe: () => {\n const res = walk(doc());\n registry = res.registry;\n return res.snapshot;\n },\n read: () => readPage(doc()),\n visit: (url) => {\n const w = win();\n const target = new URL(String(url), w.location.href);\n if (target.protocol !== "http:" && target.protocol !== "https:") {\n throw new Error(`Refusing to navigate to a \'${target.protocol}\' URL.`);\n }\n w.setTimeout(() => w.location.assign(target.href), 0);\n },\n back: () => win().history.back(),\n forward: () => win().history.forward(),\n scrollTo: ({ x, y, handle }) => {\n const w = win();\n if (handle) {\n resolve(handle)?.scrollIntoView({ behavior: "smooth", block: "center" });\n return;\n }\n w.scrollTo({ left: x ?? w.scrollX, top: y ?? w.scrollY, behavior: "smooth" });\n },\n scrollBy: (dy) => win().scrollBy({ top: dy, behavior: "smooth" }),\n setField: (handle, value) => {\n before("setField");\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n if (isField(el)) {\n setNativeValue(el, value);\n return { ok: true };\n }\n return { ok: false, error: `Handle "${handle}" is not a form field` };\n },\n click: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n before("click");\n el.click();\n return { ok: true };\n },\n submit: (handle) => {\n const el = resolve(handle);\n if (!el) return { ok: false, error: `No element for handle "${handle}"` };\n const form = tag(el) === "FORM" ? el : el.closest("form");\n if (!form) return { ok: false, error: `Handle "${handle}" is not inside a form` };\n before("submit");\n if (typeof form.requestSubmit === "function") form.requestSubmit();\n else form.submit();\n return { ok: true };\n },\n rectFor: (handle) => {\n const el = resolve(handle);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, width: r.width, height: r.height };\n },\n resolve,\n call: (method, args = []) => {\n const a = Array.isArray(args) ? args : [];\n const handle = String(a[0] ?? "");\n switch (method) {\n case "getLocation":\n return agent.getLocation();\n case "describe":\n return agent.describe();\n case "read":\n return agent.read();\n case "visit":\n agent.visit(String(a[0] ?? ""));\n return void 0;\n case "back":\n agent.back();\n return void 0;\n case "forward":\n agent.forward();\n return void 0;\n case "scrollTo": {\n const o = a[0] && typeof a[0] === "object" ? a[0] : {};\n agent.scrollTo({\n x: num(o.x),\n y: num(o.y),\n handle: typeof o.handle === "string" ? o.handle : void 0\n });\n return void 0;\n }\n case "scrollBy":\n agent.scrollBy(Number(a[0] ?? 0));\n return void 0;\n case "setField":\n return agent.setField(handle, a[1]);\n case "click":\n return agent.click(handle);\n case "submit":\n return agent.submit(handle);\n case "rectFor":\n return agent.rectFor(handle);\n default:\n throw new Error(`Unknown dom-navigation method: ${String(method)}`);\n }\n }\n };\n return agent;\n }\n\n // src/dom-navigation/inject.ts\n var KEY = "__fancyDomNavigation";\n var PROTOCOL = 1;\n var g = globalThis;\n var existing = g[KEY];\n if (!existing || existing.protocol !== PROTOCOL) {\n const agent = createDomNavigationAgent();\n Object.defineProperty(globalThis, KEY, {\n configurable: true,\n enumerable: false,\n writable: false,\n value: Object.freeze({\n protocol: PROTOCOL,\n call: (method, args) => agent.call(method, args)\n })\n });\n }\n})();\n';
5
5
 
6
6
  // src/dom-navigation/script.ts
7
7
  var DOM_NAVIGATION_SCRIPT = virtual_dom_navigation_inject_default;