@particle-academy/agent-integrations 0.28.2 → 0.30.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.
- package/README.md +38 -0
- 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 +23 -4
- package/dist/bridges/files.d.ts +23 -4
- package/dist/bridges/flow.d.cts +3 -3
- package/dist/bridges/flow.d.ts +3 -3
- package/dist/bridges/forms.d.cts +25 -4
- package/dist/bridges/forms.d.ts +25 -4
- package/dist/bridges/map.d.cts +3 -3
- package/dist/bridges/map.d.ts +3 -3
- 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 +24 -4
- package/dist/bridges/terminal.d.ts +24 -4
- package/dist/bridges/tui.d.cts +45 -0
- package/dist/bridges/tui.d.ts +45 -0
- package/dist/bridges/whiteboard.d.cts +3 -3
- package/dist/bridges/whiteboard.d.ts +3 -3
- package/dist/bridges-artboard.cjs +1 -1
- package/dist/bridges-artboard.cjs.map +1 -1
- package/dist/bridges-artboard.js +3 -3
- package/dist/bridges-catalog.cjs +1 -1
- package/dist/bridges-catalog.cjs.map +1 -1
- package/dist/bridges-catalog.js +4 -4
- package/dist/bridges-charts.cjs +1 -1
- package/dist/bridges-charts.cjs.map +1 -1
- package/dist/bridges-charts.js +4 -4
- package/dist/bridges-cms.cjs +1 -1
- package/dist/bridges-cms.cjs.map +1 -1
- package/dist/bridges-cms.js +5 -5
- package/dist/bridges-code.cjs +38 -3
- package/dist/bridges-code.cjs.map +1 -1
- package/dist/bridges-code.js +4 -4
- package/dist/bridges-doc.cjs +1 -1
- package/dist/bridges-doc.cjs.map +1 -1
- package/dist/bridges-doc.js +4 -4
- package/dist/bridges-features.cjs +1 -1
- package/dist/bridges-features.cjs.map +1 -1
- package/dist/bridges-features.js +4 -4
- package/dist/bridges-files.cjs +29 -8
- package/dist/bridges-files.cjs.map +1 -1
- package/dist/bridges-files.js +4 -4
- package/dist/bridges-flow.cjs +1 -1
- package/dist/bridges-flow.cjs.map +1 -1
- package/dist/bridges-flow.js +3 -3
- package/dist/bridges-forms.cjs +12 -2
- package/dist/bridges-forms.cjs.map +1 -1
- package/dist/bridges-forms.js +4 -4
- package/dist/bridges-map.cjs +1 -1
- package/dist/bridges-map.cjs.map +1 -1
- package/dist/bridges-map.js +4 -4
- package/dist/bridges-scene.cjs +1 -1
- package/dist/bridges-scene.cjs.map +1 -1
- package/dist/bridges-scene.js +4 -4
- package/dist/bridges-screens.cjs +1 -1
- package/dist/bridges-screens.cjs.map +1 -1
- package/dist/bridges-screens.js +4 -4
- package/dist/bridges-sheets.cjs +1 -1
- package/dist/bridges-sheets.cjs.map +1 -1
- package/dist/bridges-sheets.js +4 -4
- package/dist/bridges-slides.cjs +1 -1
- package/dist/bridges-slides.cjs.map +1 -1
- package/dist/bridges-slides.js +4 -4
- package/dist/bridges-terminal.cjs +70 -45
- package/dist/bridges-terminal.cjs.map +1 -1
- package/dist/bridges-terminal.js +4 -4
- package/dist/bridges-tui.cjs +114 -0
- package/dist/bridges-tui.cjs.map +1 -0
- package/dist/bridges-tui.js +7 -0
- package/dist/bridges-tui.js.map +1 -0
- package/dist/bridges-whiteboard.cjs +1 -1
- package/dist/bridges-whiteboard.cjs.map +1 -1
- package/dist/bridges-whiteboard.js +4 -4
- package/dist/{chunk-AAMQOXZC.js → chunk-2U5JB6ZA.js} +5 -5
- package/dist/{chunk-AAMQOXZC.js.map → chunk-2U5JB6ZA.js.map} +1 -1
- package/dist/{chunk-ULJL53DL.js → chunk-2VENWGLZ.js} +3 -3
- package/dist/chunk-2VENWGLZ.js.map +1 -0
- package/dist/{chunk-66WOHZZJ.js → chunk-6YUBWQTS.js} +5 -5
- package/dist/{chunk-66WOHZZJ.js.map → chunk-6YUBWQTS.js.map} +1 -1
- package/dist/{chunk-ZX6UQURN.js → chunk-BHQJJ2YG.js} +5 -5
- package/dist/{chunk-ZX6UQURN.js.map → chunk-BHQJJ2YG.js.map} +1 -1
- package/dist/{chunk-GHY3PBPN.js → chunk-BT7HM5I6.js} +3 -3
- package/dist/{chunk-GHY3PBPN.js.map → chunk-BT7HM5I6.js.map} +1 -1
- package/dist/{chunk-YGD2VTUM.js → chunk-CA7GIJJC.js} +5 -5
- package/dist/{chunk-YGD2VTUM.js.map → chunk-CA7GIJJC.js.map} +1 -1
- package/dist/{chunk-KJ5AOOV7.js → chunk-CT3LRNH7.js} +3 -3
- package/dist/{chunk-KJ5AOOV7.js.map → chunk-CT3LRNH7.js.map} +1 -1
- package/dist/{chunk-4KAIV6OD.js → chunk-DF3PLL2E.js} +10 -5
- package/dist/chunk-DF3PLL2E.js.map +1 -0
- package/dist/{chunk-A57CRGEK.js → chunk-DKSR4R4O.js} +3 -3
- package/dist/{chunk-A57CRGEK.js.map → chunk-DKSR4R4O.js.map} +1 -1
- package/dist/{chunk-AROVENCE.js → chunk-G5EZNWXJ.js} +5 -5
- package/dist/{chunk-AROVENCE.js.map → chunk-G5EZNWXJ.js.map} +1 -1
- package/dist/{chunk-OIX2ANFS.js → chunk-GRHPEK6M.js} +45 -9
- package/dist/chunk-GRHPEK6M.js.map +1 -0
- package/dist/{chunk-XMMCSQ4I.js → chunk-HRHC5GM5.js} +33 -13
- package/dist/chunk-HRHC5GM5.js.map +1 -0
- package/dist/{chunk-5KLUYF4D.js → chunk-HSI5RVF4.js} +3 -3
- package/dist/{chunk-5KLUYF4D.js.map → chunk-HSI5RVF4.js.map} +1 -1
- package/dist/{chunk-FF5F526J.js → chunk-LQZKRBWR.js} +5 -5
- package/dist/{chunk-FF5F526J.js.map → chunk-LQZKRBWR.js.map} +1 -1
- package/dist/chunk-MFMBR6LN.js +100 -0
- package/dist/chunk-MFMBR6LN.js.map +1 -0
- package/dist/{chunk-KMSRG5KH.js → chunk-N6RLSLOF.js} +7 -7
- package/dist/chunk-N6RLSLOF.js.map +1 -0
- package/dist/{chunk-VFKO2VUR.js → chunk-OAFJ53X3.js} +5 -5
- package/dist/{chunk-VFKO2VUR.js.map → chunk-OAFJ53X3.js.map} +1 -1
- package/dist/chunk-PMI4C5S4.js +44 -0
- package/dist/chunk-PMI4C5S4.js.map +1 -0
- package/dist/{chunk-QDD4WHIV.js → chunk-PR4SXFZH.js} +5 -5
- package/dist/{chunk-QDD4WHIV.js.map → chunk-PR4SXFZH.js.map} +1 -1
- package/dist/{chunk-NN3ELTVO.js → chunk-RIXYCFDO.js} +42 -7
- package/dist/chunk-RIXYCFDO.js.map +1 -0
- package/dist/{chunk-PCW2EK5G.js → chunk-USQGA672.js} +16 -6
- package/dist/chunk-USQGA672.js.map +1 -0
- package/dist/{chunk-HWVVS6HE.js → chunk-UZHNKUER.js} +74 -49
- package/dist/chunk-UZHNKUER.js.map +1 -0
- package/dist/{chunk-QRDVJW6Q.js → chunk-WCKSDFTM.js} +5 -5
- package/dist/{chunk-QRDVJW6Q.js.map → chunk-WCKSDFTM.js.map} +1 -1
- package/dist/{chunk-AAD3Z5E7.js → chunk-ZNGTC4CO.js} +5 -5
- package/dist/{chunk-AAD3Z5E7.js.map → chunk-ZNGTC4CO.js.map} +1 -1
- package/dist/components/SharedWhiteboard/index.d.cts +1 -1
- package/dist/components/SharedWhiteboard/index.d.ts +1 -1
- package/dist/components-shared-whiteboard.cjs +10 -6
- package/dist/components-shared-whiteboard.cjs.map +1 -1
- package/dist/components-shared-whiteboard.js +6 -6
- package/dist/heuristics.js +1 -1
- package/dist/human-plus/file-store.d.cts +17 -0
- package/dist/human-plus/file-store.d.ts +17 -0
- package/dist/human-plus/index.d.cts +61 -0
- package/dist/human-plus/index.d.ts +61 -0
- package/dist/human-plus-node.cjs +121 -0
- package/dist/human-plus-node.cjs.map +1 -0
- package/dist/human-plus-node.js +76 -0
- package/dist/human-plus-node.js.map +1 -0
- package/dist/human-plus.cjs +46 -0
- package/dist/human-plus.cjs.map +1 -0
- package/dist/human-plus.js +3 -0
- package/dist/human-plus.js.map +1 -0
- package/dist/index.cjs +337 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -9
- package/dist/index.d.ts +11 -9
- package/dist/index.js +35 -27
- 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.cjs +8 -3
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +1 -1
- package/dist/presence/index.d.cts +8 -1
- package/dist/presence/index.d.ts +8 -1
- package/dist/presence.cjs +1 -1
- package/dist/presence.cjs.map +1 -1
- package/dist/presence.js +1 -1
- package/dist/relay-server/index.d.cts +20 -0
- package/dist/relay-server/index.d.ts +20 -0
- package/dist/relay-server-cli.cjs +53 -9
- package/dist/relay-server-cli.cjs.map +1 -1
- package/dist/relay-server-cli.js +11 -3
- package/dist/relay-server-cli.js.map +1 -1
- package/dist/relay-server.cjs +43 -7
- package/dist/relay-server.cjs.map +1 -1
- package/dist/relay-server.js +1 -1
- package/dist/{server-FFZpYPyM.d.ts → server-CDX643Kz.d.ts} +4 -2
- package/dist/{server-Dr3j48CU.d.cts → server-DDD3IZ56.d.cts} +4 -2
- package/dist/sharing/index.d.cts +4 -4
- package/dist/sharing/index.d.ts +4 -4
- package/dist/sharing.cjs +5 -5
- package/dist/sharing.cjs.map +1 -1
- package/dist/sharing.js +1 -1
- package/dist/sheets-adapter.d.cts +3 -3
- package/dist/sheets-adapter.d.ts +3 -3
- package/dist/{token-CrJF76oH.d.cts → token-BZ8doMam.d.cts} +15 -2
- package/dist/{token-CrJF76oH.d.ts → token-BZ8doMam.d.ts} +15 -2
- package/dist/{tool-host-C_Vh17J_.d.ts → tool-host-CWTOJEm2.d.ts} +1 -1
- package/dist/{tool-host-IDecPrF1.d.cts → tool-host-EuYHYSSH.d.cts} +1 -1
- package/dist/{types-CBoQhR8L.d.ts → types-BKD6uf86.d.ts} +1 -1
- package/dist/{types-BDI_g5UG.d.ts → types-C58ZXs4K.d.cts} +6 -2
- package/dist/{types-BDI_g5UG.d.cts → types-C58ZXs4K.d.ts} +6 -2
- package/dist/{types-B0rmWyoR.d.cts → types-NFGP769G.d.cts} +1 -1
- package/dist/undo/index.d.cts +2 -2
- package/dist/undo/index.d.ts +2 -2
- package/dist/undo.cjs.map +1 -1
- package/dist/undo.js +3 -3
- package/package.json +13 -1
- package/dist/chunk-4KAIV6OD.js.map +0 -1
- package/dist/chunk-HWVVS6HE.js.map +0 -1
- package/dist/chunk-KMSRG5KH.js.map +0 -1
- package/dist/chunk-NN3ELTVO.js.map +0 -1
- package/dist/chunk-OIX2ANFS.js.map +0 -1
- package/dist/chunk-PCW2EK5G.js.map +0 -1
- package/dist/chunk-ULJL53DL.js.map +0 -1
- package/dist/chunk-XMMCSQ4I.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -112,7 +112,7 @@ var MicroMcpServer = class extends ToolRegistry {
|
|
|
112
112
|
}
|
|
113
113
|
const request = message;
|
|
114
114
|
try {
|
|
115
|
-
const result = await this.handle(request);
|
|
115
|
+
const result = await this.handle(request, transport);
|
|
116
116
|
transport.send({ jsonrpc: "2.0", id: request.id, result });
|
|
117
117
|
} catch (err) {
|
|
118
118
|
transport.send({
|
|
@@ -122,7 +122,7 @@ var MicroMcpServer = class extends ToolRegistry {
|
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
async handle(request) {
|
|
125
|
+
async handle(request, transport) {
|
|
126
126
|
const { method, params } = request;
|
|
127
127
|
switch (method) {
|
|
128
128
|
case "initialize":
|
|
@@ -144,7 +144,7 @@ var MicroMcpServer = class extends ToolRegistry {
|
|
|
144
144
|
if (!tool) {
|
|
145
145
|
throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unknown tool: ${name}`);
|
|
146
146
|
}
|
|
147
|
-
const result = await tool.handler(args);
|
|
147
|
+
const result = await tool.handler(args, { transport });
|
|
148
148
|
return result;
|
|
149
149
|
}
|
|
150
150
|
case "ping":
|
|
@@ -161,6 +161,11 @@ var MicroMcpServer = class extends ToolRegistry {
|
|
|
161
161
|
this.broadcast({ jsonrpc: "2.0", method: "notifications/tools/list_changed" });
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
+
/** Send an application notification to every client, or one attached transport. */
|
|
165
|
+
notify(message, transport) {
|
|
166
|
+
if (transport) transport.send(message);
|
|
167
|
+
else this.broadcast(message);
|
|
168
|
+
}
|
|
164
169
|
broadcast(message) {
|
|
165
170
|
for (const t of this.transports) t.send(message);
|
|
166
171
|
}
|
|
@@ -252,6 +257,50 @@ function attachRelay(server, channel) {
|
|
|
252
257
|
return transport;
|
|
253
258
|
}
|
|
254
259
|
|
|
260
|
+
// src/human-plus/events.ts
|
|
261
|
+
var MemoryHumanPlusEventStore = class {
|
|
262
|
+
constructor() {
|
|
263
|
+
this.events = [];
|
|
264
|
+
this.acknowledgements = /* @__PURE__ */ new Map();
|
|
265
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
266
|
+
this.nextSequence = 1;
|
|
267
|
+
}
|
|
268
|
+
async append(input) {
|
|
269
|
+
const event = { ...input, id: input.id ?? globalThis.crypto.randomUUID(), timestamp: input.timestamp ?? Date.now(), sequence: this.nextSequence++ };
|
|
270
|
+
this.events.push(event);
|
|
271
|
+
this.listeners.forEach((listener) => listener(event));
|
|
272
|
+
return event;
|
|
273
|
+
}
|
|
274
|
+
async list(query) {
|
|
275
|
+
const acknowledged = this.acknowledgements.get(query.consumerId);
|
|
276
|
+
const events = this.events.filter((event) => event.sequence > (query.after ?? 0) && (!query.unacknowledgedOnly || !acknowledged?.has(event.id)) && (!query.types?.length || query.types.includes(event.type)) && (!query.surfaces?.length || query.surfaces.includes(event.surfaceId))).slice(0, Math.max(1, Math.min(500, query.limit ?? 50)));
|
|
277
|
+
return { events, nextCursor: events.at(-1)?.sequence ?? query.after ?? 0 };
|
|
278
|
+
}
|
|
279
|
+
async acknowledge(consumerId, eventIds, disposition) {
|
|
280
|
+
let values = this.acknowledgements.get(consumerId);
|
|
281
|
+
if (!values) {
|
|
282
|
+
values = /* @__PURE__ */ new Map();
|
|
283
|
+
this.acknowledgements.set(consumerId, values);
|
|
284
|
+
}
|
|
285
|
+
eventIds.forEach((id) => values.set(id, disposition));
|
|
286
|
+
}
|
|
287
|
+
subscribe(listener) {
|
|
288
|
+
this.listeners.add(listener);
|
|
289
|
+
return () => this.listeners.delete(listener);
|
|
290
|
+
}
|
|
291
|
+
async compact(options = {}) {
|
|
292
|
+
const cutoff = options.before ?? Number.POSITIVE_INFINITY;
|
|
293
|
+
const keep = options.keepLast ?? 1e3;
|
|
294
|
+
const removable = Math.max(0, this.events.length - keep);
|
|
295
|
+
this.events = this.events.filter((event, index) => index >= removable || event.timestamp >= cutoff);
|
|
296
|
+
const ids = new Set(this.events.map((event) => event.id));
|
|
297
|
+
for (const values of this.acknowledgements.values()) for (const id of values.keys()) if (!ids.has(id)) values.delete(id);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// src/presence/index.ts
|
|
302
|
+
init_registry();
|
|
303
|
+
|
|
255
304
|
// src/presence/wrap-tool-with-activity.ts
|
|
256
305
|
init_registry();
|
|
257
306
|
function wrapToolWithActivity(handler, options) {
|
|
@@ -272,7 +321,7 @@ function wrapToolWithActivity(handler, options) {
|
|
|
272
321
|
target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },
|
|
273
322
|
action: options.toolName,
|
|
274
323
|
timestamp: Date.now(),
|
|
275
|
-
meta: extractMeta(result),
|
|
324
|
+
meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),
|
|
276
325
|
ttlMs: options.ttlMs
|
|
277
326
|
});
|
|
278
327
|
return result;
|
|
@@ -286,6 +335,133 @@ function extractMeta(result) {
|
|
|
286
335
|
return void 0;
|
|
287
336
|
}
|
|
288
337
|
|
|
338
|
+
// src/presence/use-agent-activity.ts
|
|
339
|
+
init_registry();
|
|
340
|
+
function useAgentActivity(filter, options = {}) {
|
|
341
|
+
const cap = options.capacity ?? 50;
|
|
342
|
+
const [events, setEvents] = react.useState(() => fancyAutoCommon.readActivityHistory(filter).slice(-cap));
|
|
343
|
+
react.useEffect(() => {
|
|
344
|
+
setEvents(fancyAutoCommon.readActivityHistory(filter).slice(-cap));
|
|
345
|
+
return fancyAutoCommon.onActivity((event) => {
|
|
346
|
+
setEvents((prev) => {
|
|
347
|
+
const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice();
|
|
348
|
+
next.push(event);
|
|
349
|
+
return next;
|
|
350
|
+
});
|
|
351
|
+
}, filter);
|
|
352
|
+
}, [filter?.agentId, filter?.screenId, filter?.kind, cap]);
|
|
353
|
+
return { events, latest: events.length > 0 ? events[events.length - 1] : null };
|
|
354
|
+
}
|
|
355
|
+
function useAgentActivityForScreen(screenId, options = {}) {
|
|
356
|
+
const { events, latest } = useAgentActivity({ screenId }, options);
|
|
357
|
+
const fadeAfter = latest?.ttlMs ?? 1500;
|
|
358
|
+
const [isAgentActive, setActive] = react.useState(false);
|
|
359
|
+
react.useEffect(() => {
|
|
360
|
+
if (!latest) {
|
|
361
|
+
setActive(false);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
setActive(true);
|
|
365
|
+
const timer = setTimeout(() => setActive(false), fadeAfter);
|
|
366
|
+
return () => clearTimeout(timer);
|
|
367
|
+
}, [latest, fadeAfter]);
|
|
368
|
+
return { events, latest, isAgentActive };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/bridges/tui.ts
|
|
372
|
+
function registerTuiBridge(host, options) {
|
|
373
|
+
const disposers = [];
|
|
374
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
375
|
+
const pending = /* @__PURE__ */ new Map();
|
|
376
|
+
const defaultActor = options.actor ?? { kind: "agent", id: "agent", name: "Agent" };
|
|
377
|
+
const serverCapabilities = host.capabilities;
|
|
378
|
+
if (serverCapabilities) serverCapabilities.experimental = { ...serverCapabilities.experimental, "io.particle-academy/human-plus": { version: "1", push: true, durableCursor: true, stagedActions: true, wakesAgent: false } };
|
|
379
|
+
const register = (name, description, properties, required, handler) => {
|
|
380
|
+
disposers.push(host.registerTool({ name, description, inputSchema: { type: "object", properties, required } }, async (args, context) => {
|
|
381
|
+
try {
|
|
382
|
+
const result = await handler(args, context);
|
|
383
|
+
return textResult(typeof result === "string" ? result : JSON.stringify(result), result);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
return errorResult(error instanceof Error ? error.message : String(error));
|
|
386
|
+
}
|
|
387
|
+
}));
|
|
388
|
+
};
|
|
389
|
+
const publish = async (surfaceId, type, payload, actor = defaultActor, priority = "normal") => options.eventStore.append({ appId: options.appId, sessionId: options.sessionId, surfaceId, type, payload, actor, priority });
|
|
390
|
+
register("tui_surfaces_list", "List addressable Fancy TUI surfaces and commands.", {}, [], () => options.registry.list().map((surface) => ({ id: surface.id, kind: surface.kind, label: surface.label, commands: surface.commands?.map((command) => ({ name: command.name, description: command.description, policy: command.policy ?? "execute", inputSchema: command.inputSchema })) ?? [] })));
|
|
391
|
+
register("tui_surface_read", "Read a Fancy TUI surface by stable id.", { id: { type: "string" } }, ["id"], (args) => {
|
|
392
|
+
const surface = options.registry.get(String(args.id));
|
|
393
|
+
if (!surface) throw new Error(`Unknown TUI surface: ${String(args.id)}`);
|
|
394
|
+
return { id: surface.id, kind: surface.kind, state: surface.read() };
|
|
395
|
+
});
|
|
396
|
+
register("tui_action_invoke", "Invoke or propose an action on a Fancy TUI surface.", { surfaceId: { type: "string" }, command: { type: "string" }, input: { type: "object" } }, ["surfaceId", "command"], async (args) => {
|
|
397
|
+
const surfaceId = String(args.surfaceId);
|
|
398
|
+
const commandName = String(args.command);
|
|
399
|
+
const surface = options.registry.get(surfaceId);
|
|
400
|
+
if (!surface) throw new Error(`Unknown TUI surface: ${surfaceId}`);
|
|
401
|
+
const command = surface.commands?.find((value) => value.name === commandName);
|
|
402
|
+
if (!command) throw new Error(`Unknown command '${commandName}' on ${surfaceId}`);
|
|
403
|
+
const policy = command.policy ?? "execute";
|
|
404
|
+
if (policy === "observe" || policy === "human-only") throw new Error(`Action ${surfaceId}.${commandName} is ${policy}`);
|
|
405
|
+
const input = args.input && typeof args.input === "object" && !Array.isArray(args.input) ? args.input : {};
|
|
406
|
+
if (policy === "propose" || policy === "confirm") {
|
|
407
|
+
const id = globalThis.crypto.randomUUID();
|
|
408
|
+
pending.set(id, { id, surfaceId, command: commandName, input, actor: defaultActor });
|
|
409
|
+
await publish(surfaceId, "action.proposed", { id, command: commandName, input }, defaultActor, "attention");
|
|
410
|
+
return { pending: true, id };
|
|
411
|
+
}
|
|
412
|
+
const result = await command.invoke(input);
|
|
413
|
+
await publish(surfaceId, "action.executed", { command: commandName, input, result });
|
|
414
|
+
fancyAutoCommon.emitActivity({ agentId: defaultActor.id, agentName: defaultActor.name, target: { kind: "custom", elementId: surfaceId }, action: `tui_${commandName}`, timestamp: Date.now(), meta: { surfaceId } });
|
|
415
|
+
return { executed: true, result };
|
|
416
|
+
});
|
|
417
|
+
register("tui_action_confirm", "Confirm and execute a pending TUI action.", { id: { type: "string" } }, ["id"], async (args) => {
|
|
418
|
+
const id = String(args.id);
|
|
419
|
+
const value = pending.get(id);
|
|
420
|
+
if (!value) throw new Error(`Unknown pending action: ${id}`);
|
|
421
|
+
const surface = options.registry.get(value.surfaceId);
|
|
422
|
+
const command = surface?.commands?.find((x) => x.name === value.command);
|
|
423
|
+
if (!command) throw new Error("Pending action target is no longer available");
|
|
424
|
+
pending.delete(id);
|
|
425
|
+
const result = await command.invoke(value.input);
|
|
426
|
+
await publish(value.surfaceId, "action.confirmed", { id, command: value.command, result }, { kind: "human", id: "human" });
|
|
427
|
+
return { executed: true, result };
|
|
428
|
+
});
|
|
429
|
+
register("tui_action_reject", "Reject a pending TUI action.", { id: { type: "string" } }, ["id"], async (args) => {
|
|
430
|
+
const id = String(args.id);
|
|
431
|
+
const value = pending.get(id);
|
|
432
|
+
if (!value) throw new Error(`Unknown pending action: ${id}`);
|
|
433
|
+
pending.delete(id);
|
|
434
|
+
await publish(value.surfaceId, "action.rejected", { id, command: value.command }, { kind: "human", id: "human" });
|
|
435
|
+
return { rejected: true };
|
|
436
|
+
});
|
|
437
|
+
register("human_plus_events_list", "Pull durable Human+ events for a consumer.", { consumerId: { type: "string" }, after: { type: "number" }, limit: { type: "number" }, unacknowledgedOnly: { type: "boolean" } }, ["consumerId"], (args) => options.eventStore.list({ consumerId: String(args.consumerId), after: typeof args.after === "number" ? args.after : void 0, limit: typeof args.limit === "number" ? args.limit : void 0, unacknowledgedOnly: args.unacknowledgedOnly === true }));
|
|
438
|
+
register("human_plus_events_ack", "Acknowledge durable Human+ events.", { consumerId: { type: "string" }, eventIds: { type: "array", items: { type: "string" } }, disposition: { type: "string" } }, ["consumerId", "eventIds"], async (args) => {
|
|
439
|
+
const ids = Array.isArray(args.eventIds) ? args.eventIds.map(String) : [];
|
|
440
|
+
await options.eventStore.acknowledge(String(args.consumerId), ids, ["seen", "handled", "rejected", "ignored"].includes(String(args.disposition)) ? String(args.disposition) : "handled");
|
|
441
|
+
return { acknowledged: ids.length };
|
|
442
|
+
});
|
|
443
|
+
register("human_plus_events_subscribe", "Push matching Human+ events on this MCP transport while retaining them in the inbox.", { consumerId: { type: "string" }, types: { type: "array" }, surfaces: { type: "array" } }, ["consumerId"], (args, context) => {
|
|
444
|
+
if (!context?.transport) throw new Error("Subscriptions require an attached MCP transport");
|
|
445
|
+
subscriptions.set(context.transport, { consumerId: String(args.consumerId), types: Array.isArray(args.types) ? args.types.map(String) : void 0, surfaces: Array.isArray(args.surfaces) ? args.surfaces.map(String) : void 0 });
|
|
446
|
+
return { subscribed: true, delivery: "at-least-once", wakesAgent: false };
|
|
447
|
+
});
|
|
448
|
+
const unsubscribeStore = options.eventStore.subscribe((event) => {
|
|
449
|
+
const server = host;
|
|
450
|
+
if (typeof server.notify !== "function") return;
|
|
451
|
+
for (const [transport, subscription] of subscriptions) {
|
|
452
|
+
if (subscription.types?.length && !subscription.types.includes(event.type)) continue;
|
|
453
|
+
if (subscription.surfaces?.length && !subscription.surfaces.includes(event.surfaceId)) continue;
|
|
454
|
+
server.notify({ jsonrpc: "2.0", method: "notifications/human_plus/event", params: event }, transport);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
return { id: "tui", title: "Fancy TUI", pending: () => [...pending.values()], dispose: () => {
|
|
458
|
+
disposers.forEach((dispose) => dispose());
|
|
459
|
+
unsubscribeStore();
|
|
460
|
+
subscriptions.clear();
|
|
461
|
+
pending.clear();
|
|
462
|
+
} };
|
|
463
|
+
}
|
|
464
|
+
|
|
289
465
|
// src/undo/undo-tools.ts
|
|
290
466
|
var installedHosts = /* @__PURE__ */ new WeakSet();
|
|
291
467
|
function ensureUndoToolsRegistered(host, options = {}) {
|
|
@@ -367,6 +543,7 @@ var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
|
367
543
|
function registerFormBridge(host, options) {
|
|
368
544
|
const { adapter } = options;
|
|
369
545
|
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
546
|
+
const pendingMode = options.pendingMode ?? true;
|
|
370
547
|
const disposers = [];
|
|
371
548
|
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
372
549
|
const formId = adapter.id;
|
|
@@ -495,11 +672,20 @@ function registerFormBridge(host, options) {
|
|
|
495
672
|
);
|
|
496
673
|
reg(
|
|
497
674
|
"form_submit",
|
|
498
|
-
"Submit the form. Host returns ok + values (or an error).",
|
|
675
|
+
"Submit the form. Host returns ok + values (or an error). In pendingMode this is staged for the human to confirm first.",
|
|
499
676
|
{},
|
|
500
677
|
[],
|
|
501
678
|
async () => {
|
|
502
679
|
if (!adapter.submit) return errorResult("Host did not provide a submit implementation.");
|
|
680
|
+
if (pendingMode && adapter.confirm) {
|
|
681
|
+
const ok = await adapter.confirm({
|
|
682
|
+
action: "submit",
|
|
683
|
+
form: formId,
|
|
684
|
+
title: adapter.title,
|
|
685
|
+
values: adapter.getValues()
|
|
686
|
+
});
|
|
687
|
+
if (!ok) return errorResult("Declined by user");
|
|
688
|
+
}
|
|
503
689
|
const result = await adapter.submit();
|
|
504
690
|
if (!result.ok) return errorResult(result.error ?? "Submit failed");
|
|
505
691
|
return textResult("Submitted", { values: result.values });
|
|
@@ -863,6 +1049,7 @@ function useSheetsActivityHighlights(options = {}) {
|
|
|
863
1049
|
}
|
|
864
1050
|
|
|
865
1051
|
// src/bridges/code.ts
|
|
1052
|
+
var MAX_STREAM_CHARS = 1e4;
|
|
866
1053
|
var DEFAULT_AGENT3 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
867
1054
|
function registerCodeBridge(host, options) {
|
|
868
1055
|
const { adapter } = options;
|
|
@@ -875,6 +1062,16 @@ function registerCodeBridge(host, options) {
|
|
|
875
1062
|
elementId: adapter.id,
|
|
876
1063
|
label: adapter.title ?? adapter.id
|
|
877
1064
|
};
|
|
1065
|
+
const recordUndo = (action, label, prevValue, nextValue) => {
|
|
1066
|
+
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
1067
|
+
timestamp: Date.now(),
|
|
1068
|
+
bridgeId: "code",
|
|
1069
|
+
action,
|
|
1070
|
+
label,
|
|
1071
|
+
undo: () => adapter.setValue(prevValue),
|
|
1072
|
+
redo: () => adapter.setValue(nextValue)
|
|
1073
|
+
});
|
|
1074
|
+
};
|
|
878
1075
|
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
879
1076
|
const wrapped = async (args) => {
|
|
880
1077
|
try {
|
|
@@ -944,7 +1141,9 @@ function registerCodeBridge(host, options) {
|
|
|
944
1141
|
["value"],
|
|
945
1142
|
(args) => {
|
|
946
1143
|
const value = String(args.value ?? "");
|
|
1144
|
+
const prev = adapter.getValue();
|
|
947
1145
|
adapter.setValue(value);
|
|
1146
|
+
recordUndo("code_set_value", "Replace document", prev, value);
|
|
948
1147
|
return textResult(`Replaced document (${value.length} chars)`, { length: value.length });
|
|
949
1148
|
},
|
|
950
1149
|
true
|
|
@@ -956,8 +1155,10 @@ function registerCodeBridge(host, options) {
|
|
|
956
1155
|
["text"],
|
|
957
1156
|
(args) => {
|
|
958
1157
|
const text = String(args.text ?? "");
|
|
959
|
-
const
|
|
1158
|
+
const prev = adapter.getValue();
|
|
1159
|
+
const next = prev + text;
|
|
960
1160
|
adapter.setValue(next);
|
|
1161
|
+
recordUndo("code_append", `Append ${text.length} chars`, prev, next);
|
|
961
1162
|
return textResult(`Appended ${text.length} chars`, { length: next.length });
|
|
962
1163
|
},
|
|
963
1164
|
true
|
|
@@ -972,6 +1173,11 @@ function registerCodeBridge(host, options) {
|
|
|
972
1173
|
["text"],
|
|
973
1174
|
async (args) => {
|
|
974
1175
|
const text = String(args.text ?? "");
|
|
1176
|
+
if (text.length > MAX_STREAM_CHARS) {
|
|
1177
|
+
return errorResult(
|
|
1178
|
+
`Text too long to stream (${text.length} > ${MAX_STREAM_CHARS}). Use code_set_value / code_append for large content.`
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
975
1181
|
const cps = Math.max(1, Number(args.cps ?? 25));
|
|
976
1182
|
const interval = Math.max(8, Math.round(1e3 / cps));
|
|
977
1183
|
const start = adapter.getValue();
|
|
@@ -979,6 +1185,7 @@ function registerCodeBridge(host, options) {
|
|
|
979
1185
|
adapter.setValue(start + text.slice(0, i));
|
|
980
1186
|
if (i < text.length) await new Promise((r) => setTimeout(r, interval));
|
|
981
1187
|
}
|
|
1188
|
+
recordUndo("code_stream_append", `Append ${text.length} chars`, start, start + text);
|
|
982
1189
|
return textResult(`Streamed ${text.length} chars`, { length: text.length });
|
|
983
1190
|
},
|
|
984
1191
|
true
|
|
@@ -990,7 +1197,9 @@ function registerCodeBridge(host, options) {
|
|
|
990
1197
|
["text"],
|
|
991
1198
|
(args) => {
|
|
992
1199
|
if (!adapter.replaceSelection) return errorResult("Host did not provide replaceSelection.");
|
|
1200
|
+
const prev = adapter.getValue();
|
|
993
1201
|
adapter.replaceSelection(String(args.text ?? ""));
|
|
1202
|
+
recordUndo("code_replace_selection", "Replace selection", prev, adapter.getValue());
|
|
994
1203
|
return textResult("Selection replaced", {});
|
|
995
1204
|
},
|
|
996
1205
|
true
|
|
@@ -1003,7 +1212,18 @@ function registerCodeBridge(host, options) {
|
|
|
1003
1212
|
(args) => {
|
|
1004
1213
|
if (!adapter.setLanguage) return errorResult("Host did not provide setLanguage.");
|
|
1005
1214
|
const lang = String(args.language ?? "");
|
|
1215
|
+
const prevLang = adapter.getLanguage?.();
|
|
1006
1216
|
adapter.setLanguage(lang);
|
|
1217
|
+
if (prevLang !== void 0) {
|
|
1218
|
+
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
1219
|
+
timestamp: Date.now(),
|
|
1220
|
+
bridgeId: "code",
|
|
1221
|
+
action: "code_set_language",
|
|
1222
|
+
label: `Language \u2192 ${lang}`,
|
|
1223
|
+
undo: () => adapter.setLanguage?.(prevLang),
|
|
1224
|
+
redo: () => adapter.setLanguage?.(lang)
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1007
1227
|
return textResult(`Language \u2192 ${lang}`, { language: lang });
|
|
1008
1228
|
},
|
|
1009
1229
|
true
|
|
@@ -1018,7 +1238,8 @@ function registerCodeBridge(host, options) {
|
|
|
1018
1238
|
adapter.focus();
|
|
1019
1239
|
return textResult("Focused", {});
|
|
1020
1240
|
},
|
|
1021
|
-
|
|
1241
|
+
// Focus is not a document mutation — don't broadcast it as one or log undo.
|
|
1242
|
+
false
|
|
1022
1243
|
);
|
|
1023
1244
|
return {
|
|
1024
1245
|
id: `code:${adapter.id}`,
|
|
@@ -2289,6 +2510,22 @@ function registerMapBridge(host, options) {
|
|
|
2289
2510
|
|
|
2290
2511
|
// src/bridges/files.ts
|
|
2291
2512
|
var DEFAULT_AGENT9 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
2513
|
+
var MAX_SNAPSHOT_DEPTH = 8;
|
|
2514
|
+
function assertPathWithinRoot(root, path) {
|
|
2515
|
+
const segments = path.replace(/[\\/]+/g, "/").split("/");
|
|
2516
|
+
if (segments.includes("..")) {
|
|
2517
|
+
throw new Error(`Path may not contain '..': ${path}`);
|
|
2518
|
+
}
|
|
2519
|
+
const isAbsolute = /^([A-Za-z]:)?[\\/]/.test(path) || /^[A-Za-z]:/.test(path);
|
|
2520
|
+
if (isAbsolute) {
|
|
2521
|
+
const norm = (p2) => p2.replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
2522
|
+
const r = norm(root);
|
|
2523
|
+
const p = norm(path);
|
|
2524
|
+
if (p !== r && !p.startsWith(r + "/")) {
|
|
2525
|
+
throw new Error(`Path escapes the configured root: ${path}`);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2292
2529
|
var str3 = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
2293
2530
|
var num3 = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
2294
2531
|
var asPaths = (v) => {
|
|
@@ -2304,6 +2541,10 @@ function registerFilesBridge(host, options) {
|
|
|
2304
2541
|
const { adapter } = options;
|
|
2305
2542
|
const agent = { ...DEFAULT_AGENT9, ...options.agent ?? {} };
|
|
2306
2543
|
const disposers = [];
|
|
2544
|
+
const guard = (path) => {
|
|
2545
|
+
if (options.root !== void 0) assertPathWithinRoot(options.root, path);
|
|
2546
|
+
return path;
|
|
2547
|
+
};
|
|
2307
2548
|
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
2308
2549
|
const fTarget = (args, _result) => ({
|
|
2309
2550
|
kind: "files",
|
|
@@ -2359,7 +2600,7 @@ function registerFilesBridge(host, options) {
|
|
|
2359
2600
|
{ path: { type: "string", description: "Folder path to list; defaults to the current directory." } },
|
|
2360
2601
|
[],
|
|
2361
2602
|
async (args) => {
|
|
2362
|
-
const path = args.path !== void 0 ? str3(args.path) : adapter.getPath();
|
|
2603
|
+
const path = guard(args.path !== void 0 ? str3(args.path) : adapter.getPath());
|
|
2363
2604
|
const entries = await adapter.listChildren(path);
|
|
2364
2605
|
const summary = entries.map((e) => ({
|
|
2365
2606
|
path: e.path,
|
|
@@ -2378,7 +2619,7 @@ function registerFilesBridge(host, options) {
|
|
|
2378
2619
|
{ path: { type: "string" } },
|
|
2379
2620
|
["path"],
|
|
2380
2621
|
(args) => {
|
|
2381
|
-
const path = str3(args.path);
|
|
2622
|
+
const path = guard(str3(args.path));
|
|
2382
2623
|
const prev = adapter.getPath();
|
|
2383
2624
|
adapter.setPath(path);
|
|
2384
2625
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -2399,7 +2640,7 @@ function registerFilesBridge(host, options) {
|
|
|
2399
2640
|
{ path: { type: "string" } },
|
|
2400
2641
|
["path"],
|
|
2401
2642
|
(args) => {
|
|
2402
|
-
const path = str3(args.path);
|
|
2643
|
+
const path = guard(str3(args.path));
|
|
2403
2644
|
if (adapter.getExpanded().includes(path)) {
|
|
2404
2645
|
return textResult(`${path} already expanded`, { path, expanded: true });
|
|
2405
2646
|
}
|
|
@@ -2422,7 +2663,7 @@ function registerFilesBridge(host, options) {
|
|
|
2422
2663
|
{ path: { type: "string" } },
|
|
2423
2664
|
["path"],
|
|
2424
2665
|
(args) => {
|
|
2425
|
-
const path = str3(args.path);
|
|
2666
|
+
const path = guard(str3(args.path));
|
|
2426
2667
|
if (!adapter.getExpanded().includes(path)) {
|
|
2427
2668
|
return textResult(`${path} already collapsed`, { path, expanded: false });
|
|
2428
2669
|
}
|
|
@@ -2448,7 +2689,7 @@ function registerFilesBridge(host, options) {
|
|
|
2448
2689
|
},
|
|
2449
2690
|
[],
|
|
2450
2691
|
(args) => {
|
|
2451
|
-
const next = args.paths !== void 0 ? asPaths(args.paths) : asPaths(args.path);
|
|
2692
|
+
const next = (args.paths !== void 0 ? asPaths(args.paths) : asPaths(args.path)).map(guard);
|
|
2452
2693
|
const prev = adapter.getSelection();
|
|
2453
2694
|
adapter.setSelection(next);
|
|
2454
2695
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -2477,8 +2718,8 @@ function registerFilesBridge(host, options) {
|
|
|
2477
2718
|
if (!adapter.requestSnapshot) {
|
|
2478
2719
|
return errorResult("Host did not wire requestSnapshot (snapshot mode not enabled).");
|
|
2479
2720
|
}
|
|
2480
|
-
const path = args.path !== void 0 ? str3(args.path) : adapter.getPath();
|
|
2481
|
-
const depth = args.depth !== void 0 ? num3(args.depth) : void 0;
|
|
2721
|
+
const path = guard(args.path !== void 0 ? str3(args.path) : adapter.getPath());
|
|
2722
|
+
const depth = args.depth !== void 0 ? Math.max(0, Math.min(MAX_SNAPSHOT_DEPTH, num3(args.depth))) : void 0;
|
|
2482
2723
|
const tree = await adapter.requestSnapshot(path, depth);
|
|
2483
2724
|
const count = Array.isArray(tree) ? tree.length : 0;
|
|
2484
2725
|
return textResult(`Snapshot of ${path}: ${count} top-level entr${count === 1 ? "y" : "ies"}.`, {
|
|
@@ -2502,16 +2743,15 @@ var DEFAULT_AGENT10 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
|
2502
2743
|
var truncate = (s, n = 60) => s.length > n ? s.slice(0, n) + "\u2026" : s;
|
|
2503
2744
|
function registerTerminalBridge(host, options) {
|
|
2504
2745
|
const agent = { ...DEFAULT_AGENT10, ...options.agent ?? {} };
|
|
2505
|
-
const pendingMode = options.pendingMode ??
|
|
2746
|
+
const pendingMode = options.pendingMode ?? true;
|
|
2506
2747
|
const screenId = options.screenId ?? options.adapter?.screenId;
|
|
2507
2748
|
const disposers = [];
|
|
2508
2749
|
const staged = /* @__PURE__ */ new Map();
|
|
2509
2750
|
let seq = 0;
|
|
2510
2751
|
ensureUndoToolsRegistered(host);
|
|
2511
2752
|
const listTerminals = () => {
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
return [];
|
|
2753
|
+
const all = options.terminals ? options.terminals() : options.adapter ? [{ id: "terminal", label: "Terminal", active: true, ...options.adapter }] : [];
|
|
2754
|
+
return options.canAccess ? all.filter((t) => options.canAccess(t.id)) : all;
|
|
2515
2755
|
};
|
|
2516
2756
|
const resolve = (id) => {
|
|
2517
2757
|
const list = listTerminals();
|
|
@@ -2528,6 +2768,17 @@ function registerTerminalBridge(host, options) {
|
|
|
2528
2768
|
elementId: terminalId ?? screenId ?? "terminal",
|
|
2529
2769
|
label: label ?? "terminal"
|
|
2530
2770
|
});
|
|
2771
|
+
const redactMeta = (result) => {
|
|
2772
|
+
const sc = result.structuredContent;
|
|
2773
|
+
if (!sc || typeof sc !== "object") return void 0;
|
|
2774
|
+
return {
|
|
2775
|
+
terminal: sc.terminal,
|
|
2776
|
+
kind: sc.kind,
|
|
2777
|
+
length: typeof sc.data === "string" ? sc.data.length : void 0,
|
|
2778
|
+
executed: sc.executed,
|
|
2779
|
+
pending: sc.pending
|
|
2780
|
+
};
|
|
2781
|
+
};
|
|
2531
2782
|
const TERMINAL_ARG = {
|
|
2532
2783
|
terminal: {
|
|
2533
2784
|
type: "string",
|
|
@@ -2547,7 +2798,12 @@ function registerTerminalBridge(host, options) {
|
|
|
2547
2798
|
agent,
|
|
2548
2799
|
kind: "terminal",
|
|
2549
2800
|
screenId,
|
|
2550
|
-
|
|
2801
|
+
// A per-call resolver may return null to SKIP the broadcast (e.g. a
|
|
2802
|
+
// merely-staged command); only fall back to the default target when no
|
|
2803
|
+
// resolver was supplied.
|
|
2804
|
+
resolveTarget: ({ args, result }) => resolveTarget ? resolveTarget(args, result) : target(),
|
|
2805
|
+
// Never fan raw command/keystroke bytes to peers — broadcast shape only.
|
|
2806
|
+
buildMeta: redactMeta
|
|
2551
2807
|
}) : wrapped;
|
|
2552
2808
|
disposers.push(
|
|
2553
2809
|
host.registerTool(
|
|
@@ -2584,6 +2840,7 @@ function registerTerminalBridge(host, options) {
|
|
|
2584
2840
|
return textResult(`${kind === "run" ? "ran" : "wrote"} on ${t.id}: ${truncate(data)}`, {
|
|
2585
2841
|
kind,
|
|
2586
2842
|
data,
|
|
2843
|
+
length: data.length,
|
|
2587
2844
|
terminal: t.id,
|
|
2588
2845
|
executed: true
|
|
2589
2846
|
});
|
|
@@ -2594,7 +2851,7 @@ function registerTerminalBridge(host, options) {
|
|
|
2594
2851
|
options.onPending?.(entry);
|
|
2595
2852
|
return textResult(
|
|
2596
2853
|
`Staged ${kind} on ${t.id} (id ${id}) \u2014 awaiting human confirmation: ${truncate(data)}`,
|
|
2597
|
-
{ ...entry, pending: true }
|
|
2854
|
+
{ ...entry, length: data.length, pending: true }
|
|
2598
2855
|
);
|
|
2599
2856
|
}
|
|
2600
2857
|
reg(
|
|
@@ -2639,12 +2896,13 @@ function registerTerminalBridge(host, options) {
|
|
|
2639
2896
|
);
|
|
2640
2897
|
reg(
|
|
2641
2898
|
"terminal_write",
|
|
2642
|
-
"Write raw data / keystrokes to a terminal (input, control chars, ANSI). Pass `terminal` to target a specific one. In pendingMode this stages instead of executing.",
|
|
2899
|
+
"Write raw data / keystrokes to a terminal (input, control chars, ANSI). Pass `terminal` to target a specific one. In pendingMode this stages instead of executing. NOTE: bytes are written verbatim \u2014 an embedded CR runs a command, and ANSI/OSC sequences reach the terminal; treat this exactly like terminal_run for gating.",
|
|
2643
2900
|
{ ...TERMINAL_ARG, data: { type: "string", description: "Raw bytes to write." } },
|
|
2644
2901
|
["data"],
|
|
2645
2902
|
(args) => stageOrExec(need(args), "write", String(args.data)),
|
|
2646
2903
|
true,
|
|
2647
|
-
|
|
2904
|
+
// Skip the peer broadcast while only staged; emit once it actually executes.
|
|
2905
|
+
(args, result) => result?.structuredContent?.pending ? null : target(`write:${String(args.terminal ?? "")}`, resolve(args.terminal)?.id)
|
|
2648
2906
|
);
|
|
2649
2907
|
reg(
|
|
2650
2908
|
"terminal_run",
|
|
@@ -2653,41 +2911,47 @@ function registerTerminalBridge(host, options) {
|
|
|
2653
2911
|
["command"],
|
|
2654
2912
|
(args) => stageOrExec(need(args), "run", String(args.command)),
|
|
2655
2913
|
true,
|
|
2656
|
-
(args) => target(truncate(String(args.command ?? "")), resolve(args.terminal)?.id)
|
|
2657
|
-
);
|
|
2658
|
-
reg(
|
|
2659
|
-
"terminal_confirm",
|
|
2660
|
-
"Confirm + execute a staged command by id (pendingMode).",
|
|
2661
|
-
{ id: { type: "string" } },
|
|
2662
|
-
["id"],
|
|
2663
|
-
async (args) => {
|
|
2664
|
-
const id = String(args.id);
|
|
2665
|
-
const entry = staged.get(id);
|
|
2666
|
-
if (!entry) return errorResult(`No staged command ${id}`);
|
|
2667
|
-
const t = resolve(entry.terminalId);
|
|
2668
|
-
if (!t) return errorResult(`Terminal '${entry.terminalId}' is gone \u2014 cannot run ${id}`);
|
|
2669
|
-
staged.delete(id);
|
|
2670
|
-
await exec(t, entry.kind, entry.data);
|
|
2671
|
-
return textResult(`Confirmed ${id}: ${entry.kind} on ${t.id} ${truncate(entry.data)}`, { ...entry, executed: true });
|
|
2672
|
-
},
|
|
2673
|
-
true,
|
|
2674
|
-
(args) => {
|
|
2675
|
-
const e = staged.get(String(args.id));
|
|
2676
|
-
return target(`confirm:${String(args.id ?? "")}`, e?.terminalId);
|
|
2677
|
-
}
|
|
2678
|
-
);
|
|
2679
|
-
reg(
|
|
2680
|
-
"terminal_reject",
|
|
2681
|
-
"Drop a staged command by id without executing it.",
|
|
2682
|
-
{ id: { type: "string" } },
|
|
2683
|
-
["id"],
|
|
2684
|
-
(args) => {
|
|
2685
|
-
const id = String(args.id);
|
|
2686
|
-
if (!staged.delete(id)) return errorResult(`No staged command ${id}`);
|
|
2687
|
-
return textResult(`Rejected ${id}`, { id, rejected: true });
|
|
2688
|
-
},
|
|
2689
|
-
false
|
|
2914
|
+
(args, result) => result?.structuredContent?.pending ? null : target(truncate(String(args.command ?? "")), resolve(args.terminal)?.id)
|
|
2690
2915
|
);
|
|
2916
|
+
if (options.allowAgentConfirm) {
|
|
2917
|
+
reg(
|
|
2918
|
+
"terminal_confirm",
|
|
2919
|
+
"Confirm + execute a staged command by id (pendingMode).",
|
|
2920
|
+
{ id: { type: "string" } },
|
|
2921
|
+
["id"],
|
|
2922
|
+
async (args) => {
|
|
2923
|
+
const id = String(args.id);
|
|
2924
|
+
const entry = staged.get(id);
|
|
2925
|
+
if (!entry) return errorResult(`No staged command ${id}`);
|
|
2926
|
+
const t = resolve(entry.terminalId);
|
|
2927
|
+
if (!t) return errorResult(`Terminal '${entry.terminalId}' is gone \u2014 cannot run ${id}`);
|
|
2928
|
+
staged.delete(id);
|
|
2929
|
+
await exec(t, entry.kind, entry.data);
|
|
2930
|
+
return textResult(`Confirmed ${id}: ${entry.kind} on ${t.id} ${truncate(entry.data)}`, {
|
|
2931
|
+
...entry,
|
|
2932
|
+
length: entry.data.length,
|
|
2933
|
+
executed: true
|
|
2934
|
+
});
|
|
2935
|
+
},
|
|
2936
|
+
true,
|
|
2937
|
+
(args) => {
|
|
2938
|
+
const e = staged.get(String(args.id));
|
|
2939
|
+
return target(`confirm:${String(args.id ?? "")}`, e?.terminalId);
|
|
2940
|
+
}
|
|
2941
|
+
);
|
|
2942
|
+
reg(
|
|
2943
|
+
"terminal_reject",
|
|
2944
|
+
"Drop a staged command by id without executing it.",
|
|
2945
|
+
{ id: { type: "string" } },
|
|
2946
|
+
["id"],
|
|
2947
|
+
(args) => {
|
|
2948
|
+
const id = String(args.id);
|
|
2949
|
+
if (!staged.delete(id)) return errorResult(`No staged command ${id}`);
|
|
2950
|
+
return textResult(`Rejected ${id}`, { id, rejected: true });
|
|
2951
|
+
},
|
|
2952
|
+
false
|
|
2953
|
+
);
|
|
2954
|
+
}
|
|
2691
2955
|
if (canClear) {
|
|
2692
2956
|
reg(
|
|
2693
2957
|
"terminal_clear",
|
|
@@ -2732,8 +2996,10 @@ function registerTerminalBridge(host, options) {
|
|
|
2732
2996
|
if (!t.setShell) return errorResult(`Terminal '${t.id}' can't switch shells.`);
|
|
2733
2997
|
const id = String(args.id);
|
|
2734
2998
|
const shells = t.listShells?.();
|
|
2735
|
-
if (shells
|
|
2736
|
-
return errorResult(
|
|
2999
|
+
if (!shells || !shells.some((s) => s.id === id)) {
|
|
3000
|
+
return errorResult(
|
|
3001
|
+
`Unknown or unlisted shell '${id}' for ${t.id}. Call terminal_list_shells for valid ids.`
|
|
3002
|
+
);
|
|
2737
3003
|
}
|
|
2738
3004
|
await t.setShell(id);
|
|
2739
3005
|
return textResult(`Switched ${t.id} shell to ${id}`, { shell: id, terminal: t.id });
|
|
@@ -2874,6 +3140,12 @@ function registerNavigationBridge(host, options) {
|
|
|
2874
3140
|
if (stale) return stale;
|
|
2875
3141
|
const url = String(args.url ?? "");
|
|
2876
3142
|
if (!url) return errorResult("url is required");
|
|
3143
|
+
const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase();
|
|
3144
|
+
if (scheme && scheme !== "http" && scheme !== "https") {
|
|
3145
|
+
return errorResult(
|
|
3146
|
+
`Refusing to navigate to a '${scheme}:' URL. Only http(s) URLs or same-site paths are allowed.`
|
|
3147
|
+
);
|
|
3148
|
+
}
|
|
2877
3149
|
const from = adapter.getLocation().url;
|
|
2878
3150
|
await adapter.visit(url);
|
|
2879
3151
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -3858,8 +4130,7 @@ function describeSession(id, token) {
|
|
|
3858
4130
|
}
|
|
3859
4131
|
function buildShareUrl(descriptor, baseUrl = typeof window !== "undefined" ? window.location.href.split("?")[0] : "") {
|
|
3860
4132
|
const u = new URL(baseUrl);
|
|
3861
|
-
u.
|
|
3862
|
-
u.searchParams.set("token", descriptor.token);
|
|
4133
|
+
u.hash = new URLSearchParams({ session: descriptor.id, token: descriptor.token }).toString();
|
|
3863
4134
|
return u.toString();
|
|
3864
4135
|
}
|
|
3865
4136
|
function buildShareConfig(descriptor, transport = "broadcast-channel") {
|
|
@@ -3874,9 +4145,10 @@ function buildShareConfig(descriptor, transport = "broadcast-channel") {
|
|
|
3874
4145
|
}
|
|
3875
4146
|
function readSessionFromUrl() {
|
|
3876
4147
|
if (typeof window === "undefined") return null;
|
|
3877
|
-
const
|
|
3878
|
-
const
|
|
3879
|
-
const
|
|
4148
|
+
const url = new URL(window.location.href);
|
|
4149
|
+
const frag = new URLSearchParams(url.hash.replace(/^#/, ""));
|
|
4150
|
+
const id = frag.get("session") ?? url.searchParams.get("session");
|
|
4151
|
+
const token = frag.get("token") ?? url.searchParams.get("token");
|
|
3880
4152
|
if (!id || !token) return null;
|
|
3881
4153
|
return describeSession(id, token);
|
|
3882
4154
|
}
|
|
@@ -4568,39 +4840,6 @@ function buildCurlRecipe(session) {
|
|
|
4568
4840
|
].join("\n");
|
|
4569
4841
|
}
|
|
4570
4842
|
|
|
4571
|
-
// src/presence/use-agent-activity.ts
|
|
4572
|
-
init_registry();
|
|
4573
|
-
function useAgentActivity(filter, options = {}) {
|
|
4574
|
-
const cap = options.capacity ?? 50;
|
|
4575
|
-
const [events, setEvents] = react.useState(() => fancyAutoCommon.readActivityHistory(filter).slice(-cap));
|
|
4576
|
-
react.useEffect(() => {
|
|
4577
|
-
setEvents(fancyAutoCommon.readActivityHistory(filter).slice(-cap));
|
|
4578
|
-
return fancyAutoCommon.onActivity((event) => {
|
|
4579
|
-
setEvents((prev) => {
|
|
4580
|
-
const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice();
|
|
4581
|
-
next.push(event);
|
|
4582
|
-
return next;
|
|
4583
|
-
});
|
|
4584
|
-
}, filter);
|
|
4585
|
-
}, [filter?.agentId, filter?.screenId, filter?.kind, cap]);
|
|
4586
|
-
return { events, latest: events.length > 0 ? events[events.length - 1] : null };
|
|
4587
|
-
}
|
|
4588
|
-
function useAgentActivityForScreen(screenId, options = {}) {
|
|
4589
|
-
const { events, latest } = useAgentActivity({ screenId }, options);
|
|
4590
|
-
const fadeAfter = latest?.ttlMs ?? 1500;
|
|
4591
|
-
const [isAgentActive, setActive] = react.useState(false);
|
|
4592
|
-
react.useEffect(() => {
|
|
4593
|
-
if (!latest) {
|
|
4594
|
-
setActive(false);
|
|
4595
|
-
return;
|
|
4596
|
-
}
|
|
4597
|
-
setActive(true);
|
|
4598
|
-
const timer = setTimeout(() => setActive(false), fadeAfter);
|
|
4599
|
-
return () => clearTimeout(timer);
|
|
4600
|
-
}, [latest, fadeAfter]);
|
|
4601
|
-
return { events, latest, isAgentActive };
|
|
4602
|
-
}
|
|
4603
|
-
|
|
4604
4843
|
// src/connectors/targets.ts
|
|
4605
4844
|
var CLAUDE_CONNECTORS_URL = "https://claude.ai/settings/connectors";
|
|
4606
4845
|
function encodeBase64Json(value) {
|
|
@@ -5059,9 +5298,6 @@ function SimulateUsersButton({
|
|
|
5059
5298
|
);
|
|
5060
5299
|
}
|
|
5061
5300
|
|
|
5062
|
-
// src/presence/index.ts
|
|
5063
|
-
init_registry();
|
|
5064
|
-
|
|
5065
5301
|
// src/heuristics/sink.ts
|
|
5066
5302
|
function numericMeta(meta, key) {
|
|
5067
5303
|
const v = meta?.[key];
|
|
@@ -5302,6 +5538,7 @@ exports.InProcessTransport = InProcessTransport;
|
|
|
5302
5538
|
exports.MCPB_MANIFEST_VERSION = MCPB_MANIFEST_VERSION;
|
|
5303
5539
|
exports.MCPB_MIN_NODE = MCPB_MIN_NODE;
|
|
5304
5540
|
exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
|
|
5541
|
+
exports.MemoryHumanPlusEventStore = MemoryHumanPlusEventStore;
|
|
5305
5542
|
exports.MicroMcpServer = MicroMcpServer;
|
|
5306
5543
|
exports.RelayTransport = RelayTransport;
|
|
5307
5544
|
exports.ScreensActivityBridge = ScreensActivityBridge;
|
|
@@ -5348,6 +5585,7 @@ exports.registerScreensBridge = registerScreensBridge;
|
|
|
5348
5585
|
exports.registerSheetsBridge = registerSheetsBridge;
|
|
5349
5586
|
exports.registerSlidesBridge = registerSlidesBridge;
|
|
5350
5587
|
exports.registerTerminalBridge = registerTerminalBridge;
|
|
5588
|
+
exports.registerTuiBridge = registerTuiBridge;
|
|
5351
5589
|
exports.registerUndoTools = registerUndoTools;
|
|
5352
5590
|
exports.rpcError = rpcError;
|
|
5353
5591
|
exports.slugifyServerName = slugifyServerName;
|