@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
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
2
- import { B as Bridge } from '../types-EcElKOlk.cjs';
3
- import '../types-C2DtnHIn.cjs';
1
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
2
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
3
+ import '../types-gzIpsUDE.cjs';
4
4
 
5
5
  /**
6
6
  * Bridge for `@particle-academy/fancy-passkeys-ui` — the **management** half of
@@ -150,7 +150,7 @@ type PasskeyBridgeOptions = {
150
150
  * Also route `passkey_rename` through {@link confirm}. Off by default: a
151
151
  * rename is cosmetic, reversible and pushed onto the undo stack. Worth turning
152
152
  * on where the label itself carries trust — a rogue credential relabelled
153
- * "Glenn's iPhone" survives a human's audit of the list.
153
+ * "Avery's iPhone" survives a human's audit of the list.
154
154
  */
155
155
  confirmRename?: boolean;
156
156
  /**
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
2
- import { B as Bridge } from '../types-BxNRKSVd.js';
3
- import '../types-C2DtnHIn.js';
1
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
2
+ import { B as Bridge } from '../types-VX_9je7w.js';
3
+ import '../types-gzIpsUDE.js';
4
4
 
5
5
  /**
6
6
  * Bridge for `@particle-academy/fancy-passkeys-ui` — the **management** half of
@@ -150,7 +150,7 @@ type PasskeyBridgeOptions = {
150
150
  * Also route `passkey_rename` through {@link confirm}. Off by default: a
151
151
  * rename is cosmetic, reversible and pushed onto the undo stack. Worth turning
152
152
  * on where the label itself carries trust — a rogue credential relabelled
153
- * "Glenn's iPhone" survives a human's audit of the list.
153
+ * "Avery's iPhone" survives a human's audit of the list.
154
154
  */
155
155
  confirmRename?: boolean;
