@particle-academy/agent-integrations 0.29.0 → 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 +30 -1
- package/dist/bridges/files.d.cts +20 -1
- package/dist/bridges/files.d.ts +20 -1
- package/dist/bridges/forms.d.cts +22 -1
- package/dist/bridges/forms.d.ts +22 -1
- package/dist/bridges/terminal.d.cts +21 -1
- package/dist/bridges/terminal.d.ts +21 -1
- package/dist/bridges-artboard.cjs +1 -1
- package/dist/bridges-artboard.cjs.map +1 -1
- package/dist/bridges-artboard.js +1 -1
- package/dist/bridges-catalog.cjs +1 -1
- package/dist/bridges-catalog.cjs.map +1 -1
- package/dist/bridges-catalog.js +2 -2
- package/dist/bridges-charts.cjs +1 -1
- package/dist/bridges-charts.cjs.map +1 -1
- package/dist/bridges-charts.js +2 -2
- package/dist/bridges-cms.cjs +1 -1
- package/dist/bridges-cms.cjs.map +1 -1
- package/dist/bridges-cms.js +3 -3
- package/dist/bridges-code.cjs +38 -3
- package/dist/bridges-code.cjs.map +1 -1
- package/dist/bridges-code.js +2 -2
- package/dist/bridges-doc.cjs +1 -1
- package/dist/bridges-doc.cjs.map +1 -1
- package/dist/bridges-doc.js +2 -2
- package/dist/bridges-features.cjs +1 -1
- package/dist/bridges-features.cjs.map +1 -1
- package/dist/bridges-features.js +2 -2
- package/dist/bridges-files.cjs +29 -8
- package/dist/bridges-files.cjs.map +1 -1
- package/dist/bridges-files.js +2 -2
- package/dist/bridges-flow.cjs +1 -1
- package/dist/bridges-flow.cjs.map +1 -1
- package/dist/bridges-flow.js +1 -1
- package/dist/bridges-forms.cjs +12 -2
- package/dist/bridges-forms.cjs.map +1 -1
- package/dist/bridges-forms.js +2 -2
- package/dist/bridges-map.cjs +1 -1
- package/dist/bridges-map.cjs.map +1 -1
- package/dist/bridges-map.js +2 -2
- package/dist/bridges-scene.cjs +1 -1
- package/dist/bridges-scene.cjs.map +1 -1
- package/dist/bridges-scene.js +2 -2
- package/dist/bridges-screens.cjs +1 -1
- package/dist/bridges-screens.cjs.map +1 -1
- package/dist/bridges-screens.js +2 -2
- package/dist/bridges-sheets.cjs +1 -1
- package/dist/bridges-sheets.cjs.map +1 -1
- package/dist/bridges-sheets.js +2 -2
- package/dist/bridges-slides.cjs +1 -1
- package/dist/bridges-slides.cjs.map +1 -1
- package/dist/bridges-slides.js +2 -2
- package/dist/bridges-terminal.cjs +70 -45
- package/dist/bridges-terminal.cjs.map +1 -1
- package/dist/bridges-terminal.js +2 -2
- package/dist/bridges-tui.js +1 -1
- package/dist/bridges-whiteboard.cjs +1 -1
- package/dist/bridges-whiteboard.cjs.map +1 -1
- package/dist/bridges-whiteboard.js +2 -2
- package/dist/{chunk-OFCR6JY3.js → chunk-2U5JB6ZA.js} +3 -3
- package/dist/{chunk-OFCR6JY3.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-BE2XS577.js → chunk-6YUBWQTS.js} +3 -3
- package/dist/{chunk-BE2XS577.js.map → chunk-6YUBWQTS.js.map} +1 -1
- package/dist/{chunk-OHUDEA6R.js → chunk-BHQJJ2YG.js} +3 -3
- package/dist/{chunk-OHUDEA6R.js.map → chunk-BHQJJ2YG.js.map} +1 -1
- package/dist/{chunk-LZJI7EKQ.js → chunk-CA7GIJJC.js} +3 -3
- package/dist/{chunk-LZJI7EKQ.js.map → chunk-CA7GIJJC.js.map} +1 -1
- package/dist/{chunk-7VBB2FJB.js → chunk-DKSR4R4O.js} +3 -3
- package/dist/{chunk-7VBB2FJB.js.map → chunk-DKSR4R4O.js.map} +1 -1
- package/dist/{chunk-2Y7Y5PTR.js → chunk-G5EZNWXJ.js} +3 -3
- package/dist/{chunk-2Y7Y5PTR.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-VMCG3PJD.js → chunk-HRHC5GM5.js} +31 -11
- 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-AF454WRD.js → chunk-LQZKRBWR.js} +3 -3
- package/dist/{chunk-AF454WRD.js.map → chunk-LQZKRBWR.js.map} +1 -1
- package/dist/{chunk-KMSRG5KH.js → chunk-N6RLSLOF.js} +7 -7
- package/dist/chunk-N6RLSLOF.js.map +1 -0
- package/dist/{chunk-NSPL5TVF.js → chunk-OAFJ53X3.js} +3 -3
- package/dist/{chunk-NSPL5TVF.js.map → chunk-OAFJ53X3.js.map} +1 -1
- package/dist/{chunk-3PKHJ4IV.js → chunk-PR4SXFZH.js} +3 -3
- package/dist/{chunk-3PKHJ4IV.js.map → chunk-PR4SXFZH.js.map} +1 -1
- package/dist/{chunk-KYMEDV6E.js → chunk-RIXYCFDO.js} +41 -6
- package/dist/chunk-RIXYCFDO.js.map +1 -0
- package/dist/{chunk-TNU6AWUO.js → chunk-USQGA672.js} +14 -4
- package/dist/chunk-USQGA672.js.map +1 -0
- package/dist/{chunk-KKGC4ERF.js → chunk-UZHNKUER.js} +72 -47
- package/dist/chunk-UZHNKUER.js.map +1 -0
- package/dist/{chunk-UTUVN6HQ.js → chunk-WCKSDFTM.js} +3 -3
- package/dist/{chunk-UTUVN6HQ.js.map → chunk-WCKSDFTM.js.map} +1 -1
- package/dist/{chunk-HFLHLS2N.js → chunk-ZNGTC4CO.js} +3 -3
- package/dist/{chunk-HFLHLS2N.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 +2 -3
- package/dist/components-shared-whiteboard.cjs.map +1 -1
- package/dist/components-shared-whiteboard.js +4 -4
- package/dist/heuristics.js +1 -1
- package/dist/index.cjs +156 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +27 -21
- package/dist/index.js.map +1 -1
- package/dist/presence/index.d.cts +7 -0
- package/dist/presence/index.d.ts +7 -0
- 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/sharing/index.d.cts +1 -1
- package/dist/sharing/index.d.ts +1 -1
- package/dist/sharing.cjs +5 -5
- package/dist/sharing.cjs.map +1 -1
- package/dist/sharing.js +1 -1
- package/dist/{token-CrJF76oH.d.ts → token-BZ8doMam.d.cts} +15 -2
- package/dist/{token-CrJF76oH.d.cts → token-BZ8doMam.d.ts} +15 -2
- package/package.json +1 -1
- package/dist/chunk-KKGC4ERF.js.map +0 -1
- package/dist/chunk-KMSRG5KH.js.map +0 -1
- package/dist/chunk-KYMEDV6E.js.map +0 -1
- package/dist/chunk-OIX2ANFS.js.map +0 -1
- package/dist/chunk-TNU6AWUO.js.map +0 -1
- package/dist/chunk-ULJL53DL.js.map +0 -1
- package/dist/chunk-VMCG3PJD.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-
|
|
2
|
-
import { createSessionDescriptor, attachSseRelay } from './chunk-
|
|
1
|
+
import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-HSI5RVF4.js';
|
|
2
|
+
import { createSessionDescriptor, attachSseRelay } from './chunk-N6RLSLOF.js';
|
|
3
3
|
import { attachInProcess } from './chunk-AFUULW5E.js';
|
|
4
|
-
import { registerWhiteboardBridge } from './chunk-
|
|
4
|
+
import { registerWhiteboardBridge } from './chunk-2U5JB6ZA.js';
|
|
5
5
|
import './chunk-CT3LRNH7.js';
|
|
6
|
-
import './chunk-
|
|
6
|
+
import './chunk-2VENWGLZ.js';
|
|
7
7
|
import './chunk-C3TYI5TJ.js';
|
|
8
8
|
import { MicroMcpServer } from './chunk-DF3PLL2E.js';
|
|
9
9
|
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
package/dist/heuristics.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { attachHeuristicsSink, mapActivityToEvent } from './chunk-7R6RMROL.js';
|
|
2
2
|
import './chunk-UCKJAUBY.js';
|
|
3
|
-
import './chunk-
|
|
3
|
+
import './chunk-2VENWGLZ.js';
|
|
4
4
|
import './chunk-C3TYI5TJ.js';
|
|
5
5
|
//# sourceMappingURL=heuristics.js.map
|
|
6
6
|
//# sourceMappingURL=heuristics.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -321,7 +321,7 @@ function wrapToolWithActivity(handler, options) {
|
|
|
321
321
|
target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },
|
|
322
322
|
action: options.toolName,
|
|
323
323
|
timestamp: Date.now(),
|
|
324
|
-
meta: extractMeta(result),
|
|
324
|
+
meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),
|
|
325
325
|
ttlMs: options.ttlMs
|
|
326
326
|
});
|
|
327
327
|
return result;
|
|
@@ -543,6 +543,7 @@ var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
|
543
543
|
function registerFormBridge(host, options) {
|
|
544
544
|
const { adapter } = options;
|
|
545
545
|
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
546
|
+
const pendingMode = options.pendingMode ?? true;
|
|
546
547
|
const disposers = [];
|
|
547
548
|
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
548
549
|
const formId = adapter.id;
|
|
@@ -671,11 +672,20 @@ function registerFormBridge(host, options) {
|
|
|
671
672
|
);
|
|
672
673
|
reg(
|
|
673
674
|
"form_submit",
|
|
674
|
-
"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.",
|
|
675
676
|
{},
|
|
676
677
|
[],
|
|
677
678
|
async () => {
|
|
678
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
|
+
}
|
|
679
689
|
const result = await adapter.submit();
|
|
680
690
|
if (!result.ok) return errorResult(result.error ?? "Submit failed");
|
|
681
691
|
return textResult("Submitted", { values: result.values });
|
|
@@ -1039,6 +1049,7 @@ function useSheetsActivityHighlights(options = {}) {
|
|
|
1039
1049
|
}
|
|
1040
1050
|
|
|
1041
1051
|
// src/bridges/code.ts
|
|
1052
|
+
var MAX_STREAM_CHARS = 1e4;
|
|
1042
1053
|
var DEFAULT_AGENT3 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1043
1054
|
function registerCodeBridge(host, options) {
|
|
1044
1055
|
const { adapter } = options;
|
|
@@ -1051,6 +1062,16 @@ function registerCodeBridge(host, options) {
|
|
|
1051
1062
|
elementId: adapter.id,
|
|
1052
1063
|
label: adapter.title ?? adapter.id
|
|
1053
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
|
+
};
|
|
1054
1075
|
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
1055
1076
|
const wrapped = async (args) => {
|
|
1056
1077
|
try {
|
|
@@ -1120,7 +1141,9 @@ function registerCodeBridge(host, options) {
|
|
|
1120
1141
|
["value"],
|
|
1121
1142
|
(args) => {
|
|
1122
1143
|
const value = String(args.value ?? "");
|
|
1144
|
+
const prev = adapter.getValue();
|
|
1123
1145
|
adapter.setValue(value);
|
|
1146
|
+
recordUndo("code_set_value", "Replace document", prev, value);
|
|
1124
1147
|
return textResult(`Replaced document (${value.length} chars)`, { length: value.length });
|
|
1125
1148
|
},
|
|
1126
1149
|
true
|
|
@@ -1132,8 +1155,10 @@ function registerCodeBridge(host, options) {
|
|
|
1132
1155
|
["text"],
|
|
1133
1156
|
(args) => {
|
|
1134
1157
|
const text = String(args.text ?? "");
|
|
1135
|
-
const
|
|
1158
|
+
const prev = adapter.getValue();
|
|
1159
|
+
const next = prev + text;
|
|
1136
1160
|
adapter.setValue(next);
|
|
1161
|
+
recordUndo("code_append", `Append ${text.length} chars`, prev, next);
|
|
1137
1162
|
return textResult(`Appended ${text.length} chars`, { length: next.length });
|
|
1138
1163
|
},
|
|
1139
1164
|
true
|
|
@@ -1148,6 +1173,11 @@ function registerCodeBridge(host, options) {
|
|
|
1148
1173
|
["text"],
|
|
1149
1174
|
async (args) => {
|
|
1150
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
|
+
}
|
|
1151
1181
|
const cps = Math.max(1, Number(args.cps ?? 25));
|
|
1152
1182
|
const interval = Math.max(8, Math.round(1e3 / cps));
|
|
1153
1183
|
const start = adapter.getValue();
|
|
@@ -1155,6 +1185,7 @@ function registerCodeBridge(host, options) {
|
|
|
1155
1185
|
adapter.setValue(start + text.slice(0, i));
|
|
1156
1186
|
if (i < text.length) await new Promise((r) => setTimeout(r, interval));
|
|
1157
1187
|
}
|
|
1188
|
+
recordUndo("code_stream_append", `Append ${text.length} chars`, start, start + text);
|
|
1158
1189
|
return textResult(`Streamed ${text.length} chars`, { length: text.length });
|
|
1159
1190
|
},
|
|
1160
1191
|
true
|
|
@@ -1166,7 +1197,9 @@ function registerCodeBridge(host, options) {
|
|
|
1166
1197
|
["text"],
|
|
1167
1198
|
(args) => {
|
|
1168
1199
|
if (!adapter.replaceSelection) return errorResult("Host did not provide replaceSelection.");
|
|
1200
|
+
const prev = adapter.getValue();
|
|
1169
1201
|
adapter.replaceSelection(String(args.text ?? ""));
|
|
1202
|
+
recordUndo("code_replace_selection", "Replace selection", prev, adapter.getValue());
|
|
1170
1203
|
return textResult("Selection replaced", {});
|
|
1171
1204
|
},
|
|
1172
1205
|
true
|
|
@@ -1179,7 +1212,18 @@ function registerCodeBridge(host, options) {
|
|
|
1179
1212
|
(args) => {
|
|
1180
1213
|
if (!adapter.setLanguage) return errorResult("Host did not provide setLanguage.");
|
|
1181
1214
|
const lang = String(args.language ?? "");
|
|
1215
|
+
const prevLang = adapter.getLanguage?.();
|
|
1182
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
|
+
}
|
|
1183
1227
|
return textResult(`Language \u2192 ${lang}`, { language: lang });
|
|
1184
1228
|
},
|
|
1185
1229
|
true
|
|
@@ -1194,7 +1238,8 @@ function registerCodeBridge(host, options) {
|
|
|
1194
1238
|
adapter.focus();
|
|
1195
1239
|
return textResult("Focused", {});
|
|
1196
1240
|
},
|
|
1197
|
-
|
|
1241
|
+
// Focus is not a document mutation — don't broadcast it as one or log undo.
|
|
1242
|
+
false
|
|
1198
1243
|
);
|
|
1199
1244
|
return {
|
|
1200
1245
|
id: `code:${adapter.id}`,
|
|
@@ -2465,6 +2510,22 @@ function registerMapBridge(host, options) {
|
|
|
2465
2510
|
|
|
2466
2511
|
// src/bridges/files.ts
|
|
2467
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
|
+
}
|
|
2468
2529
|
var str3 = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
2469
2530
|
var num3 = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
2470
2531
|
var asPaths = (v) => {
|
|
@@ -2480,6 +2541,10 @@ function registerFilesBridge(host, options) {
|
|
|
2480
2541
|
const { adapter } = options;
|
|
2481
2542
|
const agent = { ...DEFAULT_AGENT9, ...options.agent ?? {} };
|
|
2482
2543
|
const disposers = [];
|
|
2544
|
+
const guard = (path) => {
|
|
2545
|
+
if (options.root !== void 0) assertPathWithinRoot(options.root, path);
|
|
2546
|
+
return path;
|
|
2547
|
+
};
|
|
2483
2548
|
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
2484
2549
|
const fTarget = (args, _result) => ({
|
|
2485
2550
|
kind: "files",
|
|
@@ -2535,7 +2600,7 @@ function registerFilesBridge(host, options) {
|
|
|
2535
2600
|
{ path: { type: "string", description: "Folder path to list; defaults to the current directory." } },
|
|
2536
2601
|
[],
|
|
2537
2602
|
async (args) => {
|
|
2538
|
-
const path = args.path !== void 0 ? str3(args.path) : adapter.getPath();
|
|
2603
|
+
const path = guard(args.path !== void 0 ? str3(args.path) : adapter.getPath());
|
|
2539
2604
|
const entries = await adapter.listChildren(path);
|
|
2540
2605
|
const summary = entries.map((e) => ({
|
|
2541
2606
|
path: e.path,
|
|
@@ -2554,7 +2619,7 @@ function registerFilesBridge(host, options) {
|
|
|
2554
2619
|
{ path: { type: "string" } },
|
|
2555
2620
|
["path"],
|
|
2556
2621
|
(args) => {
|
|
2557
|
-
const path = str3(args.path);
|
|
2622
|
+
const path = guard(str3(args.path));
|
|
2558
2623
|
const prev = adapter.getPath();
|
|
2559
2624
|
adapter.setPath(path);
|
|
2560
2625
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -2575,7 +2640,7 @@ function registerFilesBridge(host, options) {
|
|
|
2575
2640
|
{ path: { type: "string" } },
|
|
2576
2641
|
["path"],
|
|
2577
2642
|
(args) => {
|
|
2578
|
-
const path = str3(args.path);
|
|
2643
|
+
const path = guard(str3(args.path));
|
|
2579
2644
|
if (adapter.getExpanded().includes(path)) {
|
|
2580
2645
|
return textResult(`${path} already expanded`, { path, expanded: true });
|
|
2581
2646
|
}
|
|
@@ -2598,7 +2663,7 @@ function registerFilesBridge(host, options) {
|
|
|
2598
2663
|
{ path: { type: "string" } },
|
|
2599
2664
|
["path"],
|
|
2600
2665
|
(args) => {
|
|
2601
|
-
const path = str3(args.path);
|
|
2666
|
+
const path = guard(str3(args.path));
|
|
2602
2667
|
if (!adapter.getExpanded().includes(path)) {
|
|
2603
2668
|
return textResult(`${path} already collapsed`, { path, expanded: false });
|
|
2604
2669
|
}
|
|
@@ -2624,7 +2689,7 @@ function registerFilesBridge(host, options) {
|
|
|
2624
2689
|
},
|
|
2625
2690
|
[],
|
|
2626
2691
|
(args) => {
|
|
2627
|
-
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);
|
|
2628
2693
|
const prev = adapter.getSelection();
|
|
2629
2694
|
adapter.setSelection(next);
|
|
2630
2695
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -2653,8 +2718,8 @@ function registerFilesBridge(host, options) {
|
|
|
2653
2718
|
if (!adapter.requestSnapshot) {
|
|
2654
2719
|
return errorResult("Host did not wire requestSnapshot (snapshot mode not enabled).");
|
|
2655
2720
|
}
|
|
2656
|
-
const path = args.path !== void 0 ? str3(args.path) : adapter.getPath();
|
|
2657
|
-
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;
|
|
2658
2723
|
const tree = await adapter.requestSnapshot(path, depth);
|
|
2659
2724
|
const count = Array.isArray(tree) ? tree.length : 0;
|
|
2660
2725
|
return textResult(`Snapshot of ${path}: ${count} top-level entr${count === 1 ? "y" : "ies"}.`, {
|
|
@@ -2678,16 +2743,15 @@ var DEFAULT_AGENT10 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
|
2678
2743
|
var truncate = (s, n = 60) => s.length > n ? s.slice(0, n) + "\u2026" : s;
|
|
2679
2744
|
function registerTerminalBridge(host, options) {
|
|
2680
2745
|
const agent = { ...DEFAULT_AGENT10, ...options.agent ?? {} };
|
|
2681
|
-
const pendingMode = options.pendingMode ??
|
|
2746
|
+
const pendingMode = options.pendingMode ?? true;
|
|
2682
2747
|
const screenId = options.screenId ?? options.adapter?.screenId;
|
|
2683
2748
|
const disposers = [];
|
|
2684
2749
|
const staged = /* @__PURE__ */ new Map();
|
|
2685
2750
|
let seq = 0;
|
|
2686
2751
|
ensureUndoToolsRegistered(host);
|
|
2687
2752
|
const listTerminals = () => {
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
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;
|
|
2691
2755
|
};
|
|
2692
2756
|
const resolve = (id) => {
|
|
2693
2757
|
const list = listTerminals();
|
|
@@ -2704,6 +2768,17 @@ function registerTerminalBridge(host, options) {
|
|
|
2704
2768
|
elementId: terminalId ?? screenId ?? "terminal",
|
|
2705
2769
|
label: label ?? "terminal"
|
|
2706
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
|
+
};
|
|
2707
2782
|
const TERMINAL_ARG = {
|
|
2708
2783
|
terminal: {
|
|
2709
2784
|
type: "string",
|
|
@@ -2723,7 +2798,12 @@ function registerTerminalBridge(host, options) {
|
|
|
2723
2798
|
agent,
|
|
2724
2799
|
kind: "terminal",
|
|
2725
2800
|
screenId,
|
|
2726
|
-
|
|
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
|
|
2727
2807
|
}) : wrapped;
|
|
2728
2808
|
disposers.push(
|
|
2729
2809
|
host.registerTool(
|
|
@@ -2760,6 +2840,7 @@ function registerTerminalBridge(host, options) {
|
|
|
2760
2840
|
return textResult(`${kind === "run" ? "ran" : "wrote"} on ${t.id}: ${truncate(data)}`, {
|
|
2761
2841
|
kind,
|
|
2762
2842
|
data,
|
|
2843
|
+
length: data.length,
|
|
2763
2844
|
terminal: t.id,
|
|
2764
2845
|
executed: true
|
|
2765
2846
|
});
|
|
@@ -2770,7 +2851,7 @@ function registerTerminalBridge(host, options) {
|
|
|
2770
2851
|
options.onPending?.(entry);
|
|
2771
2852
|
return textResult(
|
|
2772
2853
|
`Staged ${kind} on ${t.id} (id ${id}) \u2014 awaiting human confirmation: ${truncate(data)}`,
|
|
2773
|
-
{ ...entry, pending: true }
|
|
2854
|
+
{ ...entry, length: data.length, pending: true }
|
|
2774
2855
|
);
|
|
2775
2856
|
}
|
|
2776
2857
|
reg(
|
|
@@ -2815,12 +2896,13 @@ function registerTerminalBridge(host, options) {
|
|
|
2815
2896
|
);
|
|
2816
2897
|
reg(
|
|
2817
2898
|
"terminal_write",
|
|
2818
|
-
"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.",
|
|
2819
2900
|
{ ...TERMINAL_ARG, data: { type: "string", description: "Raw bytes to write." } },
|
|
2820
2901
|
["data"],
|
|
2821
2902
|
(args) => stageOrExec(need(args), "write", String(args.data)),
|
|
2822
2903
|
true,
|
|
2823
|
-
|
|
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)
|
|
2824
2906
|
);
|
|
2825
2907
|
reg(
|
|
2826
2908
|
"terminal_run",
|
|
@@ -2829,41 +2911,47 @@ function registerTerminalBridge(host, options) {
|
|
|
2829
2911
|
["command"],
|
|
2830
2912
|
(args) => stageOrExec(need(args), "run", String(args.command)),
|
|
2831
2913
|
true,
|
|
2832
|
-
(args) => target(truncate(String(args.command ?? "")), resolve(args.terminal)?.id)
|
|
2833
|
-
);
|
|
2834
|
-
reg(
|
|
2835
|
-
"terminal_confirm",
|
|
2836
|
-
"Confirm + execute a staged command by id (pendingMode).",
|
|
2837
|
-
{ id: { type: "string" } },
|
|
2838
|
-
["id"],
|
|
2839
|
-
async (args) => {
|
|
2840
|
-
const id = String(args.id);
|
|
2841
|
-
const entry = staged.get(id);
|
|
2842
|
-
if (!entry) return errorResult(`No staged command ${id}`);
|
|
2843
|
-
const t = resolve(entry.terminalId);
|
|
2844
|
-
if (!t) return errorResult(`Terminal '${entry.terminalId}' is gone \u2014 cannot run ${id}`);
|
|
2845
|
-
staged.delete(id);
|
|
2846
|
-
await exec(t, entry.kind, entry.data);
|
|
2847
|
-
return textResult(`Confirmed ${id}: ${entry.kind} on ${t.id} ${truncate(entry.data)}`, { ...entry, executed: true });
|
|
2848
|
-
},
|
|
2849
|
-
true,
|
|
2850
|
-
(args) => {
|
|
2851
|
-
const e = staged.get(String(args.id));
|
|
2852
|
-
return target(`confirm:${String(args.id ?? "")}`, e?.terminalId);
|
|
2853
|
-
}
|
|
2854
|
-
);
|
|
2855
|
-
reg(
|
|
2856
|
-
"terminal_reject",
|
|
2857
|
-
"Drop a staged command by id without executing it.",
|
|
2858
|
-
{ id: { type: "string" } },
|
|
2859
|
-
["id"],
|
|
2860
|
-
(args) => {
|
|
2861
|
-
const id = String(args.id);
|
|
2862
|
-
if (!staged.delete(id)) return errorResult(`No staged command ${id}`);
|
|
2863
|
-
return textResult(`Rejected ${id}`, { id, rejected: true });
|
|
2864
|
-
},
|
|
2865
|
-
false
|
|
2914
|
+
(args, result) => result?.structuredContent?.pending ? null : target(truncate(String(args.command ?? "")), resolve(args.terminal)?.id)
|
|
2866
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
|
+
}
|
|
2867
2955
|
if (canClear) {
|
|
2868
2956
|
reg(
|
|
2869
2957
|
"terminal_clear",
|
|
@@ -2908,8 +2996,10 @@ function registerTerminalBridge(host, options) {
|
|
|
2908
2996
|
if (!t.setShell) return errorResult(`Terminal '${t.id}' can't switch shells.`);
|
|
2909
2997
|
const id = String(args.id);
|
|
2910
2998
|
const shells = t.listShells?.();
|
|
2911
|
-
if (shells
|
|
2912
|
-
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
|
+
);
|
|
2913
3003
|
}
|
|
2914
3004
|
await t.setShell(id);
|
|
2915
3005
|
return textResult(`Switched ${t.id} shell to ${id}`, { shell: id, terminal: t.id });
|
|
@@ -3050,6 +3140,12 @@ function registerNavigationBridge(host, options) {
|
|
|
3050
3140
|
if (stale) return stale;
|
|
3051
3141
|
const url = String(args.url ?? "");
|
|
3052
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
|
+
}
|
|
3053
3149
|
const from = adapter.getLocation().url;
|
|
3054
3150
|
await adapter.visit(url);
|
|
3055
3151
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -4034,8 +4130,7 @@ function describeSession(id, token) {
|
|
|
4034
4130
|
}
|
|
4035
4131
|
function buildShareUrl(descriptor, baseUrl = typeof window !== "undefined" ? window.location.href.split("?")[0] : "") {
|
|
4036
4132
|
const u = new URL(baseUrl);
|
|
4037
|
-
u.
|
|
4038
|
-
u.searchParams.set("token", descriptor.token);
|
|
4133
|
+
u.hash = new URLSearchParams({ session: descriptor.id, token: descriptor.token }).toString();
|
|
4039
4134
|
return u.toString();
|
|
4040
4135
|
}
|
|
4041
4136
|
function buildShareConfig(descriptor, transport = "broadcast-channel") {
|
|
@@ -4050,9 +4145,10 @@ function buildShareConfig(descriptor, transport = "broadcast-channel") {
|
|
|
4050
4145
|
}
|
|
4051
4146
|
function readSessionFromUrl() {
|
|
4052
4147
|
if (typeof window === "undefined") return null;
|
|
4053
|
-
const
|
|
4054
|
-
const
|
|
4055
|
-
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");
|
|
4056
4152
|
if (!id || !token) return null;
|
|
4057
4153
|
return describeSession(id, token);
|
|
4058
4154
|
}
|