@particle-academy/agent-integrations 0.29.0 → 0.31.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/git.d.cts +44 -0
- package/dist/bridges/git.d.ts +44 -0
- 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-git.cjs +214 -0
- package/dist/bridges-git.cjs.map +1 -0
- package/dist/bridges-git.js +7 -0
- package/dist/bridges-git.js.map +1 -0
- 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-4D3II37O.js +94 -0
- package/dist/chunk-4D3II37O.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 +244 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +31 -24
- 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 +35 -7
- 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}`,
|
|
@@ -2463,8 +2508,111 @@ function registerMapBridge(host, options) {
|
|
|
2463
2508
|
};
|
|
2464
2509
|
}
|
|
2465
2510
|
|
|
2511
|
+
// src/bridges/git.ts
|
|
2512
|
+
function registerGitBridge(host, options) {
|
|
2513
|
+
const agent = { id: "agent", name: "Agent", color: "#a855f7", ...options.agent ?? {} };
|
|
2514
|
+
const disposers = [];
|
|
2515
|
+
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
2516
|
+
const reg = (name, description, properties, required, handler, mutation2 = false) => {
|
|
2517
|
+
const wrapped = async (args) => {
|
|
2518
|
+
try {
|
|
2519
|
+
return await handler(args);
|
|
2520
|
+
} catch (error) {
|
|
2521
|
+
return errorResult(error instanceof Error ? error.message : String(error));
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
const final = mutation2 ? wrapToolWithActivity(wrapped, {
|
|
2525
|
+
toolName: name,
|
|
2526
|
+
agent,
|
|
2527
|
+
kind: "git",
|
|
2528
|
+
resolveTarget: ({ args }) => ({ kind: "git", elementId: typeof args.path === "string" ? args.path : void 0 })
|
|
2529
|
+
}) : wrapped;
|
|
2530
|
+
disposers.push(host.registerTool({
|
|
2531
|
+
name,
|
|
2532
|
+
description,
|
|
2533
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
2534
|
+
}, final));
|
|
2535
|
+
};
|
|
2536
|
+
reg("git_status", "Read normalized working-tree and branch status.", {}, [], async () => {
|
|
2537
|
+
const result = await options.adapter.status();
|
|
2538
|
+
return textResult(JSON.stringify(result, null, 2), result);
|
|
2539
|
+
});
|
|
2540
|
+
reg("git_log", "Read normalized commit history.", {
|
|
2541
|
+
ref: { type: "string" },
|
|
2542
|
+
limit: { type: "number" },
|
|
2543
|
+
skip: { type: "number" }
|
|
2544
|
+
}, [], async (args) => {
|
|
2545
|
+
const result = await options.adapter.log(args);
|
|
2546
|
+
return textResult(JSON.stringify(result, null, 2), result);
|
|
2547
|
+
});
|
|
2548
|
+
reg("git_diff", "Read a normalized Git diff.", {
|
|
2549
|
+
from: { type: "string" },
|
|
2550
|
+
to: { type: "string" },
|
|
2551
|
+
staged: { type: "boolean" },
|
|
2552
|
+
paths: { type: "array", items: { type: "string" } }
|
|
2553
|
+
}, [], async (args) => {
|
|
2554
|
+
const result = await options.adapter.diff(args);
|
|
2555
|
+
return textResult(JSON.stringify(result, null, 2), result);
|
|
2556
|
+
});
|
|
2557
|
+
if (options.adapter.reviews) reg("git_reviews_list", "List normalized pull/merge requests.", {
|
|
2558
|
+
state: { type: "string" },
|
|
2559
|
+
cursor: { type: "string" },
|
|
2560
|
+
limit: { type: "number" }
|
|
2561
|
+
}, [], async (args) => {
|
|
2562
|
+
const result = await options.adapter.reviews(args);
|
|
2563
|
+
return textResult(JSON.stringify(result, null, 2), result);
|
|
2564
|
+
});
|
|
2565
|
+
if (options.adapter.checks) reg("git_checks", "Read checks or pipeline summaries for a revision.", {
|
|
2566
|
+
revision: { type: "string" }
|
|
2567
|
+
}, ["revision"], async (args) => {
|
|
2568
|
+
const result = await options.adapter.checks(String(args.revision));
|
|
2569
|
+
return textResult(JSON.stringify(result, null, 2), result);
|
|
2570
|
+
});
|
|
2571
|
+
const mutation = (name, description, properties, required) => reg(name, description, properties, required, async (args) => {
|
|
2572
|
+
const proposal = await options.adapter.propose(name.slice(4), args);
|
|
2573
|
+
if (args.apply === true) {
|
|
2574
|
+
if (!options.allowApply || !options.adapter.applyProposal) return errorResult("This host requires human confirmation before applying Git writes.");
|
|
2575
|
+
const result = await options.adapter.applyProposal(proposal);
|
|
2576
|
+
return textResult(`Applied: ${proposal.summary}`, result);
|
|
2577
|
+
}
|
|
2578
|
+
return textResult(`Proposed: ${proposal.summary}`, proposal);
|
|
2579
|
+
}, true);
|
|
2580
|
+
mutation("git_stage", "Propose staging paths.", { paths: { type: "array", items: { type: "string" } }, apply: { type: "boolean" } }, ["paths"]);
|
|
2581
|
+
mutation("git_unstage", "Propose unstaging paths.", { paths: { type: "array", items: { type: "string" } }, apply: { type: "boolean" } }, ["paths"]);
|
|
2582
|
+
mutation("git_commit", "Propose committing staged changes.", { message: { type: "string" }, apply: { type: "boolean" } }, ["message"]);
|
|
2583
|
+
mutation("git_checkout", "Propose checking out a ref.", { target: { type: "string" }, apply: { type: "boolean" } }, ["target"]);
|
|
2584
|
+
mutation("git_fetch", "Propose fetching a remote.", { remote: { type: "string" }, apply: { type: "boolean" } }, []);
|
|
2585
|
+
mutation("git_pull", "Propose a fast-forward-only pull.", { remote: { type: "string" }, branch: { type: "string" }, apply: { type: "boolean" } }, []);
|
|
2586
|
+
mutation("git_push", "Propose pushing a branch.", { remote: { type: "string" }, branch: { type: "string" }, apply: { type: "boolean" } }, []);
|
|
2587
|
+
mutation("git_review_create", "Propose creating a pull/merge request.", {
|
|
2588
|
+
title: { type: "string" },
|
|
2589
|
+
body: { type: "string" },
|
|
2590
|
+
sourceBranch: { type: "string" },
|
|
2591
|
+
targetBranch: { type: "string" },
|
|
2592
|
+
draft: { type: "boolean" },
|
|
2593
|
+
apply: { type: "boolean" }
|
|
2594
|
+
}, ["title", "sourceBranch", "targetBranch"]);
|
|
2595
|
+
return { id: "git", title: "Git", dispose: () => disposers.forEach((dispose) => dispose()) };
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2466
2598
|
// src/bridges/files.ts
|
|
2467
2599
|
var DEFAULT_AGENT9 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
2600
|
+
var MAX_SNAPSHOT_DEPTH = 8;
|
|
2601
|
+
function assertPathWithinRoot(root, path) {
|
|
2602
|
+
const segments = path.replace(/[\\/]+/g, "/").split("/");
|
|
2603
|
+
if (segments.includes("..")) {
|
|
2604
|
+
throw new Error(`Path may not contain '..': ${path}`);
|
|
2605
|
+
}
|
|
2606
|
+
const isAbsolute = /^([A-Za-z]:)?[\\/]/.test(path) || /^[A-Za-z]:/.test(path);
|
|
2607
|
+
if (isAbsolute) {
|
|
2608
|
+
const norm = (p2) => p2.replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
2609
|
+
const r = norm(root);
|
|
2610
|
+
const p = norm(path);
|
|
2611
|
+
if (p !== r && !p.startsWith(r + "/")) {
|
|
2612
|
+
throw new Error(`Path escapes the configured root: ${path}`);
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2468
2616
|
var str3 = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
2469
2617
|
var num3 = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
2470
2618
|
var asPaths = (v) => {
|
|
@@ -2480,6 +2628,10 @@ function registerFilesBridge(host, options) {
|
|
|
2480
2628
|
const { adapter } = options;
|
|
2481
2629
|
const agent = { ...DEFAULT_AGENT9, ...options.agent ?? {} };
|
|
2482
2630
|
const disposers = [];
|
|
2631
|
+
const guard = (path) => {
|
|
2632
|
+
if (options.root !== void 0) assertPathWithinRoot(options.root, path);
|
|
2633
|
+
return path;
|
|
2634
|
+
};
|
|
2483
2635
|
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
2484
2636
|
const fTarget = (args, _result) => ({
|
|
2485
2637
|
kind: "files",
|
|
@@ -2535,7 +2687,7 @@ function registerFilesBridge(host, options) {
|
|
|
2535
2687
|
{ path: { type: "string", description: "Folder path to list; defaults to the current directory." } },
|
|
2536
2688
|
[],
|
|
2537
2689
|
async (args) => {
|
|
2538
|
-
const path = args.path !== void 0 ? str3(args.path) : adapter.getPath();
|
|
2690
|
+
const path = guard(args.path !== void 0 ? str3(args.path) : adapter.getPath());
|
|
2539
2691
|
const entries = await adapter.listChildren(path);
|
|
2540
2692
|
const summary = entries.map((e) => ({
|
|
2541
2693
|
path: e.path,
|
|
@@ -2554,7 +2706,7 @@ function registerFilesBridge(host, options) {
|
|
|
2554
2706
|
{ path: { type: "string" } },
|
|
2555
2707
|
["path"],
|
|
2556
2708
|
(args) => {
|
|
2557
|
-
const path = str3(args.path);
|
|
2709
|
+
const path = guard(str3(args.path));
|
|
2558
2710
|
const prev = adapter.getPath();
|
|
2559
2711
|
adapter.setPath(path);
|
|
2560
2712
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -2575,7 +2727,7 @@ function registerFilesBridge(host, options) {
|
|
|
2575
2727
|
{ path: { type: "string" } },
|
|
2576
2728
|
["path"],
|
|
2577
2729
|
(args) => {
|
|
2578
|
-
const path = str3(args.path);
|
|
2730
|
+
const path = guard(str3(args.path));
|
|
2579
2731
|
if (adapter.getExpanded().includes(path)) {
|
|
2580
2732
|
return textResult(`${path} already expanded`, { path, expanded: true });
|
|
2581
2733
|
}
|
|
@@ -2598,7 +2750,7 @@ function registerFilesBridge(host, options) {
|
|
|
2598
2750
|
{ path: { type: "string" } },
|
|
2599
2751
|
["path"],
|
|
2600
2752
|
(args) => {
|
|
2601
|
-
const path = str3(args.path);
|
|
2753
|
+
const path = guard(str3(args.path));
|
|
2602
2754
|
if (!adapter.getExpanded().includes(path)) {
|
|
2603
2755
|
return textResult(`${path} already collapsed`, { path, expanded: false });
|
|
2604
2756
|
}
|
|
@@ -2624,7 +2776,7 @@ function registerFilesBridge(host, options) {
|
|
|
2624
2776
|
},
|
|
2625
2777
|
[],
|
|
2626
2778
|
(args) => {
|
|
2627
|
-
const next = args.paths !== void 0 ? asPaths(args.paths) : asPaths(args.path);
|
|
2779
|
+
const next = (args.paths !== void 0 ? asPaths(args.paths) : asPaths(args.path)).map(guard);
|
|
2628
2780
|
const prev = adapter.getSelection();
|
|
2629
2781
|
adapter.setSelection(next);
|
|
2630
2782
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -2653,8 +2805,8 @@ function registerFilesBridge(host, options) {
|
|
|
2653
2805
|
if (!adapter.requestSnapshot) {
|
|
2654
2806
|
return errorResult("Host did not wire requestSnapshot (snapshot mode not enabled).");
|
|
2655
2807
|
}
|
|
2656
|
-
const path = args.path !== void 0 ? str3(args.path) : adapter.getPath();
|
|
2657
|
-
const depth = args.depth !== void 0 ? num3(args.depth) : void 0;
|
|
2808
|
+
const path = guard(args.path !== void 0 ? str3(args.path) : adapter.getPath());
|
|
2809
|
+
const depth = args.depth !== void 0 ? Math.max(0, Math.min(MAX_SNAPSHOT_DEPTH, num3(args.depth))) : void 0;
|
|
2658
2810
|
const tree = await adapter.requestSnapshot(path, depth);
|
|
2659
2811
|
const count = Array.isArray(tree) ? tree.length : 0;
|
|
2660
2812
|
return textResult(`Snapshot of ${path}: ${count} top-level entr${count === 1 ? "y" : "ies"}.`, {
|
|
@@ -2678,16 +2830,15 @@ var DEFAULT_AGENT10 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
|
2678
2830
|
var truncate = (s, n = 60) => s.length > n ? s.slice(0, n) + "\u2026" : s;
|
|
2679
2831
|
function registerTerminalBridge(host, options) {
|
|
2680
2832
|
const agent = { ...DEFAULT_AGENT10, ...options.agent ?? {} };
|
|
2681
|
-
const pendingMode = options.pendingMode ??
|
|
2833
|
+
const pendingMode = options.pendingMode ?? true;
|
|
2682
2834
|
const screenId = options.screenId ?? options.adapter?.screenId;
|
|
2683
2835
|
const disposers = [];
|
|
2684
2836
|
const staged = /* @__PURE__ */ new Map();
|
|
2685
2837
|
let seq = 0;
|
|
2686
2838
|
ensureUndoToolsRegistered(host);
|
|
2687
2839
|
const listTerminals = () => {
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
return [];
|
|
2840
|
+
const all = options.terminals ? options.terminals() : options.adapter ? [{ id: "terminal", label: "Terminal", active: true, ...options.adapter }] : [];
|
|
2841
|
+
return options.canAccess ? all.filter((t) => options.canAccess(t.id)) : all;
|
|
2691
2842
|
};
|
|
2692
2843
|
const resolve = (id) => {
|
|
2693
2844
|
const list = listTerminals();
|
|
@@ -2704,6 +2855,17 @@ function registerTerminalBridge(host, options) {
|
|
|
2704
2855
|
elementId: terminalId ?? screenId ?? "terminal",
|
|
2705
2856
|
label: label ?? "terminal"
|
|
2706
2857
|
});
|
|
2858
|
+
const redactMeta = (result) => {
|
|
2859
|
+
const sc = result.structuredContent;
|
|
2860
|
+
if (!sc || typeof sc !== "object") return void 0;
|
|
2861
|
+
return {
|
|
2862
|
+
terminal: sc.terminal,
|
|
2863
|
+
kind: sc.kind,
|
|
2864
|
+
length: typeof sc.data === "string" ? sc.data.length : void 0,
|
|
2865
|
+
executed: sc.executed,
|
|
2866
|
+
pending: sc.pending
|
|
2867
|
+
};
|
|
2868
|
+
};
|
|
2707
2869
|
const TERMINAL_ARG = {
|
|
2708
2870
|
terminal: {
|
|
2709
2871
|
type: "string",
|
|
@@ -2723,7 +2885,12 @@ function registerTerminalBridge(host, options) {
|
|
|
2723
2885
|
agent,
|
|
2724
2886
|
kind: "terminal",
|
|
2725
2887
|
screenId,
|
|
2726
|
-
|
|
2888
|
+
// A per-call resolver may return null to SKIP the broadcast (e.g. a
|
|
2889
|
+
// merely-staged command); only fall back to the default target when no
|
|
2890
|
+
// resolver was supplied.
|
|
2891
|
+
resolveTarget: ({ args, result }) => resolveTarget ? resolveTarget(args, result) : target(),
|
|
2892
|
+
// Never fan raw command/keystroke bytes to peers — broadcast shape only.
|
|
2893
|
+
buildMeta: redactMeta
|
|
2727
2894
|
}) : wrapped;
|
|
2728
2895
|
disposers.push(
|
|
2729
2896
|
host.registerTool(
|
|
@@ -2760,6 +2927,7 @@ function registerTerminalBridge(host, options) {
|
|
|
2760
2927
|
return textResult(`${kind === "run" ? "ran" : "wrote"} on ${t.id}: ${truncate(data)}`, {
|
|
2761
2928
|
kind,
|
|
2762
2929
|
data,
|
|
2930
|
+
length: data.length,
|
|
2763
2931
|
terminal: t.id,
|
|
2764
2932
|
executed: true
|
|
2765
2933
|
});
|
|
@@ -2770,7 +2938,7 @@ function registerTerminalBridge(host, options) {
|
|
|
2770
2938
|
options.onPending?.(entry);
|
|
2771
2939
|
return textResult(
|
|
2772
2940
|
`Staged ${kind} on ${t.id} (id ${id}) \u2014 awaiting human confirmation: ${truncate(data)}`,
|
|
2773
|
-
{ ...entry, pending: true }
|
|
2941
|
+
{ ...entry, length: data.length, pending: true }
|
|
2774
2942
|
);
|
|
2775
2943
|
}
|
|
2776
2944
|
reg(
|
|
@@ -2815,12 +2983,13 @@ function registerTerminalBridge(host, options) {
|
|
|
2815
2983
|
);
|
|
2816
2984
|
reg(
|
|
2817
2985
|
"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.",
|
|
2986
|
+
"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
2987
|
{ ...TERMINAL_ARG, data: { type: "string", description: "Raw bytes to write." } },
|
|
2820
2988
|
["data"],
|
|
2821
2989
|
(args) => stageOrExec(need(args), "write", String(args.data)),
|
|
2822
2990
|
true,
|
|
2823
|
-
|
|
2991
|
+
// Skip the peer broadcast while only staged; emit once it actually executes.
|
|
2992
|
+
(args, result) => result?.structuredContent?.pending ? null : target(`write:${String(args.terminal ?? "")}`, resolve(args.terminal)?.id)
|
|
2824
2993
|
);
|
|
2825
2994
|
reg(
|
|
2826
2995
|
"terminal_run",
|
|
@@ -2829,41 +2998,47 @@ function registerTerminalBridge(host, options) {
|
|
|
2829
2998
|
["command"],
|
|
2830
2999
|
(args) => stageOrExec(need(args), "run", String(args.command)),
|
|
2831
3000
|
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
|
|
3001
|
+
(args, result) => result?.structuredContent?.pending ? null : target(truncate(String(args.command ?? "")), resolve(args.terminal)?.id)
|
|
2866
3002
|
);
|
|
3003
|
+
if (options.allowAgentConfirm) {
|
|
3004
|
+
reg(
|
|
3005
|
+
"terminal_confirm",
|
|
3006
|
+
"Confirm + execute a staged command by id (pendingMode).",
|
|
3007
|
+
{ id: { type: "string" } },
|
|
3008
|
+
["id"],
|
|
3009
|
+
async (args) => {
|
|
3010
|
+
const id = String(args.id);
|
|
3011
|
+
const entry = staged.get(id);
|
|
3012
|
+
if (!entry) return errorResult(`No staged command ${id}`);
|
|
3013
|
+
const t = resolve(entry.terminalId);
|
|
3014
|
+
if (!t) return errorResult(`Terminal '${entry.terminalId}' is gone \u2014 cannot run ${id}`);
|
|
3015
|
+
staged.delete(id);
|
|
3016
|
+
await exec(t, entry.kind, entry.data);
|
|
3017
|
+
return textResult(`Confirmed ${id}: ${entry.kind} on ${t.id} ${truncate(entry.data)}`, {
|
|
3018
|
+
...entry,
|
|
3019
|
+
length: entry.data.length,
|
|
3020
|
+
executed: true
|
|
3021
|
+
});
|
|
3022
|
+
},
|
|
3023
|
+
true,
|
|
3024
|
+
(args) => {
|
|
3025
|
+
const e = staged.get(String(args.id));
|
|
3026
|
+
return target(`confirm:${String(args.id ?? "")}`, e?.terminalId);
|
|
3027
|
+
}
|
|
3028
|
+
);
|
|
3029
|
+
reg(
|
|
3030
|
+
"terminal_reject",
|
|
3031
|
+
"Drop a staged command by id without executing it.",
|
|
3032
|
+
{ id: { type: "string" } },
|
|
3033
|
+
["id"],
|
|
3034
|
+
(args) => {
|
|
3035
|
+
const id = String(args.id);
|
|
3036
|
+
if (!staged.delete(id)) return errorResult(`No staged command ${id}`);
|
|
3037
|
+
return textResult(`Rejected ${id}`, { id, rejected: true });
|
|
3038
|
+
},
|
|
3039
|
+
false
|
|
3040
|
+
);
|
|
3041
|
+
}
|
|
2867
3042
|
if (canClear) {
|
|
2868
3043
|
reg(
|
|
2869
3044
|
"terminal_clear",
|
|
@@ -2908,8 +3083,10 @@ function registerTerminalBridge(host, options) {
|
|
|
2908
3083
|
if (!t.setShell) return errorResult(`Terminal '${t.id}' can't switch shells.`);
|
|
2909
3084
|
const id = String(args.id);
|
|
2910
3085
|
const shells = t.listShells?.();
|
|
2911
|
-
if (shells
|
|
2912
|
-
return errorResult(
|
|
3086
|
+
if (!shells || !shells.some((s) => s.id === id)) {
|
|
3087
|
+
return errorResult(
|
|
3088
|
+
`Unknown or unlisted shell '${id}' for ${t.id}. Call terminal_list_shells for valid ids.`
|
|
3089
|
+
);
|
|
2913
3090
|
}
|
|
2914
3091
|
await t.setShell(id);
|
|
2915
3092
|
return textResult(`Switched ${t.id} shell to ${id}`, { shell: id, terminal: t.id });
|
|
@@ -3050,6 +3227,12 @@ function registerNavigationBridge(host, options) {
|
|
|
3050
3227
|
if (stale) return stale;
|
|
3051
3228
|
const url = String(args.url ?? "");
|
|
3052
3229
|
if (!url) return errorResult("url is required");
|
|
3230
|
+
const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase();
|
|
3231
|
+
if (scheme && scheme !== "http" && scheme !== "https") {
|
|
3232
|
+
return errorResult(
|
|
3233
|
+
`Refusing to navigate to a '${scheme}:' URL. Only http(s) URLs or same-site paths are allowed.`
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
3053
3236
|
const from = adapter.getLocation().url;
|
|
3054
3237
|
await adapter.visit(url);
|
|
3055
3238
|
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
@@ -4034,8 +4217,7 @@ function describeSession(id, token) {
|
|
|
4034
4217
|
}
|
|
4035
4218
|
function buildShareUrl(descriptor, baseUrl = typeof window !== "undefined" ? window.location.href.split("?")[0] : "") {
|
|
4036
4219
|
const u = new URL(baseUrl);
|
|
4037
|
-
u.
|
|
4038
|
-
u.searchParams.set("token", descriptor.token);
|
|
4220
|
+
u.hash = new URLSearchParams({ session: descriptor.id, token: descriptor.token }).toString();
|
|
4039
4221
|
return u.toString();
|
|
4040
4222
|
}
|
|
4041
4223
|
function buildShareConfig(descriptor, transport = "broadcast-channel") {
|
|
@@ -4050,9 +4232,10 @@ function buildShareConfig(descriptor, transport = "broadcast-channel") {
|
|
|
4050
4232
|
}
|
|
4051
4233
|
function readSessionFromUrl() {
|
|
4052
4234
|
if (typeof window === "undefined") return null;
|
|
4053
|
-
const
|
|
4054
|
-
const
|
|
4055
|
-
const
|
|
4235
|
+
const url = new URL(window.location.href);
|
|
4236
|
+
const frag = new URLSearchParams(url.hash.replace(/^#/, ""));
|
|
4237
|
+
const id = frag.get("session") ?? url.searchParams.get("session");
|
|
4238
|
+
const token = frag.get("token") ?? url.searchParams.get("token");
|
|
4056
4239
|
if (!id || !token) return null;
|
|
4057
4240
|
return describeSession(id, token);
|
|
4058
4241
|
}
|
|
@@ -5482,6 +5665,7 @@ exports.registerDocBridge = registerDocBridge;
|
|
|
5482
5665
|
exports.registerFeaturesBridge = registerFeaturesBridge;
|
|
5483
5666
|
exports.registerFilesBridge = registerFilesBridge;
|
|
5484
5667
|
exports.registerFormBridge = registerFormBridge;
|
|
5668
|
+
exports.registerGitBridge = registerGitBridge;
|
|
5485
5669
|
exports.registerMapBridge = registerMapBridge;
|
|
5486
5670
|
exports.registerNavigationBridge = registerNavigationBridge;
|
|
5487
5671
|
exports.registerSceneBridge = registerSceneBridge;
|