156
156
  /**
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
2
- import { B as Bridge } from '../types-EcElKOlk.cjs';
3
- import '../types-C2DtnHIn.cjs';
1
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
2
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
3
+ import '../types-gzIpsUDE.cjs';
4
4
 
5
5
  /**
6
6
  * Loose Scene types — mirror the public surface of fancy-3d's Scene
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
2
- import { B as Bridge } from '../types-BxNRKSVd.js';
3
- import '../types-C2DtnHIn.js';
1
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
2
+ import { B as Bridge } from '../types-VX_9je7w.js';
3
+ import '../types-gzIpsUDE.js';
4
4
 
5
5
  /**
6
6
  * Loose Scene types — mirror the public surface of fancy-3d's Scene
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
2
- import { B as Bridge } from '../types-EcElKOlk.cjs';
3
- import '../types-C2DtnHIn.cjs';
1
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
2
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
3
+ import '../types-gzIpsUDE.cjs';
4
4
 
5
5
  /**
6
6
  * Loose snapshot of a screen — what's in fancy-screens' ScreenMeta plus
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
2
- import { B as Bridge } from '../types-BxNRKSVd.js';
3
- import '../types-C2DtnHIn.js';
1
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
2
+ import { B as Bridge } from '../types-VX_9je7w.js';
3
+ import '../types-gzIpsUDE.js';
4
4
 
5
5
  /**
6
6
  * Loose snapshot of a screen — what's in fancy-screens' ScreenMeta plus
@@ -1,7 +1,7 @@
1
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
2
- import { B as Bridge } from '../types-EcElKOlk.cjs';
1
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
2
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
3
3
  import { WorkbookData } from '@particle-academy/fancy-sheets';
4
- import '../types-C2DtnHIn.cjs';
4
+ import '../types-gzIpsUDE.cjs';
5
5
 
6
6
  /**
7
7
  * Cell writes funnel through fancy-sheets' own `reduceWorkbook` + `SheetOp` —
@@ -1,7 +1,7 @@
1
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
2
- import { B as Bridge } from '../types-BxNRKSVd.js';
1
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
2
+ import { B as Bridge } from '../types-VX_9je7w.js';
3
3
  import { WorkbookData } from '@particle-academy/fancy-sheets';
4
- import '../types-C2DtnHIn.js';
4
+ import '../types-gzIpsUDE.js';
5
5
 
6
6
  /**
7
7
  * Cell writes funnel through fancy-sheets' own `reduceWorkbook` + `SheetOp` —
@@ -1,7 +1,7 @@
1
1
  import { Deck, DeckOp } from '@particle-academy/fancy-slides';
2
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
3
- import { B as Bridge } from '../types-EcElKOlk.cjs';
4
- import '../types-C2DtnHIn.cjs';
2
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
3
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
4
+ import '../types-gzIpsUDE.cjs';
5
5
 
6
6
  /**
7
7
  * Adapter — the bridge calls into these to read or change deck state. The
@@ -1,7 +1,7 @@
1
1
  import { Deck, DeckOp } from '@particle-academy/fancy-slides';
2
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
3
- import { B as Bridge } from '../types-BxNRKSVd.js';
4
- import '../types-C2DtnHIn.js';
2
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
3
+ import { B as Bridge } from '../types-VX_9je7w.js';
4
+ import '../types-gzIpsUDE.js';
5
5
 
6
6
  /**
7
7
  * Adapter — the bridge calls into these to read or change deck state. The
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
2
- import { B as Bridge } from '../types-EcElKOlk.cjs';
3
- import '../types-C2DtnHIn.cjs';
1
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
2
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
3
+ import '../types-gzIpsUDE.cjs';
4
4
 
5
5
  /**
6
6
  * A shell/profile an agent can switch a terminal to. Mirrors fancy-term's
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
2
- import { B as Bridge } from '../types-BxNRKSVd.js';
3
- import '../types-C2DtnHIn.js';
1
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
2
+ import { B as Bridge } from '../types-VX_9je7w.js';
3
+ import '../types-gzIpsUDE.js';
4
4
 
5
5
  /**
6
6
  * A shell/profile an agent can switch a terminal to. Mirrors fancy-term's
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
1
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
2
2
  import { HumanPlusEventStore, HumanPlusActor } from '../human-plus/index.cjs';
3
- import '../types-C2DtnHIn.cjs';
3
+ import '../types-gzIpsUDE.cjs';
4
4
 
5
5
  type TuiActionPolicy = "observe" | "execute" | "propose" | "confirm" | "human-only";
6
6
  interface TuiSurfaceCommandLike {
@@ -1,6 +1,6 @@
1
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
1
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
2
2
  import { HumanPlusEventStore, HumanPlusActor } from '../human-plus/index.js';
3
- import '../types-C2DtnHIn.js';
3
+ import '../types-gzIpsUDE.js';
4
4
 
5
5
  type TuiActionPolicy = "observe" | "execute" | "propose" | "confirm" | "human-only";
6
6
  interface TuiSurfaceCommandLike {
@@ -1,7 +1,7 @@
1
1
  import { StickyNoteItem, ShapeItem, ConnectorItem, Stroke, Viewport, RemoteCursor } from '@particle-academy/fancy-whiteboard';
2
- import { T as ToolHost } from '../tool-host-LVBgCN5m.cjs';
3
- import { B as Bridge } from '../types-EcElKOlk.cjs';
4
- import '../types-C2DtnHIn.cjs';
2
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
3
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
4
+ import '../types-gzIpsUDE.cjs';
5
5
 
6
6
  /**
7
7
  * State accessors / mutators the bridge needs from the host. The host owns
@@ -1,7 +1,7 @@
1
1
  import { StickyNoteItem, ShapeItem, ConnectorItem, Stroke, Viewport, RemoteCursor } from '@particle-academy/fancy-whiteboard';
2
- import { T as ToolHost } from '../tool-host-Wo0pkY83.js';
3
- import { B as Bridge } from '../types-BxNRKSVd.js';
4
- import '../types-C2DtnHIn.js';
2
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
3
+ import { B as Bridge } from '../types-VX_9je7w.js';
4
+ import '../types-gzIpsUDE.js';
5
5
 
6
6
  /**
7
7
  * State accessors / mutators the bridge needs from the host. The host owns
@@ -0,0 +1,391 @@
1
+ 'use strict';
2
+
3
+ var fancyAutoCommon = require('@particle-academy/fancy-auto-common');
4
+
5
+ // src/mcp/server.ts
6
+ function textResult(text, structured) {
7
+ return {
8
+ content: [{ type: "text", text }],
9
+ ...structured !== void 0 ? { structuredContent: structured } : {}
10
+ };
11
+ }
12
+ function errorResult(text) {
13
+ return { content: [{ type: "text", text }], isError: true };
14
+ }
15
+
16
+ // src/presence/wrap-tool-with-activity.ts
17
+ function wrapToolWithActivity(handler, options) {
18
+ return async (args) => {
19
+ const result = await handler(args);
20
+ if (result.isError) return result;
21
+ let target;
22
+ if (options.resolveTarget) {
23
+ target = options.resolveTarget({ toolName: options.toolName, args, result });
24
+ } else {
25
+ target = { kind: options.kind, screenId: options.screenId };
26
+ }
27
+ if (!target) return result;
28
+ fancyAutoCommon.emitActivity({
29
+ agentId: options.agent.id,
30
+ agentName: options.agent.name,
31
+ agentColor: options.agent.color,
32
+ target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },
33
+ action: options.toolName,
34
+ timestamp: Date.now(),
35
+ meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),
36
+ ttlMs: options.ttlMs
37
+ });
38
+ return result;
39
+ };
40
+ }
41
+ function extractMeta(result) {
42
+ const sc = result.structuredContent;
43
+ if (sc && typeof sc === "object" && !Array.isArray(sc)) {
44
+ return sc;
45
+ }
46
+ return void 0;
47
+ }
48
+
49
+ // src/undo/undo-tools.ts
50
+ var installedHosts = /* @__PURE__ */ new WeakSet();
51
+ function ensureUndoToolsRegistered(host, options = {}) {
52
+ if (installedHosts.has(host)) return;
53
+ installedHosts.add(host);
54
+ registerUndoTools(host, options);
55
+ }
56
+ function registerUndoTools(host, options = {}) {
57
+ const defaultAgent = options.defaultAgentId ?? "agent";
58
+ const disposers = [];
59
+ const agentOf = (args) => typeof args?.agentId === "string" ? args.agentId : defaultAgent;
60
+ disposers.push(
61
+ host.registerTool(
62
+ {
63
+ name: "agent_undo",
64
+ description: "Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: { agentId: { type: "string" } },
68
+ additionalProperties: false
69
+ }
70
+ },
71
+ async (args) => {
72
+ const entry = await fancyAutoCommon.undoOne(agentOf(args));
73
+ if (!entry) return errorResult("Nothing to undo.");
74
+ return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });
75
+ }
76
+ )
77
+ );
78
+ disposers.push(
79
+ host.registerTool(
80
+ {
81
+ name: "agent_redo",
82
+ description: "Redo the most recently undone action.",
83
+ inputSchema: {
84
+ type: "object",
85
+ properties: { agentId: { type: "string" } },
86
+ additionalProperties: false
87
+ }
88
+ },
89
+ async (args) => {
90
+ const entry = await fancyAutoCommon.redoOne(agentOf(args));
91
+ if (!entry) return errorResult("Nothing to redo.");
92
+ return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });
93
+ }
94
+ )
95
+ );
96
+ disposers.push(
97
+ host.registerTool(
98
+ {
99
+ name: "agent_history",
100
+ description: "List the agent's undo stack (oldest first). Useful for understanding what's reversible.",
101
+ inputSchema: {
102
+ type: "object",
103
+ properties: { agentId: { type: "string" } },
104
+ additionalProperties: false
105
+ }
106
+ },
107
+ async (args) => {
108
+ const history = fancyAutoCommon.readHistory(agentOf(args)).map(serialize);
109
+ const text = history.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join("\n");
110
+ return textResult(text || "(empty)", history);
111
+ }
112
+ )
113
+ );
114
+ return () => disposers.forEach((d) => d());
115
+ }
116
+ function serialize(entry) {
117
+ return {
118
+ timestamp: entry.timestamp,
119
+ bridgeId: entry.bridgeId,
120
+ action: entry.action,
121
+ label: entry.label
122
+ };
123
+ }
124
+
125
+ // src/bridges/navigation.ts
126
+ var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
127
+ function registerNavigationBridge(host, options) {
128
+ const { adapter } = options;
129
+ const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
130
+ const pendingMode = options.pendingMode ?? true;
131
+ const disposers = [];
132
+ let groundedRevision = null;
133
+ const staleIntent = async () => {
134
+ const current = await adapter.getRevision?.();
135
+ if (current === void 0) return null;
136
+ if (groundedRevision === null || groundedRevision !== current) {
137
+ return errorResult(
138
+ `Human took control at page revision ${current}. This agent action was cancelled; call page_describe before acting again.`
139
+ );
140
+ }
141
+ return null;
142
+ };
143
+ ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
144
+ const target = (label, elementId) => ({
145
+ kind: "navigation",
146
+ screenId: adapter.screenId,
147
+ elementId,
148
+ label
149
+ });
150
+ const reg = (name, description, properties, required, handler, activity) => {
151
+ const wrapped = async (args) => {
152
+ try {
153
+ return await handler(args);
154
+ } catch (e) {
155
+ return errorResult(e instanceof Error ? e.message : String(e));
156
+ }
157
+ };
158
+ const final = activity ? wrapToolWithActivity(wrapped, {
159
+ toolName: name,
160
+ agent: { id: agent.id, name: agent.name, color: agent.color },
161
+ kind: "navigation",
162
+ screenId: adapter.screenId,
163
+ resolveTarget: ({ args }) => activity(args)
164
+ }) : wrapped;
165
+ disposers.push(
166
+ host.registerTool(
167
+ {
168
+ name,
169
+ description,
170
+ inputSchema: { type: "object", properties, required, additionalProperties: false }
171
+ },
172
+ final
173
+ )
174
+ );
175
+ };
176
+ reg(
177
+ "page_describe",
178
+ "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.",
179
+ {},
180
+ [],
181
+ async () => {
182
+ const snap = await adapter.describe();
183
+ const revision = await adapter.getRevision?.();
184
+ if (revision !== void 0) {
185
+ snap.revision = revision;
186
+ groundedRevision = revision;
187
+ }
188
+ const text = [
189
+ `URL: ${snap.url}`,
190
+ `Title: ${snap.title}`,
191
+ "",
192
+ ...snap.actions.map((a) => `[${a.handle}] ${a.role}: ${a.label}${a.destructive ? " (destructive)" : ""}`)
193
+ ].join("\n");
194
+ return textResult(text, snap);
195
+ },
196
+ () => target("Inspect page")
197
+ );
198
+ reg(
199
+ "page_read",
200
+ "Read the page's visible text / heading outline for grounding.",
201
+ {},
202
+ [],
203
+ async () => textResult(adapter.read ? await adapter.read() : "(host did not provide page text)"),
204
+ () => target("Read page")
205
+ );
206
+ reg(
207
+ "page_focus",
208
+ "Move the agent's visible focus to an interactive element by stable handle without activating it.",
209
+ { handle: { type: "string" } },
210
+ ["handle"],
211
+ async (args) => {
212
+ const stale = await staleIntent();
213
+ if (stale) return stale;
214
+ const handle = String(args.handle ?? "");
215
+ const action = (await adapter.describe()).actions.find((candidate) => candidate.handle === handle);
216
+ if (!action) return errorResult(`No element for handle "${handle}"`);
217
+ return textResult(`Focused ${handle}`, {
218
+ handle,
219
+ rect: await adapter.rectFor?.(handle) ?? void 0
220
+ });
221
+ },
222
+ (args) => target(`Focus ${String(args.handle ?? "")}`, String(args.handle ?? ""))
223
+ );
224
+ reg(
225
+ "nav_visit",
226
+ "Navigate to a URL (same-site path or absolute). The human watches the page change.",
227
+ { url: { type: "string", description: "Path like /packages or an absolute URL." } },
228
+ ["url"],
229
+ async (args) => {
230
+ const stale = await staleIntent();
231
+ if (stale) return stale;
232
+ const url = String(args.url ?? "");
233
+ if (!url) return errorResult("url is required");
234
+ const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase();
235
+ if (scheme && scheme !== "http" && scheme !== "https") {
236
+ return errorResult(
237
+ `Refusing to navigate to a '${scheme}:' URL. Only http(s) URLs or same-site paths are allowed.`
238
+ );
239
+ }
240
+ const from = (await adapter.getLocation()).url;
241
+ await adapter.visit(url);
242
+ fancyAutoCommon.pushUndoEntry(agent.id, {
243
+ timestamp: Date.now(),
244
+ bridgeId: "navigation",
245
+ action: "nav_visit",
246
+ label: `Navigate to ${url}`,
247
+ undo: () => {
248
+ adapter.visit(from);
249
+ },
250
+ redo: () => {
251
+ adapter.visit(url);
252
+ }
253
+ });
254
+ return textResult(`Navigated to ${url}`, { url });
255
+ },
256
+ (args) => target(`Navigate \u2192 ${String(args.url ?? "")}`)
257
+ );
258
+ reg(
259
+ "nav_back",
260
+ "Go back to the previous page.",
261
+ {},
262
+ [],
263
+ async () => {
264
+ const stale = await staleIntent();
265
+ if (stale) return stale;
266
+ if (!adapter.back) return errorResult("Host did not provide back navigation.");
267
+ await adapter.back();
268
+ return textResult("Went back");
269
+ },
270
+ () => target("Back")
271
+ );
272
+ reg(
273
+ "nav_forward",
274
+ "Go forward to the next page.",
275
+ {},
276
+ [],
277
+ async () => {
278
+ const stale = await staleIntent();
279
+ if (stale) return stale;
280
+ if (!adapter.forward) return errorResult("Host did not provide forward navigation.");
281
+ await adapter.forward();
282
+ return textResult("Went forward");
283
+ },
284
+ () => target("Forward")
285
+ );
286
+ reg(
287
+ "nav_scroll_to",
288
+ "Scroll the page to absolute coordinates, or to a specific element by its handle.",
289
+ {
290
+ handle: { type: "string", description: "Scroll this element into view." },
291
+ x: { type: "number" },
292
+ y: { type: "number" }
293
+ },
294
+ [],
295
+ async (args) => {
296
+ const stale = await staleIntent();
297
+ if (stale) return stale;
298
+ const handle = typeof args.handle === "string" ? args.handle : void 0;
299
+ await adapter.scrollTo({
300
+ handle,
301
+ x: typeof args.x === "number" ? args.x : void 0,
302
+ y: typeof args.y === "number" ? args.y : void 0
303
+ });
304
+ return textResult("Scrolled", handle ? { handle, rect: await adapter.rectFor?.(handle) ?? void 0 } : void 0);
305
+ },
306
+ () => target("Scroll")
307
+ );
308
+ reg(
309
+ "nav_scroll_by",
310
+ "Scroll the page by a vertical delta in pixels (negative scrolls up).",
311
+ { dy: { type: "number" } },
312
+ ["dy"],
313
+ async (args) => {
314
+ const stale = await staleIntent();
315
+ if (stale) return stale;
316
+ await adapter.scrollBy(Number(args.dy ?? 0));
317
+ return textResult(`Scrolled by ${Number(args.dy ?? 0)}px`);
318
+ },
319
+ () => target("Scroll")
320
+ );
321
+ reg(
322
+ "page_set_field",
323
+ "Set a form field's value by handle. The host updates the controlled input and the human sees it change.",
324
+ {
325
+ handle: { type: "string" },
326
+ value: { description: "Value to set; type matches the field." }
327
+ },
328
+ ["handle", "value"],
329
+ async (args) => {
330
+ const stale = await staleIntent();
331
+ if (stale) return stale;
332
+ const handle = String(args.handle ?? "");
333
+ const res = await adapter.setField(handle, args.value);
334
+ if (!res.ok) return errorResult(res.error ?? `Could not set ${handle}`);
335
+ return textResult(`${handle} \u2190 ${JSON.stringify(args.value)}`, { handle, value: args.value, rect: await adapter.rectFor?.(handle) ?? void 0 });
336
+ },
337
+ (args) => target(`Set ${String(args.handle ?? "")}`, String(args.handle ?? ""))
338
+ );
339
+ reg(
340
+ "page_click",
341
+ "Activate an element by handle (link, button, checkbox\u2026). Destructive elements are staged for the human to confirm.",
342
+ { handle: { type: "string" } },
343
+ ["handle"],
344
+ async (args) => {
345
+ const stale = await staleIntent();
346
+ if (stale) return stale;
347
+ const handle = String(args.handle ?? "");
348
+ const action = (await adapter.describe()).actions.find((a) => a.handle === handle);
349
+ const rect = await adapter.rectFor?.(handle) ?? void 0;
350
+ if (pendingMode && action?.destructive && adapter.confirm) {
351
+ const ok = await adapter.confirm({ action: "click", handle, label: action.label });
352
+ if (!ok) return errorResult("Declined by user");
353
+ }
354
+ const res = await adapter.click(handle);
355
+ if (!res.ok) return errorResult(res.error ?? `Could not click ${handle}`);
356
+ return textResult(`Clicked ${handle}`, { handle, rect });
357
+ },
358
+ (args) => target(`Click ${String(args.handle ?? "")}`, String(args.handle ?? ""))
359
+ );
360
+ reg(
361
+ "page_submit",
362
+ "Submit a form by handle. Always staged for the human to confirm when pendingMode is on.",
363
+ { handle: { type: "string" } },
364
+ ["handle"],
365
+ async (args) => {
366
+ const stale = await staleIntent();
367
+ if (stale) return stale;
368
+ const handle = String(args.handle ?? "");
369
+ const rect = await adapter.rectFor?.(handle) ?? void 0;
370
+ if (pendingMode && adapter.confirm) {
371
+ const ok = await adapter.confirm({ action: "submit", handle, label: handle });
372
+ if (!ok) return errorResult("Declined by user");
373
+ }
374
+ const res = await adapter.submit(handle);
375
+ if (!res.ok) return errorResult(res.error ?? "Submit failed");
376
+ return textResult(`Submitted ${handle}`, { handle, rect });
377
+ },
378
+ (args) => target(`Submit ${String(args.handle ?? "")}`, String(args.handle ?? ""))
379
+ );
380
+ return {
381
+ id: "navigation",
382
+ title: "Co-browsing",
383
+ dispose: () => {
384
+ for (const d of disposers) d();
385
+ }
386
+ };
387
+ }
388
+
389
+ exports.registerNavigationBridge = registerNavigationBridge;
390
+ //# sourceMappingURL=bridges-navigation.cjs.map
391
+ //# sourceMappingURL=bridges-navigation.cjs.map