@particle-academy/agent-integrations 0.46.0 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +885 -0
- package/LICENSE +21 -0
- package/README.md +53 -3
- package/dist/bridges/artboard.d.cts +3 -3
- package/dist/bridges/artboard.d.ts +3 -3
- package/dist/bridges/catalog.d.cts +3 -3
- package/dist/bridges/catalog.d.ts +3 -3
- package/dist/bridges/charts.d.cts +3 -3
- package/dist/bridges/charts.d.ts +3 -3
- package/dist/bridges/cms.d.cts +3 -3
- package/dist/bridges/cms.d.ts +3 -3
- package/dist/bridges/code.d.cts +3 -3
- package/dist/bridges/code.d.ts +3 -3
- package/dist/bridges/doc.d.cts +3 -3
- package/dist/bridges/doc.d.ts +3 -3
- package/dist/bridges/features.d.cts +3 -3
- package/dist/bridges/features.d.ts +3 -3
- package/dist/bridges/files.d.cts +3 -3
- package/dist/bridges/files.d.ts +3 -3
- package/dist/bridges/flow.d.cts +3 -3
- package/dist/bridges/flow.d.ts +3 -3
- package/dist/bridges/forms.d.cts +3 -3
- package/dist/bridges/forms.d.ts +3 -3
- package/dist/bridges/git.d.cts +3 -3
- package/dist/bridges/git.d.ts +3 -3
- package/dist/bridges/grid.d.cts +3 -3
- package/dist/bridges/grid.d.ts +3 -3
- package/dist/bridges/map.d.cts +3 -3
- package/dist/bridges/map.d.ts +3 -3
- package/dist/bridges/navigation.d.cts +140 -0
- package/dist/bridges/navigation.d.ts +140 -0
- package/dist/bridges/passkeys.d.cts +4 -4
- package/dist/bridges/passkeys.d.ts +4 -4
- package/dist/bridges/scene.d.cts +3 -3
- package/dist/bridges/scene.d.ts +3 -3
- package/dist/bridges/screens.d.cts +3 -3
- package/dist/bridges/screens.d.ts +3 -3
- package/dist/bridges/sheets.d.cts +3 -3
- package/dist/bridges/sheets.d.ts +3 -3
- package/dist/bridges/slides.d.cts +3 -3
- package/dist/bridges/slides.d.ts +3 -3
- package/dist/bridges/terminal.d.cts +3 -3
- package/dist/bridges/terminal.d.ts +3 -3
- package/dist/bridges/tui.d.cts +2 -2
- package/dist/bridges/tui.d.ts +2 -2
- package/dist/bridges/whiteboard.d.cts +3 -3
- package/dist/bridges/whiteboard.d.ts +3 -3
- package/dist/bridges-navigation.cjs +437 -0
- package/dist/bridges-navigation.cjs.map +1 -0
- package/dist/bridges-navigation.js +7 -0
- package/dist/bridges-navigation.js.map +1 -0
- package/dist/bridges-passkeys.cjs.map +1 -1
- package/dist/bridges-passkeys.js +1 -1
- package/dist/{chunk-PWYEW6NL.js → chunk-G6CRHFWU.js} +2 -2
- package/dist/{chunk-PWYEW6NL.js.map → chunk-G6CRHFWU.js.map} +1 -1
- package/dist/chunk-OHNGURUJ.js +317 -0
- package/dist/chunk-OHNGURUJ.js.map +1 -0
- package/dist/dom-navigation/index.d.cts +175 -0
- package/dist/dom-navigation/index.d.ts +175 -0
- package/dist/dom-navigation/script.d.cts +3 -0
- package/dist/dom-navigation/script.d.ts +3 -0
- package/dist/dom-navigation-script.cjs +11 -0
- package/dist/dom-navigation-script.cjs.map +1 -0
- package/dist/dom-navigation-script.js +9 -0
- package/dist/dom-navigation-script.js.map +1 -0
- package/dist/dom-navigation.cjs +405 -0
- package/dist/dom-navigation.cjs.map +1 -0
- package/dist/dom-navigation.js +388 -0
- package/dist/dom-navigation.js.map +1 -0
- package/dist/index.cjs +94 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -125
- package/dist/index.d.ts +10 -125
- package/dist/index.js +12 -275
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.cts +5 -5
- package/dist/mcp/index.d.ts +5 -5
- package/dist/mcp/stdio.d.cts +3 -3
- package/dist/mcp/stdio.d.ts +3 -3
- package/dist/presence/index.d.cts +1 -1
- package/dist/presence/index.d.ts +1 -1
- package/dist/{server-BIcA3ZtK.d.cts → server-1y0n61R5.d.cts} +2 -2
- package/dist/{server-DnUyeavY.d.ts → server-vq6qVl3T.d.ts} +2 -2
- package/dist/sharing/index.d.cts +3 -3
- package/dist/sharing/index.d.ts +3 -3
- package/dist/sheets-adapter.d.cts +3 -3
- package/dist/sheets-adapter.d.ts +3 -3
- package/dist/{tool-host-LVBgCN5m.d.cts → tool-host-D67N2a2h.d.cts} +1 -1
- package/dist/{tool-host-Wo0pkY83.d.ts → tool-host-LYfdPE8u.d.ts} +1 -1
- package/dist/{types-EcElKOlk.d.cts → types-DP5yQZQU.d.cts} +1 -1
- package/dist/{types-BxNRKSVd.d.ts → types-VX_9je7w.d.ts} +1 -1
- package/dist/{types-C2DtnHIn.d.cts → types-gzIpsUDE.d.cts} +1 -1
- package/dist/{types-C2DtnHIn.d.ts → types-gzIpsUDE.d.ts} +1 -1
- package/dist/undo/index.d.cts +2 -2
- package/dist/undo/index.d.ts +2 -2
- package/package.json +35 -3
|
@@ -0,0 +1,437 @@
|
|
|
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
|
+
var isControl = (code) => code < 32 || code >= 127 && code <= 159 || code === 8232 || code === 8233;
|
|
128
|
+
function oneLine(value, max) {
|
|
129
|
+
const raw = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
130
|
+
const bounded = raw.length > max * 4 ? raw.slice(0, max * 4) : raw;
|
|
131
|
+
let out = "";
|
|
132
|
+
for (const c of bounded) out += isControl(c.charCodeAt(0)) ? " " : c;
|
|
133
|
+
out = out.replace(/\s+/g, " ").trim();
|
|
134
|
+
return out.length > max ? out.slice(0, max - 3) + "..." : out;
|
|
135
|
+
}
|
|
136
|
+
var SAFE_HANDLE = /^[A-Za-z0-9_.:#@/-]{1,80}$/;
|
|
137
|
+
var CAP = { url: 500, title: 200, role: 40, label: 200, heading: 200, text: 4e3, error: 300, headings: 50 };
|
|
138
|
+
function formatReadout(r) {
|
|
139
|
+
if (typeof r === "string") return oneLine(r, CAP.text);
|
|
140
|
+
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)}`);
|
|
141
|
+
return [oneLine(r.title, CAP.title), ...headings, "", oneLine(r.text, CAP.text)].join("\n");
|
|
142
|
+
}
|
|
143
|
+
function registerNavigationBridge(host, options) {
|
|
144
|
+
const { adapter } = options;
|
|
145
|
+
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
146
|
+
const pendingMode = options.pendingMode ?? true;
|
|
147
|
+
const disposers = [];
|
|
148
|
+
let groundedRevision = null;
|
|
149
|
+
const staleIntent = async () => {
|
|
150
|
+
const current = await adapter.getRevision?.();
|
|
151
|
+
if (current === void 0) return null;
|
|
152
|
+
if (groundedRevision === null || groundedRevision !== current) {
|
|
153
|
+
return errorResult(
|
|
154
|
+
`Human took control at page revision ${current}. This agent action was cancelled; call page_describe before acting again.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
};
|
|
159
|
+
let aliases = /* @__PURE__ */ new Map();
|
|
160
|
+
const pageHandle = (handle) => aliases.get(handle) ?? handle;
|
|
161
|
+
const failure = (message, fallback) => errorResult(oneLine(message ?? fallback, CAP.error) || fallback);
|
|
162
|
+
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
163
|
+
const target = (label, elementId) => ({
|
|
164
|
+
kind: "navigation",
|
|
165
|
+
screenId: adapter.screenId,
|
|
166
|
+
elementId,
|
|
167
|
+
label
|
|
168
|
+
});
|
|
169
|
+
const reg = (name, description, properties, required, handler, activity) => {
|
|
170
|
+
const wrapped = async (args) => {
|
|
171
|
+
try {
|
|
172
|
+
return await handler(args);
|
|
173
|
+
} catch (e) {
|
|
174
|
+
return failure(e instanceof Error ? e.message : String(e), `${name} failed`);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const final = activity ? wrapToolWithActivity(wrapped, {
|
|
178
|
+
toolName: name,
|
|
179
|
+
agent: { id: agent.id, name: agent.name, color: agent.color },
|
|
180
|
+
kind: "navigation",
|
|
181
|
+
screenId: adapter.screenId,
|
|
182
|
+
resolveTarget: ({ args }) => activity(args)
|
|
183
|
+
}) : wrapped;
|
|
184
|
+
disposers.push(
|
|
185
|
+
host.registerTool(
|
|
186
|
+
{
|
|
187
|
+
name,
|
|
188
|
+
description,
|
|
189
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
190
|
+
},
|
|
191
|
+
final
|
|
192
|
+
)
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
reg(
|
|
196
|
+
"page_describe",
|
|
197
|
+
"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.",
|
|
198
|
+
{},
|
|
199
|
+
[],
|
|
200
|
+
async () => {
|
|
201
|
+
const raw = await adapter.describe();
|
|
202
|
+
const next = /* @__PURE__ */ new Map();
|
|
203
|
+
const actions = (Array.isArray(raw.actions) ? raw.actions : []).map((a) => {
|
|
204
|
+
const handle = String(a.handle ?? "");
|
|
205
|
+
if (SAFE_HANDLE.test(handle)) return a;
|
|
206
|
+
const alias = `~${next.size}`;
|
|
207
|
+
next.set(alias, handle);
|
|
208
|
+
return { ...a, handle: alias };
|
|
209
|
+
});
|
|
210
|
+
aliases = next;
|
|
211
|
+
const snap = { ...raw, actions };
|
|
212
|
+
const revision = await adapter.getRevision?.();
|
|
213
|
+
if (revision !== void 0) {
|
|
214
|
+
snap.revision = revision;
|
|
215
|
+
groundedRevision = revision;
|
|
216
|
+
}
|
|
217
|
+
const text = [
|
|
218
|
+
`URL: ${oneLine(snap.url, CAP.url)}`,
|
|
219
|
+
`Title: ${oneLine(snap.title, CAP.title)}`,
|
|
220
|
+
"",
|
|
221
|
+
...actions.map(
|
|
222
|
+
(a) => `[${a.handle}] ${oneLine(a.role, CAP.role)}: ${oneLine(a.label, CAP.label)}${a.destructive ? " (destructive)" : ""}`
|
|
223
|
+
)
|
|
224
|
+
].join("\n");
|
|
225
|
+
return textResult(text, snap);
|
|
226
|
+
},
|
|
227
|
+
() => target("Inspect page")
|
|
228
|
+
);
|
|
229
|
+
reg(
|
|
230
|
+
"page_read",
|
|
231
|
+
"Read the page's visible text / heading outline for grounding.",
|
|
232
|
+
{},
|
|
233
|
+
[],
|
|
234
|
+
async () => {
|
|
235
|
+
const r = adapter.read ? await adapter.read() : void 0;
|
|
236
|
+
return textResult(r == null ? "(host did not provide page text)" : formatReadout(r));
|
|
237
|
+
},
|
|
238
|
+
() => target("Read page")
|
|
239
|
+
);
|
|
240
|
+
reg(
|
|
241
|
+
"page_focus",
|
|
242
|
+
"Move the agent's visible focus to an interactive element by stable handle without activating it.",
|
|
243
|
+
{ handle: { type: "string" } },
|
|
244
|
+
["handle"],
|
|
245
|
+
async (args) => {
|
|
246
|
+
const stale = await staleIntent();
|
|
247
|
+
if (stale) return stale;
|
|
248
|
+
const shown = String(args.handle ?? "");
|
|
249
|
+
const handle = pageHandle(shown);
|
|
250
|
+
const action = (await adapter.describe()).actions.find((candidate) => candidate.handle === handle);
|
|
251
|
+
if (!action) return failure(`No element for handle "${shown}"`, "No element for that handle");
|
|
252
|
+
return textResult(`Focused ${oneLine(shown, CAP.label)}`, {
|
|
253
|
+
handle: shown,
|
|
254
|
+
rect: await adapter.rectFor?.(handle) ?? void 0
|
|
255
|
+
});
|
|
256
|
+
},
|
|
257
|
+
(args) => target(`Focus ${String(args.handle ?? "")}`, String(args.handle ?? ""))
|
|
258
|
+
);
|
|
259
|
+
reg(
|
|
260
|
+
"nav_visit",
|
|
261
|
+
"Navigate to a URL (same-site path or absolute). The human watches the page change.",
|
|
262
|
+
{ url: { type: "string", description: "Path like /packages or an absolute URL." } },
|
|
263
|
+
["url"],
|
|
264
|
+
async (args) => {
|
|
265
|
+
const stale = await staleIntent();
|
|
266
|
+
if (stale) return stale;
|
|
267
|
+
const url = String(args.url ?? "");
|
|
268
|
+
if (!url) return errorResult("url is required");
|
|
269
|
+
const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase();
|
|
270
|
+
if (scheme && scheme !== "http" && scheme !== "https") {
|
|
271
|
+
return errorResult(
|
|
272
|
+
`Refusing to navigate to a '${scheme}:' URL. Only http(s) URLs or same-site paths are allowed.`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
const from = (await adapter.getLocation()).url;
|
|
276
|
+
await adapter.visit(url);
|
|
277
|
+
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
278
|
+
timestamp: Date.now(),
|
|
279
|
+
bridgeId: "navigation",
|
|
280
|
+
action: "nav_visit",
|
|
281
|
+
label: `Navigate to ${url}`,
|
|
282
|
+
undo: () => {
|
|
283
|
+
adapter.visit(from);
|
|
284
|
+
},
|
|
285
|
+
redo: () => {
|
|
286
|
+
adapter.visit(url);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
return textResult(`Navigated to ${url}`, { url });
|
|
290
|
+
},
|
|
291
|
+
(args) => target(`Navigate \u2192 ${String(args.url ?? "")}`)
|
|
292
|
+
);
|
|
293
|
+
reg(
|
|
294
|
+
"nav_back",
|
|
295
|
+
"Go back to the previous page.",
|
|
296
|
+
{},
|
|
297
|
+
[],
|
|
298
|
+
async () => {
|
|
299
|
+
const stale = await staleIntent();
|
|
300
|
+
if (stale) return stale;
|
|
301
|
+
if (!adapter.back) return errorResult("Host did not provide back navigation.");
|
|
302
|
+
await adapter.back();
|
|
303
|
+
return textResult("Went back");
|
|
304
|
+
},
|
|
305
|
+
() => target("Back")
|
|
306
|
+
);
|
|
307
|
+
reg(
|
|
308
|
+
"nav_forward",
|
|
309
|
+
"Go forward to the next page.",
|
|
310
|
+
{},
|
|
311
|
+
[],
|
|
312
|
+
async () => {
|
|
313
|
+
const stale = await staleIntent();
|
|
314
|
+
if (stale) return stale;
|
|
315
|
+
if (!adapter.forward) return errorResult("Host did not provide forward navigation.");
|
|
316
|
+
await adapter.forward();
|
|
317
|
+
return textResult("Went forward");
|
|
318
|
+
},
|
|
319
|
+
() => target("Forward")
|
|
320
|
+
);
|
|
321
|
+
reg(
|
|
322
|
+
"nav_scroll_to",
|
|
323
|
+
"Scroll the page to absolute coordinates, or to a specific element by its handle.",
|
|
324
|
+
{
|
|
325
|
+
handle: { type: "string", description: "Scroll this element into view." },
|
|
326
|
+
x: { type: "number" },
|
|
327
|
+
y: { type: "number" }
|
|
328
|
+
},
|
|
329
|
+
[],
|
|
330
|
+
async (args) => {
|
|
331
|
+
const stale = await staleIntent();
|
|
332
|
+
if (stale) return stale;
|
|
333
|
+
const shown = typeof args.handle === "string" ? args.handle : void 0;
|
|
334
|
+
const handle = shown === void 0 ? void 0 : pageHandle(shown);
|
|
335
|
+
await adapter.scrollTo({
|
|
336
|
+
handle,
|
|
337
|
+
x: typeof args.x === "number" ? args.x : void 0,
|
|
338
|
+
y: typeof args.y === "number" ? args.y : void 0
|
|
339
|
+
});
|
|
340
|
+
return textResult(
|
|
341
|
+
"Scrolled",
|
|
342
|
+
handle ? { handle: shown, rect: await adapter.rectFor?.(handle) ?? void 0 } : void 0
|
|
343
|
+
);
|
|
344
|
+
},
|
|
345
|
+
() => target("Scroll")
|
|
346
|
+
);
|
|
347
|
+
reg(
|
|
348
|
+
"nav_scroll_by",
|
|
349
|
+
"Scroll the page by a vertical delta in pixels (negative scrolls up).",
|
|
350
|
+
{ dy: { type: "number" } },
|
|
351
|
+
["dy"],
|
|
352
|
+
async (args) => {
|
|
353
|
+
const stale = await staleIntent();
|
|
354
|
+
if (stale) return stale;
|
|
355
|
+
await adapter.scrollBy(Number(args.dy ?? 0));
|
|
356
|
+
return textResult(`Scrolled by ${Number(args.dy ?? 0)}px`);
|
|
357
|
+
},
|
|
358
|
+
() => target("Scroll")
|
|
359
|
+
);
|
|
360
|
+
reg(
|
|
361
|
+
"page_set_field",
|
|
362
|
+
"Set a form field's value by handle. The host updates the controlled input and the human sees it change.",
|
|
363
|
+
{
|
|
364
|
+
handle: { type: "string" },
|
|
365
|
+
value: { description: "Value to set; type matches the field." }
|
|
366
|
+
},
|
|
367
|
+
["handle", "value"],
|
|
368
|
+
async (args) => {
|
|
369
|
+
const stale = await staleIntent();
|
|
370
|
+
if (stale) return stale;
|
|
371
|
+
const shown = String(args.handle ?? "");
|
|
372
|
+
const handle = pageHandle(shown);
|
|
373
|
+
const res = await adapter.setField(handle, args.value);
|
|
374
|
+
if (!res.ok) return failure(res.error, `Could not set ${oneLine(shown, CAP.label)}`);
|
|
375
|
+
return textResult(`${oneLine(shown, CAP.label)} \u2190 ${JSON.stringify(args.value)}`, {
|
|
376
|
+
handle: shown,
|
|
377
|
+
value: args.value,
|
|
378
|
+
rect: await adapter.rectFor?.(handle) ?? void 0
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
(args) => target(`Set ${String(args.handle ?? "")}`, String(args.handle ?? ""))
|
|
382
|
+
);
|
|
383
|
+
reg(
|
|
384
|
+
"page_click",
|
|
385
|
+
"Activate an element by handle (link, button, checkbox\u2026). Destructive elements are staged for the human to confirm.",
|
|
386
|
+
{ handle: { type: "string" } },
|
|
387
|
+
["handle"],
|
|
388
|
+
async (args) => {
|
|
389
|
+
const stale = await staleIntent();
|
|
390
|
+
if (stale) return stale;
|
|
391
|
+
const shown = String(args.handle ?? "");
|
|
392
|
+
const handle = pageHandle(shown);
|
|
393
|
+
const action = (await adapter.describe()).actions.find((a) => a.handle === handle);
|
|
394
|
+
const rect = await adapter.rectFor?.(handle) ?? void 0;
|
|
395
|
+
if (pendingMode && action?.destructive && adapter.confirm) {
|
|
396
|
+
const ok = await adapter.confirm({ action: "click", handle, label: action.label });
|
|
397
|
+
if (!ok) return errorResult("Declined by user");
|
|
398
|
+
}
|
|
399
|
+
const res = await adapter.click(handle);
|
|
400
|
+
if (!res.ok) return failure(res.error, `Could not click ${oneLine(shown, CAP.label)}`);
|
|
401
|
+
return textResult(`Clicked ${oneLine(shown, CAP.label)}`, { handle: shown, rect });
|
|
402
|
+
},
|
|
403
|
+
(args) => target(`Click ${String(args.handle ?? "")}`, String(args.handle ?? ""))
|
|
404
|
+
);
|
|
405
|
+
reg(
|
|
406
|
+
"page_submit",
|
|
407
|
+
"Submit a form by handle. Always staged for the human to confirm when pendingMode is on.",
|
|
408
|
+
{ handle: { type: "string" } },
|
|
409
|
+
["handle"],
|
|
410
|
+
async (args) => {
|
|
411
|
+
const stale = await staleIntent();
|
|
412
|
+
if (stale) return stale;
|
|
413
|
+
const shown = String(args.handle ?? "");
|
|
414
|
+
const handle = pageHandle(shown);
|
|
415
|
+
const rect = await adapter.rectFor?.(handle) ?? void 0;
|
|
416
|
+
if (pendingMode && adapter.confirm) {
|
|
417
|
+
const ok = await adapter.confirm({ action: "submit", handle, label: handle });
|
|
418
|
+
if (!ok) return errorResult("Declined by user");
|
|
419
|
+
}
|
|
420
|
+
const res = await adapter.submit(handle);
|
|
421
|
+
if (!res.ok) return failure(res.error, "Submit failed");
|
|
422
|
+
return textResult(`Submitted ${oneLine(shown, CAP.label)}`, { handle: shown, rect });
|
|
423
|
+
},
|
|
424
|
+
(args) => target(`Submit ${String(args.handle ?? "")}`, String(args.handle ?? ""))
|
|
425
|
+
);
|
|
426
|
+
return {
|
|
427
|
+
id: "navigation",
|
|
428
|
+
title: "Co-browsing",
|
|
429
|
+
dispose: () => {
|
|
430
|
+
for (const d of disposers) d();
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
exports.registerNavigationBridge = registerNavigationBridge;
|
|
436
|
+
//# sourceMappingURL=bridges-navigation.cjs.map
|
|
437
|
+
//# sourceMappingURL=bridges-navigation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/server.ts","../src/presence/wrap-tool-with-activity.ts","../src/undo/undo-tools.ts","../src/bridges/navigation.ts"],"names":["emitActivity","undoOne","redoOne","readHistory","pushUndoEntry"],"mappings":"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","file":"bridges-navigation.cjs","sourcesContent":["import {\n type CallToolResult,\n type JsonObject,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcId,\n type RegisteredTool,\n type ServerCapabilities,\n type ServerInfo,\n type ToolDefinition,\n type ToolHandler,\n JSONRPC_INTERNAL_ERROR,\n JSONRPC_INVALID_PARAMS,\n JSONRPC_METHOD_NOT_FOUND,\n MCP_PROTOCOL_VERSION,\n} from \"./types\";\nimport { ToolRegistry } from \"./tool-host\";\n\nexport type McpServerOptions = {\n info: ServerInfo;\n /** Defaults to { tools: { listChanged: true } } */\n capabilities?: ServerCapabilities;\n /** Free-text instructions surfaced to clients during initialize. */\n instructions?: string;\n /**\n * The protocol revisions this server speaks, NEWEST FIRST.\n *\n * `initialize` echoes the client's requested revision when it is in this\n * list, and answers the first entry otherwise — the spec's rule, which leaves\n * the client to decide whether it can live with the answer.\n *\n * Defaults to `[MCP_PROTOCOL_VERSION]`: one revision, answered whatever the\n * client asks, which is exactly what this server did before the option\n * existed. Only list a revision the tools you register are correct under.\n */\n protocolVersions?: readonly string[];\n};\n\nexport type Transport = {\n /** Called by the server when it has a message to deliver to the client. */\n send: (message: JsonRpcMessage) => void;\n /** Called by the server when it's torn down so the transport can clean up. */\n close?: () => void;\n};\n\n/**\n * MicroMcpServer — protocol-level MCP server, transport-agnostic.\n *\n * Use it like:\n *\n * const server = new MicroMcpServer({ info: { name: \"session\", version: \"0.1\" } });\n * server.registerTool({ name: \"...\", inputSchema: { type: \"object\" } }, async (args) => ({...}));\n * const transport = new InProcessTransport();\n * server.attach(transport);\n * transport.deliver({ ... }); // client → server frames\n *\n * The same server can serve multiple transports (e.g. an in-process agent\n * AND a relayed external client) by attaching each one.\n */\nexport class MicroMcpServer extends ToolRegistry {\n private transports = new Set<Transport>();\n /**\n * Transports owed a `tools/list_changed` at the end of this tick. Empty when\n * none is scheduled. See {@link scheduleListChangedNotification}.\n */\n private listChangedOwed = new Set<Transport>();\n\n readonly info: ServerInfo;\n readonly capabilities: ServerCapabilities;\n readonly instructions?: string;\n readonly protocolVersions: readonly string[];\n\n constructor(options: McpServerOptions) {\n super();\n this.info = options.info;\n this.capabilities = options.capabilities ?? { tools: { listChanged: true } };\n this.instructions = options.instructions;\n if (options.protocolVersions && options.protocolVersions.length === 0) {\n // No answer exists for a client's initialize. Fail here, next to the\n // mistake, rather than in a handshake nobody is watching.\n throw new Error(\"MicroMcpServer: protocolVersions is empty — a server must speak at least one protocol revision.\");\n }\n this.protocolVersions = options.protocolVersions ? [...options.protocolVersions] : [MCP_PROTOCOL_VERSION];\n }\n\n attach(transport: Transport): () => void {\n this.transports.add(transport);\n return () => this.detach(transport);\n }\n\n detach(transport: Transport): void {\n if (this.transports.delete(transport)) {\n transport.close?.();\n }\n }\n\n unregisterTool(name: string): void {\n if (this.tools.delete(name)) {\n this.scheduleListChangedNotification();\n }\n }\n\n protected onToolsChanged(): void {\n this.scheduleListChangedNotification();\n }\n\n /**\n * Receive a JSON-RPC frame from a client (called by the transport).\n * The transport is responsible for sending the response back.\n */\n async receive(transport: Transport, message: JsonRpcMessage): Promise<void> {\n if (!(\"method\" in message)) return; // It's a response, not a request — ignore.\n\n const isNotification = !(\"id\" in message);\n if (isNotification) {\n // Notifications are fire-and-forget. We ignore unknown methods.\n return;\n }\n\n const request = message as JsonRpcRequest;\n try {\n const result = await this.handle(request, transport);\n transport.send({ jsonrpc: \"2.0\", id: request.id, result });\n } catch (err) {\n transport.send({\n jsonrpc: \"2.0\",\n id: request.id,\n error: this.toRpcError(err),\n });\n }\n }\n\n private async handle(request: JsonRpcRequest, transport: Transport): Promise<any> {\n const { method, params } = request;\n switch (method) {\n case \"initialize\":\n return {\n protocolVersion: this.negotiate(params?.protocolVersion),\n capabilities: this.capabilities,\n serverInfo: this.info,\n ...(this.instructions ? { instructions: this.instructions } : {}),\n };\n\n case \"tools/list\":\n return { tools: this.listTools() };\n\n case \"tools/call\": {\n const name = params?.name;\n const args = (params?.arguments ?? {}) as JsonObject;\n if (typeof name !== \"string\") {\n throw rpcError(JSONRPC_INVALID_PARAMS, \"tools/call requires `name`\");\n }\n const tool = this.tools.get(name);\n if (!tool) {\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, this.missingToolMessage(name));\n }\n const result = await tool.handler(args, { transport });\n return result satisfies CallToolResult;\n }\n\n case \"ping\":\n return {};\n\n default:\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unsupported method: ${method}`);\n }\n }\n\n /** The revision to answer `initialize` with: the one asked for if spoken, else the newest. */\n private negotiate(requested: unknown): string {\n return typeof requested === \"string\" && this.protocolVersions.includes(requested)\n ? requested\n : this.protocolVersions[0];\n }\n\n /**\n * Tell the transports that SAW the old tool list that it changed.\n *\n * Changes in one tick coalesce into one notification. The recipients are the\n * transports attached at the moment of a change, not at the moment the\n * notification goes out: a transport attached afterwards never had the old\n * list, so \"your list is stale\" is noise to it — and on stdio, where a host\n * builds the server and attaches stdin/stdout in the same tick, it was the\n * first frame a client received, before it had even sent `initialize`.\n */\n private scheduleListChangedNotification(): void {\n if (this.transports.size === 0) return;\n const scheduled = this.listChangedOwed.size > 0;\n for (const t of this.transports) this.listChangedOwed.add(t);\n if (scheduled) return;\n queueMicrotask(() => {\n const owed = this.listChangedOwed;\n this.listChangedOwed = new Set();\n for (const t of owed) {\n // Detached since the change: it is not listening any more.\n if (this.transports.has(t)) t.send({ jsonrpc: \"2.0\", method: \"notifications/tools/list_changed\" });\n }\n });\n }\n\n /** Send an application notification to every client, or one attached transport. */\n notify(message: JsonRpcMessage, transport?: Transport): void {\n if (transport) transport.send(message);\n else this.broadcast(message);\n }\n\n private broadcast(message: JsonRpcMessage): void {\n for (const t of this.transports) t.send(message);\n }\n\n private toRpcError(err: unknown): { code: number; message: string; data?: any } {\n if (err && typeof err === \"object\" && \"code\" in err && \"message\" in err) {\n return err as any;\n }\n return {\n code: JSONRPC_INTERNAL_ERROR,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport function rpcError(code: number, message: string, data?: any) {\n return { code, message, ...(data !== undefined ? { data } : {}) };\n}\n\n/**\n * Helper to build a CallToolResult from a string or structured value.\n */\nexport function textResult(text: string, structured?: any): CallToolResult {\n return {\n content: [{ type: \"text\", text }],\n ...(structured !== undefined ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(text: string): CallToolResult {\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\n// Internal helper so the JsonRpcId import isn't dropped by tsup\ntype _KeepIdImport = JsonRpcId;\n","import type { CallToolResult } from \"../mcp/types\";\nimport { emitActivity } from \"./registry\";\nimport type { AgentTarget } from \"./types\";\n\nexport type ActivityAgent = { id: string; name?: string; color?: string };\n\nexport type ActivityResolverContext<TArgs = Record<string, unknown>> = {\n /** Tool name as registered. */\n toolName: string;\n /** Arguments the tool was called with. */\n args: TArgs;\n /** The CallToolResult the underlying handler produced. */\n result: CallToolResult;\n};\n\n/**\n * Resolves an `AgentTarget` for an executed tool. Bridges declare one of\n * these per registration so the wrapper knows which surface / element /\n * screen the activity belongs to.\n *\n * The resolver runs AFTER the tool handler so it can inspect the result\n * (e.g. read a newly-created item id from `structuredContent`).\n */\nexport type ActivityTargetResolver<TArgs = Record<string, unknown>> = (\n ctx: ActivityResolverContext<TArgs>,\n) => AgentTarget | null;\n\nexport type ToolHandler<TArgs = Record<string, unknown>> = (\n args: TArgs,\n) => Promise<CallToolResult> | CallToolResult;\n\n/**\n * wrapToolWithActivity — decorate a bridge tool handler so every successful\n * call emits an `AgentActivityEvent`. Returns a new handler with the same shape.\n *\n * Usage in a bridge:\n *\n * server.registerTool(\n * definition,\n * wrapToolWithActivity(\n * handler,\n * { agent, kind: \"whiteboard\", resolveTarget: ({ args }) => ({\n * kind: \"whiteboard\", elementId: args.id as string,\n * }) },\n * ),\n * );\n */\nexport function wrapToolWithActivity<TArgs = Record<string, unknown>>(\n handler: ToolHandler<TArgs>,\n options: {\n toolName: string;\n agent: ActivityAgent;\n /** Optional fancy-screens screen id this bridge is scoped to. */\n screenId?: string;\n /** Default target kind if the resolver returns one without `kind`. */\n kind: AgentTarget[\"kind\"];\n /** Per-call resolver. Return `null` to skip emitting (e.g. for read-only tools). */\n resolveTarget?: ActivityTargetResolver<TArgs>;\n /** Optional ttl override. */\n ttlMs?: number;\n /**\n * Build the `meta` broadcast to all relay peers. Defaults to the tool's full\n * `structuredContent`. Override to REDACT sensitive fields before fan-out —\n * e.g. the terminal bridge strips raw command/keystroke bytes so a command\n * line (or its secrets) is never broadcast verbatim to every peer.\n */\n buildMeta?: (result: CallToolResult) => Record<string, unknown> | undefined;\n },\n): ToolHandler<TArgs> {\n return async (args) => {\n const result = await handler(args);\n if (result.isError) return result;\n\n let target: AgentTarget | null;\n if (options.resolveTarget) {\n target = options.resolveTarget({ toolName: options.toolName, args, result });\n } else {\n target = { kind: options.kind, screenId: options.screenId };\n }\n if (!target) return result;\n\n emitActivity({\n agentId: options.agent.id,\n agentName: options.agent.name,\n agentColor: options.agent.color,\n target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },\n action: options.toolName,\n timestamp: Date.now(),\n meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),\n ttlMs: options.ttlMs,\n });\n return result;\n };\n}\n\nfunction extractMeta(result: CallToolResult): Record<string, unknown> | undefined {\n const sc = result.structuredContent;\n if (sc && typeof sc === \"object\" && !Array.isArray(sc)) {\n return sc as Record<string, unknown>;\n }\n return undefined;\n}\n","import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport { readHistory, redoOne, undoOne } from \"./undo-stack\";\n\nexport type UndoToolsOptions = {\n /** Default agent id when the caller doesn't pass one. */\n defaultAgentId?: string;\n};\n\n/**\n * Idempotent tracker so multiple bridges on the same server only register\n * agent_undo / agent_redo / agent_history once.\n */\nconst installedHosts = new WeakSet<ToolHost>();\n\n/**\n * ensureUndoToolsRegistered — bridges call this on construction. Safe to\n * call repeatedly with the same server; subsequent calls are no-ops.\n */\nexport function ensureUndoToolsRegistered(host: ToolHost, options: UndoToolsOptions = {}): void {\n if (installedHosts.has(host)) return;\n installedHosts.add(host);\n registerUndoTools(host, options);\n}\n\n/**\n * registerUndoTools — add agent_undo / agent_redo / agent_history to the\n * server. Returns a disposer that unregisters all three.\n */\nexport function registerUndoTools(host: ToolHost, options: UndoToolsOptions = {}): () => void {\n const defaultAgent = options.defaultAgentId ?? \"agent\";\n const disposers: Array<() => void> = [];\n const agentOf = (args: any): string =>\n typeof args?.agentId === \"string\" ? args.agentId : defaultAgent;\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_undo\",\n description: \"Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await undoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to undo.\");\n return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });\n },\n ),\n );\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_redo\",\n description: \"Redo the most recently undone action.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await redoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to redo.\");\n return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });\n },\n ),\n );\n\n disposers.push(\n host.registerTool(\n {\n name: \"agent_history\",\n description: \"List the agent's undo stack (oldest first). Useful for understanding what's reversible.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const history = readHistory(agentOf(args)).map(serialize);\n const text = history.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join(\"\\n\");\n return textResult(text || \"(empty)\", history);\n },\n ),\n );\n\n return () => disposers.forEach((d) => d());\n}\n\nfunction serialize(entry: import(\"./undo-stack\").UndoEntry) {\n return {\n timestamp: entry.timestamp,\n bridgeId: entry.bridgeId,\n action: entry.action,\n label: entry.label,\n };\n}\n","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"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { registerNavigationBridge } from './chunk-OHNGURUJ.js';
|
|
2
|
+
import './chunk-R4XXOGQH.js';
|
|
3
|
+
import './chunk-2VENWGLZ.js';
|
|
4
|
+
import './chunk-C3TYI5TJ.js';
|
|
5
|
+
import './chunk-CZV3WUW5.js';
|
|
6
|
+
//# sourceMappingURL=bridges-navigation.js.map
|
|
7
|
+
//# sourceMappingURL=bridges-navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"bridges-navigation.js"}
|