nolo-cli 0.33.0-alpha.2 → 0.33.0-alpha.21
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/{_executeModel-RWPVI3YV.js → _executeModel-XKWVTZAY.js} +35 -34
- package/{action-SMLG6OTB.js → action-Q4KO7DDW.js} +4 -4
- package/{addContentAction-SFO244QN.js → addContentAction-FRXDZKSI.js} +3 -3
- package/{addDialogAgentAction-W44ZCEHB.js → addDialogAgentAction-XL5G3LPR.js} +2 -2
- package/{agentDeleteCommand-KU2RXQPP.js → agentDeleteCommand-ZU4H6F6M.js} +19 -18
- package/{agentEmailCommands-MPCMDENS.js → agentEmailCommands-6ULFXW4A.js} +41 -40
- package/{agentListCommands-EPD4N6FX.js → agentListCommands-VN7WCW7Z.js} +21 -20
- package/{agentMachineCommands-AF7C3YRT.js → agentMachineCommands-5PALVQBD.js} +21 -20
- package/{agentPullCommand-YH5GEBK4.js → agentPullCommand-TSWAJPS2.js} +1 -1
- package/{agentRecordCommands-EYE6MZZG.js → agentRecordCommands-XEB66KRX.js} +40 -39
- package/{agentRunCommand-LL22SGTN.js → agentRunCommand-OJX27NII.js} +42 -41
- package/{agentRuntimeLocal-JX34G47Z.js → agentRuntimeLocal-UWDOE55A.js} +20 -19
- package/{agentSlice-TNMFQQOV.js → agentSlice-SZRRJ3ZX.js} +5 -5
- package/{appDeleteCommands-43K5VXQB.js → appDeleteCommands-R63PZAY7.js} +20 -19
- package/{appDeployCommands-FEEJSVDZ.js → appDeployCommands-VNRT54YO.js} +20 -19
- package/{appDeployStatusCommands-H76CG45R.js → appDeployStatusCommands-6IPTKLEM.js} +20 -19
- package/{appGetCommands-MDZYFTYZ.js → appGetCommands-JPFVMVAI.js} +20 -19
- package/{appListCommands-TUJJXPWG.js → appListCommands-B3SLTPII.js} +20 -19
- package/{authCommand-T7UK4CWS.js → authCommand-EX5G5PF4.js} +12 -11
- package/{authCommands-EAPFGNHN.js → authCommands-VR6Q6GU2.js} +4 -4
- package/{chatQueueReduxAdapter-LVQXC4ZY.js → chatQueueReduxAdapter-IEX32ZVP.js} +15 -14
- package/{chunk-2H3XIBY4.js → chunk-2CNX3CJE.js} +4 -82
- package/{chunk-MPZJVZDC.js → chunk-2UDMTJOM.js} +9 -9
- package/{chunk-WHD6BG7M.js → chunk-37LDAHQN.js} +20 -18
- package/{chunk-2UECQ7RE.js → chunk-3AOLVADY.js} +1 -1
- package/{chunk-HUCT7HID.js → chunk-42IF53CR.js} +1 -1
- package/{chunk-AAGCMVFT.js → chunk-46JSKTPF.js} +189 -38
- package/{chunk-3WXRAHCG.js → chunk-6MSSX7FI.js} +1 -1
- package/{chunk-57QJXIB6.js → chunk-6YH4UR3E.js} +3 -3
- package/{chunk-HDKRSRYU.js → chunk-7BNPRRDS.js} +150 -29
- package/{chunk-FMEVI5TG.js → chunk-7CH6U3FL.js} +1 -1
- package/{chunk-OR2HNLVO.js → chunk-7EPNNOL4.js} +6 -6
- package/{chunk-PREQU5QV.js → chunk-7OGIWPNK.js} +1 -1
- package/{chunk-DFUPZE6V.js → chunk-7SLMYTSF.js} +1 -1
- package/{chunk-3GZXL6FM.js → chunk-AOWETAXX.js} +1 -1
- package/{chunk-2IVEXF7P.js → chunk-BHILFABL.js} +1 -1
- package/{chunk-TFPUAELY.js → chunk-DDET6246.js} +1 -1
- package/{chunk-ZURFOV5J.js → chunk-DMT4NJXR.js} +1 -1
- package/{chunk-RAZOLMJR.js → chunk-EQ6HOWEJ.js} +2 -2
- package/{chunk-6HYVJV7B.js → chunk-ESHYE7WU.js} +2 -1
- package/{chunk-JZ5AMMXU.js → chunk-FH7VUJN5.js} +2 -2
- package/{chunk-I6N2UVVY.js → chunk-FV63ICZC.js} +1 -1
- package/{chunk-U4TRT5V4.js → chunk-GEPHJJW4.js} +1 -1
- package/{chunk-VH2F7ZB6.js → chunk-GTEHJWXU.js} +1 -1
- package/{chunk-AOXVOC6D.js → chunk-HCOIKLSH.js} +2 -2
- package/{chunk-GTYM4HKV.js → chunk-IEHZHWN7.js} +2 -2
- package/{chunk-2QTEYCR7.js → chunk-JFUW3UTO.js} +1 -1
- package/{chunk-73GINUCW.js → chunk-JL2PUYVS.js} +102 -23
- package/{chunk-37V5LNEQ.js → chunk-KIL7V27H.js} +1 -1
- package/{chunk-JTSGWTYZ.js → chunk-KMMQFY5O.js} +1 -1
- package/{chunk-UK5JGQVU.js → chunk-KZB5SNYH.js} +9 -14
- package/{chunk-XCCGU6QB.js → chunk-L5XKDBSH.js} +1 -1
- package/{chunk-DPMBNHOJ.js → chunk-LAZTMWIU.js} +1 -1
- package/{chunk-M4NC7NF6.js → chunk-LJGEEYGC.js} +2 -2
- package/{chunk-B2BSG6QQ.js → chunk-LLKHGZME.js} +4 -4
- package/{chunk-7E72JMBB.js → chunk-N2WDGMD5.js} +45 -4
- package/{chunk-Q5ITEGIN.js → chunk-N4XRDM26.js} +1 -1
- package/{chunk-AFV4FOT2.js → chunk-OQVRTSZ5.js} +4 -91
- package/{chunk-3VAQHAXT.js → chunk-PU47HKTF.js} +11 -11
- package/{chunk-CLP2K7U4.js → chunk-QKCMP3LU.js} +2 -2
- package/{chunk-JZPBVBIY.js → chunk-QTZD6HBK.js} +18 -8
- package/{chunk-LOD75GOS.js → chunk-REOW4YJX.js} +80 -1099
- package/{chunk-6SXC35F4.js → chunk-RO3AEN3L.js} +5 -5
- package/{chunk-5CS33IBL.js → chunk-RQFO3CAF.js} +8 -9
- package/{chunk-GMXWYXAF.js → chunk-SBPGB6TF.js} +3 -3
- package/{chunk-FSSX3XRY.js → chunk-U2FRXKC7.js} +1 -1
- package/{chunk-NUO3QFWD.js → chunk-UNHKVD2N.js} +1 -1
- package/{chunk-GE6IWA5N.js → chunk-UNZDCI3O.js} +126 -1
- package/{chunk-JMPYDFHY.js → chunk-V44VE7R4.js} +2 -2
- package/{chunk-IL545V3E.js → chunk-V4MT7AEN.js} +1 -1
- package/{chunk-FXFGNOT2.js → chunk-VFVCYIRQ.js} +1 -1
- package/chunk-VIRTYWWE.js +97 -0
- package/{chunk-ZJMME4DS.js → chunk-XS543SNR.js} +5 -5
- package/{chunk-O6GEBMIY.js → chunk-XTSVOICC.js} +195 -47
- package/{chunk-6PO2SYCS.js → chunk-YH3FPBG6.js} +1569 -204
- package/{chunk-WLH5BAKX.js → chunk-YYTTDOWA.js} +2 -2
- package/{chunk-5SBK2MTI.js → chunk-ZKUATCEY.js} +20 -69
- package/{codingSkills-DY5MINPG.js → codingSkills-5VOV3JVB.js} +2 -2
- package/{createAgentAutomationAction-INQT23LX.js → createAgentAutomationAction-72NNXELT.js} +16 -15
- package/{createDialogAction-LDCPDI73.js → createDialogAction-3CJ2GXNO.js} +18 -17
- package/{createPageAction-5YXOG2M4.js → createPageAction-HZHQGQO6.js} +16 -15
- package/{dbSlice-3SD5L5H2.js → dbSlice-2YD2PZER.js} +2 -2
- package/{dialogCommands-CHJO47AX.js → dialogCommands-2BF4ZSWH.js} +18 -17
- package/{dialogSlice-DOWKOILN.js → dialogSlice-5R4O4377.js} +15 -14
- package/{docCreateCommands-OVZARUFX.js → docCreateCommands-HMZUBFGL.js} +19 -18
- package/{docDeleteCommands-E2XJ62LD.js → docDeleteCommands-DFR7GCOR.js} +19 -18
- package/{docReadCommands-I7OSREPJ.js → docReadCommands-YM7RDUAZ.js} +19 -18
- package/{docStore-UAWSKZ2Y.js → docStore-A4SRW2DJ.js} +17 -16
- package/{docUpdateCommands-X4Z26VJM.js → docUpdateCommands-TTQBD7IU.js} +19 -18
- package/{getPricing-L2ENXWHV.js → getPricing-J4VXWR5B.js} +3 -3
- package/{handleSendMessageAction-C3TSDWKH.js → handleSendMessageAction-CIFBZCI2.js} +15 -14
- package/index.js +70 -70
- package/{localRuntimeAuthority-E3R24J4T.js → localRuntimeAuthority-S77B5QVB.js} +6 -2
- package/{localRuntimeDb-DDZCJFQZ.js → localRuntimeDb-N3H3HQ7G.js} +1 -1
- package/{machineCommands-BUOIYJAD.js → machineCommands-QSW4JKR2.js} +40 -39
- package/{machineInfo-M6KB6HZN.js → machineInfo-66ZFUMLE.js} +1 -1
- package/{machineWsRunDispatch-7CRWAA6O.js → machineWsRunDispatch-X3ZC7DZJ.js} +39 -38
- package/{models-OX2S5C6Q.js → models-SQFOI4FI.js} +2 -2
- package/package.json +1 -1
- package/{read-6G73BMEY.js → read-526GJR3R.js} +1 -1
- package/{readDocTool-DK4EI2GA.js → readDocTool-JNEZUOSM.js} +1 -1
- package/{readlineWorkspace-ZDWEASJV.js → readlineWorkspace-J2ARMEUC.js} +2701 -2083
- package/{removeDialogAgentAction-E7H557UO.js → removeDialogAgentAction-NPMEFEJW.js} +4 -4
- package/{runAgentClientLoop-U7HYA3O5.js → runAgentClientLoop-5I2R74NR.js} +31 -30
- package/{runtimeDoctorCommands-KIKM6BIO.js → runtimeDoctorCommands-KHGQKT3B.js} +3 -3
- package/{setDialogExtraReferencesAction-RMMGBR5F.js → setDialogExtraReferencesAction-ZZ6QWAG2.js} +3 -3
- package/{setPrimaryDialogAgentAction-FWBKKLVA.js → setPrimaryDialogAgentAction-BG72QA6S.js} +4 -4
- package/{settingSlice-73PV4KG4.js → settingSlice-JCCLCXZB.js} +3 -3
- package/{skillDiscovery-KLLKXPUO.js → skillDiscovery-AYBKAG6X.js} +3 -3
- package/{spaceCommands-GEWZXGHM.js → spaceCommands-EZYMMJJH.js} +18 -17
- package/{spaceSlice-B6MOVY4N.js → spaceSlice-TL2FR4AF.js} +16 -15
- package/{streamAgentChatTurn-T527EQTT.js → streamAgentChatTurn-UDYSE2ES.js} +45 -54
- package/{switchDialogAgentAction-US6QNL3N.js → switchDialogAgentAction-YPGPM2LL.js} +2 -2
- package/{tableCommands-QE47K72R.js → tableCommands-64FZBAI3.js} +1 -1
- package/{tools-XMWCVMPM.js → tools-KWYWDJ77.js} +25 -24
- package/{tuiSummaryLlmCaller-NCAJSVSD.js → tuiSummaryLlmCaller-WUVAOIYU.js} +9 -8
- package/{updateCommands-AK2RDOFR.js → updateCommands-MNDUIKN7.js} +2 -2
- package/{updateDialogTitleAction-XPJZRJEL.js → updateDialogTitleAction-NQCOEOLF.js} +16 -15
|
@@ -3,11 +3,10 @@ import {
|
|
|
3
3
|
buildIdentityBlock,
|
|
4
4
|
buildRuntimeGuidanceBlocks,
|
|
5
5
|
resolveToolGuidedSections
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZKUATCEY.js";
|
|
7
7
|
import {
|
|
8
|
-
getModelContextWindow
|
|
9
|
-
|
|
10
|
-
} from "./chunk-2H3XIBY4.js";
|
|
8
|
+
getModelContextWindow
|
|
9
|
+
} from "./chunk-2CNX3CJE.js";
|
|
11
10
|
import {
|
|
12
11
|
COMPACTION_SUMMARY_SYSTEM_PROMPT,
|
|
13
12
|
buildCompactionMetricsFromPlan,
|
|
@@ -19,17 +18,17 @@ import {
|
|
|
19
18
|
formatMessagesForSummaryWithTruncation,
|
|
20
19
|
planCompression,
|
|
21
20
|
planContextUsage
|
|
22
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-HCOIKLSH.js";
|
|
23
22
|
import {
|
|
24
23
|
findModelConfig,
|
|
25
24
|
getProviderByModelName
|
|
26
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-7SLMYTSF.js";
|
|
27
26
|
import {
|
|
28
27
|
buildDialogSummaryLayer
|
|
29
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-3AOLVADY.js";
|
|
30
29
|
import {
|
|
31
30
|
buildUserResponseLanguageContext
|
|
32
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-7CH6U3FL.js";
|
|
33
32
|
import {
|
|
34
33
|
canonicalizeToolName,
|
|
35
34
|
canonicalizeToolNames
|
|
@@ -37,18 +36,18 @@ import {
|
|
|
37
36
|
import {
|
|
38
37
|
toErrorMessage
|
|
39
38
|
} from "./chunk-HNMXYYJR.js";
|
|
40
|
-
import {
|
|
41
|
-
PLATFORM_HOSTED_QWEN_37_FLASH_MODEL
|
|
42
|
-
} from "./chunk-OR2HNLVO.js";
|
|
43
39
|
import {
|
|
44
40
|
asRecordOrEmpty
|
|
45
41
|
} from "./chunk-KX6YBIHG.js";
|
|
42
|
+
import {
|
|
43
|
+
asOptionalFiniteNumber
|
|
44
|
+
} from "./chunk-WWC7I6TA.js";
|
|
46
45
|
import {
|
|
47
46
|
asTrimmedLowercaseString
|
|
48
47
|
} from "./chunk-QSJ3HFEF.js";
|
|
49
48
|
import {
|
|
50
|
-
|
|
51
|
-
} from "./chunk-
|
|
49
|
+
asOptionalPositiveFiniteNumber
|
|
50
|
+
} from "./chunk-U6VC4MU4.js";
|
|
52
51
|
import {
|
|
53
52
|
isRecord
|
|
54
53
|
} from "./chunk-KUG52IAP.js";
|
|
@@ -98,6 +97,283 @@ function readTitleParams(value) {
|
|
|
98
97
|
return Object.fromEntries(entries);
|
|
99
98
|
}
|
|
100
99
|
|
|
100
|
+
// packages/agent-runtime/shellCommandPolicy.ts
|
|
101
|
+
var DESTRUCTIVE_NEGATION_PATTERNS = [
|
|
102
|
+
"\u522B\u5220",
|
|
103
|
+
"\u522B\u5220\u9664",
|
|
104
|
+
"\u4E0D\u8981\u5220",
|
|
105
|
+
"\u4E0D\u8981\u5220\u9664",
|
|
106
|
+
"\u522B\u4E71\u5220",
|
|
107
|
+
"\u522B\u52A8",
|
|
108
|
+
"\u4E0D\u8981\u52A8",
|
|
109
|
+
"\u4E0D\u8981\u6E05\u7406",
|
|
110
|
+
"\u4E0D\u8981\u6E05\u7A7A",
|
|
111
|
+
"\u4E0D\u8981\u91CD\u7F6E",
|
|
112
|
+
"don't delete",
|
|
113
|
+
"do not delete",
|
|
114
|
+
"don't remove",
|
|
115
|
+
"do not remove",
|
|
116
|
+
"without deleting"
|
|
117
|
+
];
|
|
118
|
+
var EXPLICIT_DESTRUCTIVE_REQUEST_PATTERNS = [
|
|
119
|
+
"\u5220\u9664",
|
|
120
|
+
"\u5220\u6389",
|
|
121
|
+
"\u79FB\u9664",
|
|
122
|
+
"\u6E05\u7406",
|
|
123
|
+
"\u6E05\u7A7A",
|
|
124
|
+
"\u91CD\u7F6E",
|
|
125
|
+
"drop",
|
|
126
|
+
"delete",
|
|
127
|
+
"remove",
|
|
128
|
+
"erase",
|
|
129
|
+
"clean up",
|
|
130
|
+
"reset"
|
|
131
|
+
];
|
|
132
|
+
var SHELL_DESTRUCTIVE_PATTERNS = [
|
|
133
|
+
/\brm\s+(-[^\n\r]*\s+)?/i,
|
|
134
|
+
/\bremove-item\b/i,
|
|
135
|
+
/\bdel\s+\/?[a-z]*\s+/i,
|
|
136
|
+
/\berase\s+/i,
|
|
137
|
+
/\brmdir\s+/i,
|
|
138
|
+
/\brd\s+\/s\b/i,
|
|
139
|
+
/\bgit\s+reset\s+(--hard\b|--merge\b|--keep\b)/i,
|
|
140
|
+
/\bgit\s+clean\s+-[^\n\r]*f/i,
|
|
141
|
+
/\bgit\s+checkout\s+--\b/i,
|
|
142
|
+
/\bgit\s+restore\s+--source=/i
|
|
143
|
+
];
|
|
144
|
+
var SHELL_STRING_EXECUTION_PATTERNS = [
|
|
145
|
+
/\b(?:ba|da|z|k)?sh\b/i,
|
|
146
|
+
/\beval\b/i,
|
|
147
|
+
/\bexec\b/i,
|
|
148
|
+
/\bxargs\b/i,
|
|
149
|
+
/\bsource\s/i,
|
|
150
|
+
/\b(?:node|deno|bun)\s+(?:-\S+\s+)*(?:-e|--eval)\b/i,
|
|
151
|
+
/\bpython[\d.]*\s+(?:-\S+\s+)*-c\b/i,
|
|
152
|
+
/\b(?:perl|ruby)\s+(?:-\S+\s+)*-e\b/i
|
|
153
|
+
];
|
|
154
|
+
var stripQuotedSegments = (text) => {
|
|
155
|
+
let out = "";
|
|
156
|
+
let i = 0;
|
|
157
|
+
while (i < text.length) {
|
|
158
|
+
const ch = text[i];
|
|
159
|
+
if (ch === "\\") {
|
|
160
|
+
out += text.slice(i, i + 2);
|
|
161
|
+
i += 2;
|
|
162
|
+
} else if (ch === "'") {
|
|
163
|
+
const end = text.indexOf("'", i + 1);
|
|
164
|
+
if (end === -1) return out + text.slice(i);
|
|
165
|
+
out += "''";
|
|
166
|
+
i = end + 1;
|
|
167
|
+
} else if (ch === '"') {
|
|
168
|
+
let j = i + 1;
|
|
169
|
+
while (j < text.length && text[j] !== '"') {
|
|
170
|
+
j += text[j] === "\\" ? 2 : 1;
|
|
171
|
+
}
|
|
172
|
+
if (j >= text.length) return out + text.slice(i);
|
|
173
|
+
out += '""';
|
|
174
|
+
i = j + 1;
|
|
175
|
+
} else {
|
|
176
|
+
out += ch;
|
|
177
|
+
i += 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
};
|
|
182
|
+
var normalizeUserInput = (value) => asTrimmedLowercaseString(value);
|
|
183
|
+
var containsAny = (text, patterns) => patterns.some((pattern) => text.includes(pattern));
|
|
184
|
+
var hasExplicitDestructivePermission = (userInput) => {
|
|
185
|
+
const normalized = normalizeUserInput(userInput);
|
|
186
|
+
if (!normalized) return false;
|
|
187
|
+
if (containsAny(normalized, DESTRUCTIVE_NEGATION_PATTERNS)) return false;
|
|
188
|
+
return containsAny(normalized, EXPLICIT_DESTRUCTIVE_REQUEST_PATTERNS);
|
|
189
|
+
};
|
|
190
|
+
var SHELL_COMMAND_ARG_ALIASES = [
|
|
191
|
+
"command",
|
|
192
|
+
"runCommand",
|
|
193
|
+
"run_command",
|
|
194
|
+
"terminalCommand",
|
|
195
|
+
"terminal_command",
|
|
196
|
+
"runInBash",
|
|
197
|
+
"run_in_bash",
|
|
198
|
+
"executeCommand",
|
|
199
|
+
"execute_command",
|
|
200
|
+
"execShell",
|
|
201
|
+
"bash",
|
|
202
|
+
"cmd"
|
|
203
|
+
];
|
|
204
|
+
var resolveShellCommandArg = (args) => {
|
|
205
|
+
if (!args) return void 0;
|
|
206
|
+
for (const key of SHELL_COMMAND_ARG_ALIASES) {
|
|
207
|
+
const value = args[key];
|
|
208
|
+
if (value != null) return value;
|
|
209
|
+
}
|
|
210
|
+
return void 0;
|
|
211
|
+
};
|
|
212
|
+
var buildCombinedShellInput = (args) => {
|
|
213
|
+
const command = resolveShellCommandArg(args);
|
|
214
|
+
return [
|
|
215
|
+
typeof command === "string" ? command : "",
|
|
216
|
+
typeof args.input === "string" ? args.input : ""
|
|
217
|
+
].filter(Boolean).join("\n");
|
|
218
|
+
};
|
|
219
|
+
function resolveScannableShellInput(args) {
|
|
220
|
+
const combined = buildCombinedShellInput(args);
|
|
221
|
+
if (!combined.trim()) return void 0;
|
|
222
|
+
return SHELL_STRING_EXECUTION_PATTERNS.some(
|
|
223
|
+
(pattern) => pattern.test(combined)
|
|
224
|
+
) ? combined : stripQuotedSegments(combined);
|
|
225
|
+
}
|
|
226
|
+
function isDestructiveShellCommand(args) {
|
|
227
|
+
const scannable = resolveScannableShellInput(args);
|
|
228
|
+
if (scannable === void 0) return false;
|
|
229
|
+
return SHELL_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(scannable));
|
|
230
|
+
}
|
|
231
|
+
var LONG_RUNNING_COMMAND_PATTERNS = [
|
|
232
|
+
/\b(run|npm|yarn|pnpm|bun|npx)\s+(run\s+)?(dev|serve|start|watch)\b/i,
|
|
233
|
+
/(^|[\s=&|;])--watch\b/i,
|
|
234
|
+
/\bvite\b.*\b(dev|serve)\b/i,
|
|
235
|
+
/\bwebpack\b.*(?:--watch|serve)\b/i,
|
|
236
|
+
/\bnodemon\b/i,
|
|
237
|
+
/\bgatsby\b.*\b(develop|serve)\b/i,
|
|
238
|
+
/\bnext\b.*\b(dev|start)\b/i,
|
|
239
|
+
/\belectron\b.*\bdev\b/i,
|
|
240
|
+
/\bforever\b/i,
|
|
241
|
+
/\bpm2\b.*\bstart\b/i
|
|
242
|
+
];
|
|
243
|
+
function isLongRunningShellCommand(args) {
|
|
244
|
+
const scannable = resolveScannableShellInput(args);
|
|
245
|
+
if (scannable === void 0) return false;
|
|
246
|
+
return LONG_RUNNING_COMMAND_PATTERNS.some((pattern) => pattern.test(scannable));
|
|
247
|
+
}
|
|
248
|
+
var IMMEDIATE_DETACH_COMMAND_PATTERNS = [
|
|
249
|
+
/\btail\s+(?:-\S*[fF]\S*|--follow)\b/,
|
|
250
|
+
/\bwatch\s+\S/,
|
|
251
|
+
/\bwhile\s+(?:true|:|\[\s*true\s*\])\s*;\s*do\b/
|
|
252
|
+
];
|
|
253
|
+
var IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS = 5;
|
|
254
|
+
var SLEEP_PREFIX_PATTERN = /^(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*(?:nohup\s+|time\s+)*sleep\s+(\S+)/;
|
|
255
|
+
function parseSleepDurationSeconds(token) {
|
|
256
|
+
const match = /^(\d+(?:\.\d+)?)(s|m|h|d)?$/i.exec(token);
|
|
257
|
+
if (!match) return void 0;
|
|
258
|
+
const value = Number(match[1]);
|
|
259
|
+
if (!Number.isFinite(value)) return void 0;
|
|
260
|
+
const unit = (match[2] ?? "s").toLowerCase();
|
|
261
|
+
const factor = unit === "m" ? 60 : unit === "h" ? 3600 : unit === "d" ? 86400 : 1;
|
|
262
|
+
return value * factor;
|
|
263
|
+
}
|
|
264
|
+
function isLongSleepCommand(scannable) {
|
|
265
|
+
const match = SLEEP_PREFIX_PATTERN.exec(scannable.trim());
|
|
266
|
+
if (!match) return false;
|
|
267
|
+
const durationToken = match[1] ?? "";
|
|
268
|
+
if (/^inf(inity)?$/i.test(durationToken)) return true;
|
|
269
|
+
const seconds = parseSleepDurationSeconds(durationToken);
|
|
270
|
+
if (seconds === void 0) return true;
|
|
271
|
+
return seconds > IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS;
|
|
272
|
+
}
|
|
273
|
+
function isImmediateDetachShellCommand(args) {
|
|
274
|
+
const scannable = resolveScannableShellInput(args);
|
|
275
|
+
if (scannable === void 0) return false;
|
|
276
|
+
return isLongSleepCommand(scannable) || LONG_RUNNING_COMMAND_PATTERNS.some((pattern) => pattern.test(scannable)) || IMMEDIATE_DETACH_COMMAND_PATTERNS.some(
|
|
277
|
+
(pattern) => pattern.test(scannable)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
function evaluateShellCommandPolicy(args) {
|
|
281
|
+
if (isDestructiveShellCommand(args) && !hasExplicitDestructivePermission(args.userInput)) {
|
|
282
|
+
return {
|
|
283
|
+
verdict: "forbidden",
|
|
284
|
+
permissionDecision: "ask",
|
|
285
|
+
code: "destructive_action_requires_confirmation",
|
|
286
|
+
reason: "\u5F53\u524D\u8FD0\u884C\u9ED8\u8BA4\u7981\u6B62\u81EA\u52A8\u6267\u884C\u53EF\u80FD\u5220\u9664\u7528\u6237\u5185\u5BB9\u7684 shell \u547D\u4EE4\u3002\u53EA\u6709\u5F53\u7528\u6237\u5728\u5F53\u524D\u8BF7\u6C42\u91CC\u660E\u786E\u8981\u6C42\u5220\u9664/\u6E05\u7406\u65F6\uFF0C\u624D\u80FD\u7EE7\u7EED\uFF1B\u5426\u5219\u8BF7\u505C\u6B62\u5E76\u5148\u8BF4\u660E\u9650\u5236\u3002",
|
|
287
|
+
permissionRequest: {
|
|
288
|
+
id: "permission-shell-destructive-action",
|
|
289
|
+
tool: "execShell",
|
|
290
|
+
action: "destructive_shell_command",
|
|
291
|
+
title: "\u786E\u8BA4\u6267\u884C\u7834\u574F\u6027 shell \u547D\u4EE4",
|
|
292
|
+
body: "\u8BE5\u547D\u4EE4\u53EF\u80FD\u5220\u9664\u6216\u91CD\u7F6E\u7528\u6237\u5185\u5BB9\uFF0C\u9700\u8981\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u624D\u80FD\u6267\u884C\u3002",
|
|
293
|
+
suggestedRule: {
|
|
294
|
+
scope: "once",
|
|
295
|
+
pattern: { capability: "destructive_action", target: "shell_command" }
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
policy: {
|
|
299
|
+
capability: "destructive_action",
|
|
300
|
+
target: "shell_command",
|
|
301
|
+
detail: "execShell destructive command"
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const longRunningHint = isLongRunningShellCommand(args);
|
|
306
|
+
return {
|
|
307
|
+
verdict: "allowed",
|
|
308
|
+
permissionDecision: "allow",
|
|
309
|
+
...longRunningHint ? { longRunningHint: true } : {}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// packages/agent-runtime/processRegistry.ts
|
|
314
|
+
var ProcessRegistry = class {
|
|
315
|
+
processes = /* @__PURE__ */ new Map();
|
|
316
|
+
add(proc) {
|
|
317
|
+
this.processes.set(proc.pid, {
|
|
318
|
+
pid: proc.pid,
|
|
319
|
+
pgid: proc.pgid,
|
|
320
|
+
command: proc.command,
|
|
321
|
+
label: proc.label,
|
|
322
|
+
startedAt: Date.now(),
|
|
323
|
+
status: "running",
|
|
324
|
+
persist: proc.persist ?? false
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
list() {
|
|
328
|
+
return Array.from(this.processes.values()).map((proc) => ({ ...proc }));
|
|
329
|
+
}
|
|
330
|
+
get(pid) {
|
|
331
|
+
const item = this.processes.get(pid);
|
|
332
|
+
return item ? { ...item } : void 0;
|
|
333
|
+
}
|
|
334
|
+
kill(pid, signal = "SIGTERM") {
|
|
335
|
+
const item = this.processes.get(pid);
|
|
336
|
+
if (!item) return false;
|
|
337
|
+
if (item.status === "running") {
|
|
338
|
+
try {
|
|
339
|
+
process.kill(-item.pgid, signal);
|
|
340
|
+
} catch {
|
|
341
|
+
}
|
|
342
|
+
item.status = "stopped";
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
stopAll(signal = "SIGTERM", opts) {
|
|
348
|
+
for (const item of this.processes.values()) {
|
|
349
|
+
if (item.status === "running" && (opts?.includePersist || !item.persist)) {
|
|
350
|
+
try {
|
|
351
|
+
process.kill(-item.pgid, signal);
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
item.status = "stopped";
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
markExited(pid, exitCode) {
|
|
359
|
+
const item = this.processes.get(pid);
|
|
360
|
+
if (item && item.status === "running") {
|
|
361
|
+
item.exitCode = exitCode;
|
|
362
|
+
item.status = exitCode === 0 ? "exited" : "failed";
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
clear() {
|
|
366
|
+
this.processes.clear();
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
var registry = null;
|
|
370
|
+
function getProcessRegistry() {
|
|
371
|
+
if (!registry) {
|
|
372
|
+
registry = new ProcessRegistry();
|
|
373
|
+
}
|
|
374
|
+
return registry;
|
|
375
|
+
}
|
|
376
|
+
|
|
101
377
|
// packages/ai/llm/agentCapabilities.ts
|
|
102
378
|
var ANTIGRAVITY_EFFORT_SUFFIXES = [
|
|
103
379
|
"-tiered",
|
|
@@ -166,36 +442,6 @@ var resolveAgentImageInputSupport = (agent) => {
|
|
|
166
442
|
};
|
|
167
443
|
|
|
168
444
|
// packages/ai/agent/imagePreprocessing.ts
|
|
169
|
-
var DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY = PUBLIC_QWEN_37_FLASH_AGENT_KEY;
|
|
170
|
-
var DESCRIBE_IMAGE_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u56FE\u7247\u63CF\u8FF0\u52A9\u624B\u3002\u8BF7\u6781\u5176\u8BE6\u7EC6\u5730\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\uFF0C\u76EE\u6807\u662F\u8BA9\u4E00\u4E2A\u770B\u4E0D\u5230\u56FE\u7247\u7684 AI \u52A9\u624B\u80FD\u5B8C\u5168\u7406\u89E3\u56FE\u7247\u5185\u5BB9\u3002
|
|
171
|
-
|
|
172
|
-
\u8BF7\u6309\u4EE5\u4E0B\u7ED3\u6784\u63CF\u8FF0\uFF1A
|
|
173
|
-
|
|
174
|
-
1. **\u56FE\u7247\u7C7B\u578B**\uFF1A\u8FD9\u662F\u754C\u9762\u622A\u56FE\u3001\u7167\u7247\u3001\u56FE\u8868\u3001\u6587\u6863\u3001\u8FD8\u662F\u5176\u4ED6\u7C7B\u578B\uFF1F
|
|
175
|
-
|
|
176
|
-
2. **\u5982\u679C\u662F\u754C\u9762\u622A\u56FE**\uFF1A
|
|
177
|
-
- \u5217\u51FA\u6240\u6709\u53EF\u89C1\u7684 UI \u5143\u7D20\uFF1A\u6807\u9898\u680F\u3001\u5BFC\u822A\u83DC\u5355\u3001\u6309\u94AE\u3001\u8F93\u5165\u6846\u3001\u5217\u8868\u9879\u7B49
|
|
178
|
-
- \u9010\u4E2A\u63CF\u8FF0\u6BCF\u4E2A\u5143\u7D20\u7684\u6587\u5B57\u5185\u5BB9\u548C\u4F4D\u7F6E\uFF08\u5982"\u5DE6\u4E0A\u89D2"\u3001"\u53F3\u4FA7\u680F"\uFF09
|
|
179
|
-
- \u63CF\u8FF0\u989C\u8272\u4E3B\u9898\u3001\u5E03\u5C40\u7ED3\u6784\u3001\u5F53\u524D\u72B6\u6001\uFF08\u5982\u9009\u4E2D\u3001hover\u3001loading\uFF09
|
|
180
|
-
- \u5982\u679C\u6709\u5F39\u7A97/\u5BF9\u8BDD\u6846/\u901A\u77E5\uFF0C\u5B8C\u6574\u8BB0\u5F55\u5176\u6587\u5B57\u5185\u5BB9
|
|
181
|
-
|
|
182
|
-
3. **\u5982\u679C\u662F\u751F\u6D3B\u7167\u7247**\uFF1A
|
|
183
|
-
- \u573A\u666F/\u73AF\u5883\uFF1A\u5BA4\u5185/\u5BA4\u5916\u3001\u5730\u70B9\u7C7B\u578B\u3001\u65F6\u95F4\u611F\uFF08\u767D\u5929/\u591C\u665A\uFF09
|
|
184
|
-
- \u4EBA\u7269\uFF1A\u4EBA\u6570\u3001\u59FF\u52BF\u3001\u8868\u60C5\u3001\u7A7F\u7740\u3001\u52A8\u4F5C
|
|
185
|
-
- \u7269\u4F53\uFF1A\u524D\u666F/\u80CC\u666F\u4E2D\u7684\u6240\u6709\u663E\u8457\u7269\u4F53
|
|
186
|
-
- \u5149\u7EBF\u3001\u5929\u6C14\u3001\u6C1B\u56F4
|
|
187
|
-
|
|
188
|
-
4. **\u5982\u679C\u662F\u56FE\u8868/\u6570\u636E\u56FE**\uFF1A
|
|
189
|
-
- \u56FE\u8868\u7C7B\u578B\uFF08\u67F1\u72B6\u56FE/\u6298\u7EBF\u56FE/\u997C\u56FE\u7B49\uFF09
|
|
190
|
-
- \u5750\u6807\u8F74\u6807\u7B7E\u3001\u6570\u503C\u8303\u56F4\u3001\u5355\u4F4D
|
|
191
|
-
- \u5173\u952E\u6570\u636E\u70B9\u548C\u8D8B\u52BF
|
|
192
|
-
- \u56FE\u4F8B\u3001\u6807\u9898
|
|
193
|
-
|
|
194
|
-
5. **\u6240\u6709\u53EF\u89C1\u6587\u5B57**\uFF1A\u9010\u5B57\u8BB0\u5F55\u56FE\u7247\u4E2D\u51FA\u73B0\u7684\u6240\u6709\u6587\u5B57\uFF08UI \u6587\u5B57\u3001\u8DEF\u724C\u3001\u6807\u7B7E\u3001\u6C34\u5370\u7B49\uFF09\uFF0C\u4FDD\u7559\u539F\u6587\u3002
|
|
195
|
-
|
|
196
|
-
6. **\u989C\u8272\u548C\u5E03\u5C40**\uFF1A\u4E3B\u8272\u8C03\u3001\u5E03\u5C40\u7ED3\u6784\u3001\u7A7A\u95F4\u5173\u7CFB\u3002
|
|
197
|
-
|
|
198
|
-
\u8BF7\u5C3D\u53EF\u80FD\u8BE6\u7EC6\uFF0C\u4E0D\u8981\u9057\u6F0F\u4EFB\u4F55\u53EF\u89C1\u4FE1\u606F\u3002`;
|
|
199
445
|
function extractImageUrlsFromMessages(messages) {
|
|
200
446
|
const urls = [];
|
|
201
447
|
for (const msg of messages) {
|
|
@@ -211,162 +457,1255 @@ function extractImageUrlsFromMessages(messages) {
|
|
|
211
457
|
function hasImageInRuntimeMessages(messages) {
|
|
212
458
|
return extractImageUrlsFromMessages(messages).length > 0;
|
|
213
459
|
}
|
|
214
|
-
var
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (imageUrls.length === 0) return messages;
|
|
221
|
-
const key = cacheKeyForUrls(imageUrls);
|
|
222
|
-
let description = IMAGE_DESCRIPTION_CACHE.get(key);
|
|
223
|
-
if (!description) {
|
|
224
|
-
const rawDesc = await describeImage(imageUrls);
|
|
225
|
-
if (rawDesc === null || !rawDesc.trim()) return messages;
|
|
226
|
-
description = rawDesc.trim();
|
|
227
|
-
IMAGE_DESCRIPTION_CACHE.set(key, description);
|
|
228
|
-
}
|
|
229
|
-
const descriptionText = `[\u56FE\u7247\u63CF\u8FF0] ${description}`;
|
|
230
|
-
return messages.map((msg) => {
|
|
231
|
-
if (!Array.isArray(msg.content)) return msg;
|
|
232
|
-
const hasImage = msg.content.some((p) => p?.type === "image_url");
|
|
233
|
-
if (!hasImage) return msg;
|
|
234
|
-
const newContent = msg.content.map((part) => {
|
|
235
|
-
if (part && typeof part === "object" && part.type === "image_url") {
|
|
236
|
-
return { type: "text", text: descriptionText };
|
|
237
|
-
}
|
|
238
|
-
return part;
|
|
239
|
-
}).filter((p) => p !== null && p !== void 0);
|
|
240
|
-
return { ...msg, content: newContent };
|
|
241
|
-
});
|
|
460
|
+
var IMAGE_OMITTED_PLACEHOLDER = "[Image content omitted: model does not support image input]";
|
|
461
|
+
function stripImagePartsFromContent(content) {
|
|
462
|
+
if (!Array.isArray(content)) return content;
|
|
463
|
+
const filtered = content.filter((part) => part?.type !== "image_url");
|
|
464
|
+
if (filtered.length === 0) return IMAGE_OMITTED_PLACEHOLDER;
|
|
465
|
+
return filtered;
|
|
242
466
|
}
|
|
243
|
-
|
|
244
|
-
|
|
467
|
+
function stripImagePartsFromMessages(messages) {
|
|
468
|
+
return messages.map((msg) => ({
|
|
469
|
+
...msg,
|
|
470
|
+
content: stripImagePartsFromContent(msg.content)
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
async function stripImagePartsForTextOnlyAgent(messages, agentConfig) {
|
|
474
|
+
const supportsVision = resolveAgentImageInputSupport(agentConfig);
|
|
475
|
+
const hasImages = hasImageInRuntimeMessages(messages);
|
|
476
|
+
return { messages: !supportsVision && hasImages ? stripImagePartsFromMessages(messages) : messages };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// packages/agent-runtime/workspaceShell.ts
|
|
480
|
+
import { resolve } from "node:path";
|
|
481
|
+
import { spawn as spawnChildProcess } from "node:child_process";
|
|
482
|
+
|
|
483
|
+
// packages/agent-runtime/runtimeCompat.ts
|
|
484
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
485
|
+
import { join } from "node:path";
|
|
486
|
+
import { spawn } from "node:child_process";
|
|
487
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
488
|
+
function isExecutableFile(candidate) {
|
|
489
|
+
if (!existsSync(candidate)) return false;
|
|
245
490
|
try {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return text;
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.warn("[imagePreprocessing] vision describe failed, will fallback:", error);
|
|
491
|
+
return statSync(candidate).isFile();
|
|
492
|
+
} catch {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function resolveExecutableOnPath(name) {
|
|
497
|
+
if (IS_WINDOWS && !/\.[a-z]+$/i.test(name)) {
|
|
498
|
+
const exts = (process.env.PATHEXT || ".EXE;.CMD;.BAT").split(";").filter(Boolean);
|
|
499
|
+
for (const ext of exts) {
|
|
500
|
+
const found = resolveExecutableOnPath(name + ext);
|
|
501
|
+
if (found) return found;
|
|
502
|
+
}
|
|
261
503
|
return null;
|
|
262
504
|
}
|
|
505
|
+
const pathEnv = process.env.PATH ?? "";
|
|
506
|
+
const sep = pathEnv.includes(";") && IS_WINDOWS ? ";" : ":";
|
|
507
|
+
const pathDirs = pathEnv.split(sep).filter(Boolean);
|
|
508
|
+
const fallbackDirs = IS_WINDOWS ? [] : [
|
|
509
|
+
"/opt/homebrew/bin",
|
|
510
|
+
"/usr/local/bin",
|
|
511
|
+
"/usr/bin",
|
|
512
|
+
"/bin",
|
|
513
|
+
`${process.env.HOME ?? ""}/.local/bin`
|
|
514
|
+
].filter(Boolean);
|
|
515
|
+
for (const dir of [...pathDirs, ...fallbackDirs]) {
|
|
516
|
+
const candidate = join(dir, name);
|
|
517
|
+
if (isExecutableFile(candidate)) return candidate;
|
|
518
|
+
}
|
|
519
|
+
return null;
|
|
263
520
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
521
|
+
function createGlob(pattern) {
|
|
522
|
+
const regex = globToRegExp(pattern);
|
|
523
|
+
return {
|
|
524
|
+
match(path) {
|
|
525
|
+
return regex.test(path);
|
|
526
|
+
},
|
|
527
|
+
scanSync(opts) {
|
|
528
|
+
const dot = opts.dot ?? false;
|
|
529
|
+
const results = [];
|
|
530
|
+
walkDir(opts.cwd, "", dot, results);
|
|
531
|
+
return results.filter((rel) => regex.test(rel));
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function walkDir(dir, prefix, dot, out) {
|
|
536
|
+
let entries;
|
|
268
537
|
try {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
538
|
+
entries = readdirSync(dir, { withFileTypes: true, encoding: "utf8" });
|
|
539
|
+
} catch {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
for (const entry of entries) {
|
|
543
|
+
const name = String(entry.name);
|
|
544
|
+
if (!dot && name.startsWith(".")) continue;
|
|
545
|
+
const rel = prefix ? `${prefix}/${name}` : name;
|
|
546
|
+
const full = join(dir, name);
|
|
547
|
+
if (entry.isDirectory()) {
|
|
548
|
+
walkDir(full, rel, dot, out);
|
|
549
|
+
} else if (entry.isFile()) {
|
|
550
|
+
out.push(rel);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function globToRegExp(pattern) {
|
|
555
|
+
const trailingDir = pattern.endsWith("/");
|
|
556
|
+
const src = trailingDir ? pattern.slice(0, -1) : pattern;
|
|
557
|
+
const anchored = src.startsWith("/");
|
|
558
|
+
const body = anchored ? src.slice(1) : src;
|
|
559
|
+
let regex = "^";
|
|
560
|
+
if (!anchored) regex += "(?:.*/)?";
|
|
561
|
+
regex += convertBody(body);
|
|
562
|
+
if (trailingDir) {
|
|
563
|
+
regex += "(?:/.*)?";
|
|
564
|
+
}
|
|
565
|
+
regex += "$";
|
|
566
|
+
return new RegExp(regex);
|
|
567
|
+
}
|
|
568
|
+
function convertBody(p) {
|
|
569
|
+
let out = "";
|
|
570
|
+
let i = 0;
|
|
571
|
+
while (i < p.length) {
|
|
572
|
+
const c = p[i];
|
|
573
|
+
if (c === "*") {
|
|
574
|
+
if (p[i + 1] === "*") {
|
|
575
|
+
i += 2;
|
|
576
|
+
if (p[i] === "/") {
|
|
577
|
+
out += "(?:.*/)?";
|
|
578
|
+
i += 1;
|
|
579
|
+
} else {
|
|
580
|
+
out += ".*";
|
|
581
|
+
}
|
|
582
|
+
} else {
|
|
583
|
+
out += "[^/]*";
|
|
584
|
+
i += 1;
|
|
585
|
+
}
|
|
586
|
+
} else if (c === "?") {
|
|
587
|
+
out += "[^/]";
|
|
588
|
+
i += 1;
|
|
589
|
+
} else if (c === "[") {
|
|
590
|
+
out += "[";
|
|
591
|
+
i += 1;
|
|
592
|
+
if (p[i] === "!") {
|
|
593
|
+
out += "^";
|
|
594
|
+
i += 1;
|
|
595
|
+
}
|
|
596
|
+
while (i < p.length && p[i] !== "]") {
|
|
597
|
+
out += p[i] === "\\" ? "\\\\" : p[i];
|
|
598
|
+
i += 1;
|
|
599
|
+
}
|
|
600
|
+
if (p[i] === "]") {
|
|
601
|
+
out += "]";
|
|
602
|
+
i += 1;
|
|
603
|
+
}
|
|
604
|
+
} else if (c === "{") {
|
|
605
|
+
let depth = 1;
|
|
606
|
+
let j = i + 1;
|
|
607
|
+
let bodyStr = "";
|
|
608
|
+
while (j < p.length && depth > 0) {
|
|
609
|
+
if (p[j] === "{") depth += 1;
|
|
610
|
+
else if (p[j] === "}") depth -= 1;
|
|
611
|
+
if (depth > 0) bodyStr += p[j];
|
|
612
|
+
j += 1;
|
|
613
|
+
}
|
|
614
|
+
const alts = bodyStr.split(",").map((alt) => convertBody(alt));
|
|
615
|
+
out += `(?:${alts.join("|")})`;
|
|
616
|
+
i = j;
|
|
617
|
+
} else {
|
|
618
|
+
out += escapeRegexChar(c);
|
|
619
|
+
i += 1;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return out;
|
|
623
|
+
}
|
|
624
|
+
function escapeRegexChar(c) {
|
|
625
|
+
if ("\\^$.|+()".includes(c)) {
|
|
626
|
+
return `\\${c}`;
|
|
627
|
+
}
|
|
628
|
+
return c;
|
|
629
|
+
}
|
|
630
|
+
function spawnToWebStreams(options) {
|
|
631
|
+
const [command, ...args] = options.cmd;
|
|
632
|
+
const child = spawn(command, args, {
|
|
633
|
+
env: options.env,
|
|
634
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
635
|
+
});
|
|
636
|
+
const exited = new Promise((resolve2) => {
|
|
637
|
+
child.on("exit", (code) => resolve2(code ?? 1));
|
|
638
|
+
child.on("error", () => resolve2(127));
|
|
639
|
+
});
|
|
640
|
+
const toWeb = (stream) => {
|
|
641
|
+
if (!stream) return null;
|
|
642
|
+
return stream.toWeb?.() ?? new ReadableStream({
|
|
643
|
+
start(controller) {
|
|
644
|
+
stream.on("data", (chunk) => controller.enqueue(new Uint8Array(chunk)));
|
|
645
|
+
stream.on("end", () => controller.close());
|
|
646
|
+
stream.on("error", (e) => controller.error(e));
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
};
|
|
650
|
+
return {
|
|
651
|
+
stdout: toWeb(child.stdout),
|
|
652
|
+
stderr: toWeb(child.stderr),
|
|
653
|
+
exited
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// packages/agent-runtime/workspaceShell.ts
|
|
658
|
+
var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
|
|
659
|
+
var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
|
|
660
|
+
var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
|
|
661
|
+
function readTrimmedString(value) {
|
|
662
|
+
return asOptionalTrimmedString(value);
|
|
663
|
+
}
|
|
664
|
+
function extractActivityRefs(rawRefs) {
|
|
665
|
+
if (!Array.isArray(rawRefs)) return void 0;
|
|
666
|
+
const refs = rawRefs.flatMap((entry) => {
|
|
667
|
+
if (typeof entry === "string" && entry.trim()) {
|
|
668
|
+
return [{ type: "file", path: entry.trim() }];
|
|
669
|
+
}
|
|
670
|
+
if (!isRecord(entry)) return [];
|
|
671
|
+
const path = readTrimmedString(entry.path);
|
|
672
|
+
if (path) return [{ type: "file", path }];
|
|
673
|
+
const terminalId = readTrimmedString(entry.id);
|
|
674
|
+
const terminalLabel = readTrimmedString(entry.label);
|
|
675
|
+
if (entry.type === "terminal" || terminalId || terminalLabel) {
|
|
676
|
+
return [{
|
|
677
|
+
type: "terminal",
|
|
678
|
+
...terminalId ? { id: terminalId } : {},
|
|
679
|
+
...terminalLabel ? { label: terminalLabel } : {}
|
|
680
|
+
}];
|
|
681
|
+
}
|
|
682
|
+
if (entry.type === "url" || typeof entry.url === "string") {
|
|
683
|
+
const url = readTrimmedString(entry.url);
|
|
684
|
+
const label = readTrimmedString(entry.label);
|
|
685
|
+
return url ? [{ type: "url", url, ...label ? { label } : {} }] : [];
|
|
686
|
+
}
|
|
687
|
+
return [];
|
|
688
|
+
});
|
|
689
|
+
return refs.length ? refs : void 0;
|
|
690
|
+
}
|
|
691
|
+
function extractActivityAction(rawAction) {
|
|
692
|
+
if (!isRecord(rawAction)) return void 0;
|
|
693
|
+
const title = readTrimmedString(rawAction.title);
|
|
694
|
+
if (!title) return void 0;
|
|
695
|
+
const kind = readTrimmedString(rawAction.kind);
|
|
696
|
+
const detail = readTrimmedString(rawAction.detail);
|
|
697
|
+
const refs = extractActivityRefs(rawAction.refs);
|
|
698
|
+
return {
|
|
699
|
+
title,
|
|
700
|
+
...kind ? { kind } : {},
|
|
701
|
+
...detail ? { detail } : {},
|
|
702
|
+
...refs ? { refs } : {}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function extractActivityPhase(rawPhase) {
|
|
706
|
+
if (!isRecord(rawPhase)) return void 0;
|
|
707
|
+
const title = readTrimmedString(rawPhase.title);
|
|
708
|
+
if (!title) return void 0;
|
|
709
|
+
const id = readTrimmedString(rawPhase.id) || title.toLowerCase().replace(/\s+/g, "-");
|
|
710
|
+
const index = asOptionalFiniteNumber(rawPhase.index);
|
|
711
|
+
const total = asOptionalFiniteNumber(rawPhase.total);
|
|
712
|
+
const status = rawPhase.status === "pending" || rawPhase.status === "running" || rawPhase.status === "success" || rawPhase.status === "failed" ? rawPhase.status : void 0;
|
|
713
|
+
return {
|
|
714
|
+
id,
|
|
715
|
+
title,
|
|
716
|
+
...index !== void 0 ? { index } : {},
|
|
717
|
+
...total !== void 0 ? { total } : {},
|
|
718
|
+
...status ? { status } : {}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function extractActivityPlan(rawPlan) {
|
|
722
|
+
if (!isRecord(rawPlan)) return void 0;
|
|
723
|
+
if (!Array.isArray(rawPlan.phases)) return void 0;
|
|
724
|
+
const phases = rawPlan.phases.flatMap((entry, index) => {
|
|
725
|
+
const phase = extractActivityPhase(entry);
|
|
726
|
+
if (!phase) return [];
|
|
727
|
+
return [{
|
|
728
|
+
id: phase.id,
|
|
729
|
+
title: phase.title,
|
|
730
|
+
index: phase.index ?? index + 1,
|
|
731
|
+
...phase.status ? { status: phase.status } : {}
|
|
732
|
+
}];
|
|
733
|
+
});
|
|
734
|
+
if (phases.length === 0) return void 0;
|
|
735
|
+
const title = readTrimmedString(rawPlan.title);
|
|
736
|
+
return {
|
|
737
|
+
...title ? { title } : {},
|
|
738
|
+
phases
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function extractActivity(parsed) {
|
|
742
|
+
const raw = parsed._activity;
|
|
743
|
+
if (!isRecord(raw)) return void 0;
|
|
744
|
+
const nestedAction = extractActivityAction(raw.action);
|
|
745
|
+
const legacyAction = extractActivityAction(raw);
|
|
746
|
+
const action = nestedAction || legacyAction;
|
|
747
|
+
const phase = extractActivityPhase(raw.phase);
|
|
748
|
+
const plan = extractActivityPlan(raw.plan);
|
|
749
|
+
if (!action && !plan) return void 0;
|
|
750
|
+
return {
|
|
751
|
+
...action ? action : {},
|
|
752
|
+
...phase ? { phase } : {},
|
|
753
|
+
...nestedAction ? { action: nestedAction } : {},
|
|
754
|
+
...plan ? { plan } : {}
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function tokenizeShellPrefix(command) {
|
|
758
|
+
const tokens = [];
|
|
759
|
+
const pattern = /"([^"]*)"|'([^']*)'|([^\s"'|;&<>]+)/g;
|
|
760
|
+
let match;
|
|
761
|
+
while (match = pattern.exec(command)) {
|
|
762
|
+
tokens.push(match[1] ?? match[2] ?? match[3] ?? "");
|
|
763
|
+
}
|
|
764
|
+
return tokens;
|
|
765
|
+
}
|
|
766
|
+
function wrapPowerShellCommand(command) {
|
|
767
|
+
return [
|
|
768
|
+
"[Console]::InputEncoding=[System.Text.Encoding]::UTF8",
|
|
769
|
+
"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
770
|
+
"$OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
771
|
+
"$PSStyle.OutputRendering='PlainText'",
|
|
772
|
+
command
|
|
773
|
+
].join("; ");
|
|
774
|
+
}
|
|
775
|
+
function findPowerShellExecutable() {
|
|
776
|
+
return resolveExecutableOnPath("pwsh") || resolveExecutableOnPath("powershell.exe") || resolveExecutableOnPath("powershell");
|
|
777
|
+
}
|
|
778
|
+
function buildPowerShellCommand(command) {
|
|
779
|
+
const executable = findPowerShellExecutable();
|
|
780
|
+
if (!executable) throw new Error("PowerShell is not available on this machine.");
|
|
781
|
+
return [
|
|
782
|
+
executable,
|
|
783
|
+
"-NoLogo",
|
|
784
|
+
"-NoProfile",
|
|
785
|
+
"-NonInteractive",
|
|
786
|
+
"-ExecutionPolicy",
|
|
787
|
+
"Bypass",
|
|
788
|
+
"-Command",
|
|
789
|
+
wrapPowerShellCommand(command)
|
|
790
|
+
];
|
|
791
|
+
}
|
|
792
|
+
function buildBashCommand(command) {
|
|
793
|
+
const executable = resolveExecutableOnPath("bash") || resolveExecutableOnPath("sh");
|
|
794
|
+
if (!executable) throw new Error("bash/sh is not available on this machine.");
|
|
795
|
+
return [executable, "-lc", command];
|
|
796
|
+
}
|
|
797
|
+
function buildWorkspaceShellCommand(args) {
|
|
798
|
+
if (args.shell === "powershell") return buildPowerShellCommand(args.command);
|
|
799
|
+
if (args.shell === "bash") return buildBashCommand(args.command);
|
|
800
|
+
return process.platform === "win32" ? buildPowerShellCommand(args.command) : buildBashCommand(args.command);
|
|
801
|
+
}
|
|
802
|
+
function findWorkspaceShellEscapeToken(command) {
|
|
803
|
+
const tokens = tokenizeShellPrefix(command);
|
|
804
|
+
for (const token of tokens) {
|
|
805
|
+
if (token === ".." || token.startsWith("../") || token.startsWith("..\\") || token.includes("/../") || token.includes("\\..\\")) {
|
|
806
|
+
return token;
|
|
807
|
+
}
|
|
808
|
+
if (token === "~" || token.startsWith("~/") || token.startsWith("~\\")) {
|
|
809
|
+
return token;
|
|
810
|
+
}
|
|
811
|
+
if (token === "/" || token.startsWith("/") || /^[A-Za-z]:[\\/]/.test(token) || token.startsWith("\\\\")) {
|
|
812
|
+
return token;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
function buildWorkspaceShellEscapeBlockedResult(args) {
|
|
818
|
+
return {
|
|
819
|
+
content: [
|
|
820
|
+
"workspace_shell_escape_blocked",
|
|
821
|
+
`blockedToken: ${args.token}`,
|
|
822
|
+
`command: ${args.command}`,
|
|
823
|
+
"Use paths relative to the authorized folder only.",
|
|
824
|
+
"exitCode: 126"
|
|
825
|
+
].join("\n"),
|
|
826
|
+
metadata: {
|
|
827
|
+
exitCode: 126,
|
|
828
|
+
workspaceShellEscapeBlocked: true,
|
|
829
|
+
blockedToken: args.token
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function extractInteractiveGhAuthCommand(command) {
|
|
834
|
+
const tokens = tokenizeShellPrefix(command);
|
|
835
|
+
if (tokens[0] !== "gh" || tokens[1] !== "auth") return null;
|
|
836
|
+
const subcommand = tokens[2];
|
|
837
|
+
if (subcommand !== "login" && subcommand !== "refresh") return null;
|
|
838
|
+
if (tokens.includes("--help")) return null;
|
|
839
|
+
const result = ["gh", "auth", subcommand];
|
|
840
|
+
for (let index = 3; index < tokens.length; index += 1) {
|
|
841
|
+
const token = tokens[index];
|
|
842
|
+
if (!token) continue;
|
|
843
|
+
if (token === "-h" || token === "--hostname" || token === "-s" || token === "--scopes" || token === "--remove-scopes" || token === "-r") {
|
|
844
|
+
const value = tokens[index + 1];
|
|
845
|
+
if (value) {
|
|
846
|
+
result.push(token, value);
|
|
847
|
+
index += 1;
|
|
848
|
+
}
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
if (token === "--clipboard" || token === "-c" || token === "--insecure-storage" || token === "--reset-scopes") {
|
|
852
|
+
result.push(token);
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (token.startsWith("--hostname=") || token.startsWith("--scopes=") || token.startsWith("--remove-scopes=")) {
|
|
856
|
+
result.push(token);
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return result.join(" ");
|
|
861
|
+
}
|
|
862
|
+
function buildInteractiveCommandBlockedResult(command) {
|
|
863
|
+
const argv = tokenizeShellPrefix(command);
|
|
864
|
+
return {
|
|
865
|
+
content: [
|
|
866
|
+
"action_gate: handoff",
|
|
867
|
+
`command: ${command}`,
|
|
868
|
+
"Run this in the current TUI terminal, then resume the agent turn.",
|
|
869
|
+
"exitCode: 130"
|
|
870
|
+
].join("\n"),
|
|
871
|
+
metadata: {
|
|
872
|
+
exitCode: 130,
|
|
873
|
+
timedOut: false,
|
|
874
|
+
actionGate: {
|
|
875
|
+
id: `gate-${Date.now().toString(36)}-terminal-handoff`,
|
|
876
|
+
kind: "handoff",
|
|
877
|
+
title: "This command requires an interactive terminal.",
|
|
878
|
+
body: "Complete it in the terminal, then nolo will continue.",
|
|
879
|
+
payload: {
|
|
880
|
+
command: argv,
|
|
881
|
+
displayCommand: command
|
|
882
|
+
}
|
|
284
883
|
},
|
|
285
|
-
|
|
286
|
-
|
|
884
|
+
reason: "interactive-command-requires-terminal"
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
function resolveExecShellTimeoutMs(override) {
|
|
889
|
+
const fromOverride = asOptionalPositiveFiniteNumber(override);
|
|
890
|
+
if (fromOverride !== void 0) return fromOverride;
|
|
891
|
+
const raw = process.env[EXEC_SHELL_TIMEOUT_ENV];
|
|
892
|
+
if (raw === void 0) return void 0;
|
|
893
|
+
return asOptionalPositiveFiniteNumber(Number(raw));
|
|
894
|
+
}
|
|
895
|
+
function resolveDetachMs(override) {
|
|
896
|
+
if (typeof override === "number" && Number.isFinite(override) && override >= 0) {
|
|
897
|
+
return override;
|
|
898
|
+
}
|
|
899
|
+
const raw = process.env[EXEC_SHELL_DETACH_ENV];
|
|
900
|
+
if (raw === void 0) return DEFAULT_EXEC_SHELL_DETACH_MS;
|
|
901
|
+
const parsed = asOptionalPositiveFiniteNumber(Number(raw));
|
|
902
|
+
return parsed === void 0 ? DEFAULT_EXEC_SHELL_DETACH_MS : parsed;
|
|
903
|
+
}
|
|
904
|
+
function truncateToolOutput(value, limit = 2e4) {
|
|
905
|
+
if (value.length <= limit) return value;
|
|
906
|
+
const approxMarkerLen = 40;
|
|
907
|
+
if (limit <= approxMarkerLen) return value.slice(0, limit);
|
|
908
|
+
const remaining = limit - approxMarkerLen;
|
|
909
|
+
const headSize = Math.floor(remaining * 0.3);
|
|
910
|
+
const tailSize = remaining - headSize;
|
|
911
|
+
const head = value.slice(0, headSize);
|
|
912
|
+
const tail = value.slice(-tailSize);
|
|
913
|
+
const actualRemoved = value.length - headSize - tailSize;
|
|
914
|
+
return `${head}
|
|
915
|
+
|
|
916
|
+
[... truncated ${actualRemoved} chars ...]
|
|
917
|
+
|
|
918
|
+
${tail}`;
|
|
919
|
+
}
|
|
920
|
+
function readNodeStream(stream) {
|
|
921
|
+
if (!stream) return Promise.resolve("");
|
|
922
|
+
return new Promise((resolveStream, rejectStream) => {
|
|
923
|
+
const chunks = [];
|
|
924
|
+
stream.on("data", (chunk) => {
|
|
925
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
926
|
+
});
|
|
927
|
+
stream.on("error", rejectStream);
|
|
928
|
+
stream.on("end", () => {
|
|
929
|
+
resolveStream(Buffer.concat(chunks).toString("utf8"));
|
|
930
|
+
});
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
function waitForNodeProcessExit(proc) {
|
|
934
|
+
return new Promise((resolveExit, rejectExit) => {
|
|
935
|
+
proc.on("error", rejectExit);
|
|
936
|
+
proc.on("close", (code) => {
|
|
937
|
+
resolveExit(code ?? 0);
|
|
287
938
|
});
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
function deriveLabel(command) {
|
|
942
|
+
const trimmed = command.trim();
|
|
943
|
+
if (!trimmed) return "process";
|
|
944
|
+
const tokens = trimmed.split(/\s+/);
|
|
945
|
+
const lastToken = tokens[tokens.length - 1] ?? trimmed;
|
|
946
|
+
const labelCandidate = lastToken.split(/[/\\]/).pop() || lastToken;
|
|
947
|
+
return labelCandidate || trimmed;
|
|
948
|
+
}
|
|
949
|
+
function isSpawnFailureError(error) {
|
|
950
|
+
if (!error || typeof error !== "object") return false;
|
|
951
|
+
const err = error;
|
|
952
|
+
if (err.code === "ENOENT") return true;
|
|
953
|
+
const message = toErrorMessage(error).toLowerCase();
|
|
954
|
+
return message.includes("enoent") || message.includes("executable not found") || message.includes("spawn") && message.includes("not found");
|
|
955
|
+
}
|
|
956
|
+
function spawnFailedCommandResult(error, outputLimit) {
|
|
957
|
+
const stderr = `spawn failed: ${toErrorMessage(error)}
|
|
958
|
+
`;
|
|
959
|
+
return {
|
|
960
|
+
stdout: "",
|
|
961
|
+
stderr,
|
|
962
|
+
exitCode: 127,
|
|
963
|
+
timedOut: false,
|
|
964
|
+
spawnFailed: true,
|
|
965
|
+
content: truncateToolOutput(
|
|
966
|
+
[`stderr:
|
|
967
|
+
${stderr.trim()}`, "exitCode: 127"].filter(Boolean).join("\n\n"),
|
|
968
|
+
outputLimit
|
|
969
|
+
)
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
async function runWorkspaceCommand(args) {
|
|
973
|
+
const timeoutMs = asOptionalPositiveFiniteNumber(args.timeoutMs);
|
|
974
|
+
const detached = process.platform !== "win32";
|
|
975
|
+
const command = [
|
|
976
|
+
...args.commandPrefix ?? [],
|
|
977
|
+
...args.command
|
|
978
|
+
];
|
|
979
|
+
const proc = spawnChildProcess(command[0] ?? "", command.slice(1), {
|
|
980
|
+
cwd: resolve(args.workspaceRoot),
|
|
981
|
+
stdio: [
|
|
982
|
+
args.stdin === void 0 ? "ignore" : "pipe",
|
|
983
|
+
"pipe",
|
|
984
|
+
"pipe"
|
|
985
|
+
],
|
|
986
|
+
detached
|
|
987
|
+
});
|
|
988
|
+
if (args.stdin !== void 0 && proc.stdin) {
|
|
989
|
+
proc.stdin.write(args.stdin);
|
|
990
|
+
proc.stdin.end();
|
|
991
|
+
}
|
|
992
|
+
const exitPromise = waitForNodeProcessExit(proc);
|
|
993
|
+
const cleanupChildOnHostSignal = (signal) => {
|
|
994
|
+
if (typeof proc.pid === "number") {
|
|
995
|
+
try {
|
|
996
|
+
process.kill(-proc.pid, signal);
|
|
997
|
+
} catch {
|
|
998
|
+
}
|
|
291
999
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
1000
|
+
};
|
|
1001
|
+
if (detached) {
|
|
1002
|
+
process.once("SIGHUP", cleanupChildOnHostSignal);
|
|
1003
|
+
process.once("SIGTERM", cleanupChildOnHostSignal);
|
|
1004
|
+
process.once("SIGINT", cleanupChildOnHostSignal);
|
|
1005
|
+
}
|
|
1006
|
+
const detachSignalCleanup = () => {
|
|
1007
|
+
if (!detached) return;
|
|
1008
|
+
process.removeListener("SIGHUP", cleanupChildOnHostSignal);
|
|
1009
|
+
process.removeListener("SIGTERM", cleanupChildOnHostSignal);
|
|
1010
|
+
process.removeListener("SIGINT", cleanupChildOnHostSignal);
|
|
1011
|
+
};
|
|
1012
|
+
exitPromise.then(detachSignalCleanup, detachSignalCleanup);
|
|
1013
|
+
const stdoutPromise = readNodeStream(proc.stdout);
|
|
1014
|
+
const stderrPromise = readNodeStream(proc.stderr);
|
|
1015
|
+
const killChild = (signal) => {
|
|
1016
|
+
if (detached && typeof proc.pid === "number") {
|
|
1017
|
+
try {
|
|
1018
|
+
process.kill(-proc.pid, signal);
|
|
1019
|
+
return;
|
|
1020
|
+
} catch {
|
|
1021
|
+
}
|
|
296
1022
|
}
|
|
297
|
-
|
|
1023
|
+
try {
|
|
1024
|
+
proc.kill(signal);
|
|
1025
|
+
} catch {
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
let timeout;
|
|
1029
|
+
let detachTimer;
|
|
1030
|
+
const timeoutResult = Symbol("timeout");
|
|
1031
|
+
const abortResult = Symbol("abort");
|
|
1032
|
+
const detachResult = Symbol("detach");
|
|
1033
|
+
let abortListener;
|
|
1034
|
+
const abortPromise = args.abortSignal ? new Promise((resolveAbort) => {
|
|
1035
|
+
if (args.abortSignal.aborted) {
|
|
1036
|
+
resolveAbort(abortResult);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
abortListener = () => resolveAbort(abortResult);
|
|
1040
|
+
args.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
1041
|
+
}) : null;
|
|
1042
|
+
const effectiveDetachMs = resolveDetachMs(args.detachMs);
|
|
1043
|
+
const detachPromise = new Promise((resolveDetach) => {
|
|
1044
|
+
detachTimer = setTimeout(() => resolveDetach(detachResult), effectiveDetachMs);
|
|
1045
|
+
});
|
|
1046
|
+
const exitOrTimeoutPromise = timeoutMs ? Promise.race([
|
|
1047
|
+
exitPromise,
|
|
1048
|
+
new Promise((resolveTimeout) => {
|
|
1049
|
+
timeout = setTimeout(() => resolveTimeout(timeoutResult), timeoutMs);
|
|
1050
|
+
})
|
|
1051
|
+
]) : exitPromise;
|
|
1052
|
+
const raceCandidates = [
|
|
1053
|
+
exitOrTimeoutPromise,
|
|
1054
|
+
detachPromise
|
|
1055
|
+
];
|
|
1056
|
+
if (abortPromise) {
|
|
1057
|
+
raceCandidates.push(abortPromise);
|
|
1058
|
+
}
|
|
1059
|
+
let raceWinner;
|
|
1060
|
+
try {
|
|
1061
|
+
raceWinner = await Promise.race(raceCandidates);
|
|
298
1062
|
} catch (error) {
|
|
299
|
-
|
|
300
|
-
|
|
1063
|
+
if (timeout) clearTimeout(timeout);
|
|
1064
|
+
if (detachTimer) clearTimeout(detachTimer);
|
|
1065
|
+
if (abortListener && args.abortSignal) {
|
|
1066
|
+
args.abortSignal.removeEventListener("abort", abortListener);
|
|
1067
|
+
}
|
|
1068
|
+
if (isSpawnFailureError(error)) {
|
|
1069
|
+
return spawnFailedCommandResult(error, args.outputLimit);
|
|
1070
|
+
}
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1073
|
+
if (timeout) clearTimeout(timeout);
|
|
1074
|
+
if (detachTimer) clearTimeout(detachTimer);
|
|
1075
|
+
if (abortListener && args.abortSignal) {
|
|
1076
|
+
args.abortSignal.removeEventListener("abort", abortListener);
|
|
1077
|
+
}
|
|
1078
|
+
const timedOut = raceWinner === timeoutResult;
|
|
1079
|
+
const aborted = raceWinner === abortResult;
|
|
1080
|
+
const detachedProcess = raceWinner === detachResult;
|
|
1081
|
+
if (timedOut || aborted) {
|
|
1082
|
+
killChild("SIGTERM");
|
|
1083
|
+
const forceKillTimer = setTimeout(() => {
|
|
1084
|
+
killChild("SIGKILL");
|
|
1085
|
+
}, 500);
|
|
1086
|
+
try {
|
|
1087
|
+
await exitPromise;
|
|
1088
|
+
} catch {
|
|
1089
|
+
} finally {
|
|
1090
|
+
clearTimeout(forceKillTimer);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (detachedProcess) {
|
|
1094
|
+
exitPromise.catch(() => {
|
|
1095
|
+
});
|
|
1096
|
+
const pid = typeof proc.pid === "number" ? proc.pid : 0;
|
|
1097
|
+
const label = deriveLabel(args.command.join(" "));
|
|
1098
|
+
const pgid = pid;
|
|
1099
|
+
const registry2 = getProcessRegistry();
|
|
1100
|
+
registry2.add({ pid, pgid, command: args.command.join(" "), label });
|
|
1101
|
+
proc.on("close", (code) => {
|
|
1102
|
+
detachSignalCleanup();
|
|
1103
|
+
registry2.markExited(pid, code ?? 1);
|
|
1104
|
+
});
|
|
1105
|
+
const immediate = effectiveDetachMs === 0;
|
|
1106
|
+
const reason = immediate ? `command looks long-running; moved to background immediately (pid=${pid}, label=${label})` : `command detached to background after ${effectiveDetachMs}ms (pid=${pid}, label=${label})`;
|
|
1107
|
+
return {
|
|
1108
|
+
stdout: "",
|
|
1109
|
+
stderr: `${reason}
|
|
1110
|
+
Use listProcesses to inspect or stop it.`,
|
|
1111
|
+
exitCode: 0,
|
|
1112
|
+
timedOut: false,
|
|
1113
|
+
detached: true,
|
|
1114
|
+
pid,
|
|
1115
|
+
label,
|
|
1116
|
+
content: JSON.stringify({
|
|
1117
|
+
detached: true,
|
|
1118
|
+
pid,
|
|
1119
|
+
label,
|
|
1120
|
+
status: "running",
|
|
1121
|
+
...immediate ? { reason: "long-running-command" } : {}
|
|
1122
|
+
})
|
|
1123
|
+
};
|
|
301
1124
|
}
|
|
1125
|
+
const [stdout, rawStderr] = await Promise.all([
|
|
1126
|
+
stdoutPromise,
|
|
1127
|
+
stderrPromise
|
|
1128
|
+
]);
|
|
1129
|
+
const exitCode = aborted ? 130 : timedOut ? 124 : Number(raceWinner);
|
|
1130
|
+
const stderr = aborted ? `${rawStderr.trim() ? `${rawStderr.trim()}
|
|
1131
|
+
` : ""}command aborted by signal
|
|
1132
|
+
` : timedOut ? `${rawStderr.trim() ? `${rawStderr.trim()}
|
|
1133
|
+
` : ""}command timed out after ${timeoutMs ?? "unknown"}ms
|
|
1134
|
+
` : rawStderr;
|
|
1135
|
+
return {
|
|
1136
|
+
stdout,
|
|
1137
|
+
stderr,
|
|
1138
|
+
exitCode,
|
|
1139
|
+
timedOut,
|
|
1140
|
+
aborted,
|
|
1141
|
+
content: truncateToolOutput([
|
|
1142
|
+
stdout.trim() ? `stdout:
|
|
1143
|
+
${stdout.trim()}` : "",
|
|
1144
|
+
stderr.trim() ? `stderr:
|
|
1145
|
+
${stderr.trim()}` : "",
|
|
1146
|
+
`exitCode: ${exitCode}`
|
|
1147
|
+
].filter(Boolean).join("\n\n"), args.outputLimit)
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// packages/agent-runtime/capabilities/execShellCapability.ts
|
|
1152
|
+
function buildLocalExecShellContext(args) {
|
|
1153
|
+
return {
|
|
1154
|
+
workspaceRoot: args.workspaceRoot,
|
|
1155
|
+
...args.commandTimeoutMs !== void 0 ? { commandTimeoutMs: args.commandTimeoutMs } : {},
|
|
1156
|
+
...args.commandOutputLimit !== void 0 ? { commandOutputLimit: args.commandOutputLimit } : {},
|
|
1157
|
+
...args.commandPrefix ? { commandPrefix: args.commandPrefix } : {},
|
|
1158
|
+
...args.restrictToWorkspace !== void 0 ? { restrictToWorkspace: args.restrictToWorkspace } : {},
|
|
1159
|
+
...args.abortSignal ? { abortSignal: args.abortSignal } : {},
|
|
1160
|
+
...args.detachMs !== void 0 ? { detachMs: args.detachMs } : {},
|
|
1161
|
+
...args.confirmed !== void 0 ? { confirmed: args.confirmed } : {},
|
|
1162
|
+
...args.enableDestructiveShellGuard !== void 0 ? { enableDestructiveShellGuard: args.enableDestructiveShellGuard } : {},
|
|
1163
|
+
...args.confirmDestructiveAction ? { confirmDestructiveAction: args.confirmDestructiveAction } : {},
|
|
1164
|
+
...args.blockDestructiveWithoutConfirmation !== void 0 ? { blockDestructiveWithoutConfirmation: args.blockDestructiveWithoutConfirmation } : {},
|
|
1165
|
+
...args.onInvoke ? { onInvoke: args.onInvoke } : {}
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
function buildExecShellToolDefinition(toolName = "execShell") {
|
|
1169
|
+
return {
|
|
1170
|
+
type: "function",
|
|
1171
|
+
function: {
|
|
1172
|
+
name: toolName,
|
|
1173
|
+
description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess.`,
|
|
1174
|
+
parameters: {
|
|
1175
|
+
type: "object",
|
|
1176
|
+
properties: {
|
|
1177
|
+
command: {
|
|
1178
|
+
type: "string",
|
|
1179
|
+
description: "Shell command to run (non-empty)."
|
|
1180
|
+
},
|
|
1181
|
+
cmd: {
|
|
1182
|
+
type: "string",
|
|
1183
|
+
description: "Compatibility alias for command."
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
302
1189
|
}
|
|
303
|
-
function
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
parts.push({ type: "image_url", image_url: { url } });
|
|
1190
|
+
function normalizeExecShellInput(input) {
|
|
1191
|
+
if (input === null || input === void 0) {
|
|
1192
|
+
throw new Error("execShell requires a non-empty command.");
|
|
307
1193
|
}
|
|
308
|
-
|
|
1194
|
+
if (typeof input === "string") {
|
|
1195
|
+
const trimmed = input.trim();
|
|
1196
|
+
if (!trimmed) {
|
|
1197
|
+
throw new Error("execShell requires a non-empty command.");
|
|
1198
|
+
}
|
|
1199
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
1200
|
+
try {
|
|
1201
|
+
const parsed = JSON.parse(trimmed);
|
|
1202
|
+
if (parsed && typeof parsed === "object") {
|
|
1203
|
+
return normalizeExecShellInput(parsed);
|
|
1204
|
+
}
|
|
1205
|
+
} catch {
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return { command: trimmed };
|
|
1209
|
+
}
|
|
1210
|
+
if (typeof input === "object") {
|
|
1211
|
+
const record = input;
|
|
1212
|
+
let rawCommand = resolveShellCommandArg(record);
|
|
1213
|
+
if (typeof rawCommand !== "string" || !rawCommand.trim()) {
|
|
1214
|
+
const fallback = record.command ?? record.cmd;
|
|
1215
|
+
if (typeof fallback === "string" && fallback.trim()) {
|
|
1216
|
+
rawCommand = fallback;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
const command = typeof rawCommand === "string" ? rawCommand.trim() : "";
|
|
1220
|
+
if (!command) {
|
|
1221
|
+
throw new Error("execShell requires a non-empty command.");
|
|
1222
|
+
}
|
|
1223
|
+
const activity = extractActivity(record);
|
|
1224
|
+
return {
|
|
1225
|
+
command,
|
|
1226
|
+
...record.shell !== void 0 ? { shell: record.shell } : {},
|
|
1227
|
+
...activity ? { activity } : {},
|
|
1228
|
+
...record.input !== void 0 ? { input: record.input } : {}
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
throw new Error("execShell requires a non-empty command.");
|
|
1232
|
+
}
|
|
1233
|
+
var execShellCapability = {
|
|
1234
|
+
name: "execShell",
|
|
1235
|
+
getToolDefinition(toolName = "execShell") {
|
|
1236
|
+
return buildExecShellToolDefinition(toolName);
|
|
1237
|
+
},
|
|
1238
|
+
normalizeInput(input) {
|
|
1239
|
+
return normalizeExecShellInput(input);
|
|
1240
|
+
},
|
|
1241
|
+
async invoke(ctx, normalized) {
|
|
1242
|
+
const workspaceRoot = ctx.workspaceRoot || process.cwd();
|
|
1243
|
+
const command = normalized.command;
|
|
1244
|
+
if (ctx.restrictToWorkspace) {
|
|
1245
|
+
const escapeToken = findWorkspaceShellEscapeToken(command);
|
|
1246
|
+
if (escapeToken) {
|
|
1247
|
+
return buildWorkspaceShellEscapeBlockedResult({ command, token: escapeToken });
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
const interactiveAuthCommand = extractInteractiveGhAuthCommand(command);
|
|
1251
|
+
if (interactiveAuthCommand) {
|
|
1252
|
+
const blocked = buildInteractiveCommandBlockedResult(interactiveAuthCommand);
|
|
1253
|
+
return {
|
|
1254
|
+
...blocked,
|
|
1255
|
+
metadata: {
|
|
1256
|
+
...blocked.metadata,
|
|
1257
|
+
...normalized.activity ? { activity: normalized.activity } : {}
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
const result = await runWorkspaceCommand({
|
|
1262
|
+
workspaceRoot,
|
|
1263
|
+
command: buildWorkspaceShellCommand({
|
|
1264
|
+
toolName: "execShell",
|
|
1265
|
+
command,
|
|
1266
|
+
shell: normalized.shell
|
|
1267
|
+
}),
|
|
1268
|
+
timeoutMs: resolveExecShellTimeoutMs(ctx.commandTimeoutMs),
|
|
1269
|
+
outputLimit: ctx.commandOutputLimit,
|
|
1270
|
+
commandPrefix: ctx.commandPrefix,
|
|
1271
|
+
abortSignal: ctx.abortSignal,
|
|
1272
|
+
detachMs: isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
|
|
1273
|
+
stdin: typeof normalized.input === "string" ? normalized.input : void 0
|
|
1274
|
+
});
|
|
1275
|
+
return {
|
|
1276
|
+
content: result.content,
|
|
1277
|
+
metadata: {
|
|
1278
|
+
command,
|
|
1279
|
+
exitCode: result.exitCode,
|
|
1280
|
+
timedOut: result.timedOut,
|
|
1281
|
+
...result.aborted ? { aborted: true } : {},
|
|
1282
|
+
...result.detached ? { detached: true, pid: result.pid, label: result.label, status: "running" } : {},
|
|
1283
|
+
...normalized.activity ? { activity: normalized.activity } : {}
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
// packages/agent-runtime/capabilities/capabilityPolicy.ts
|
|
1290
|
+
function toDisplayCommand(command) {
|
|
1291
|
+
if (typeof command === "string") {
|
|
1292
|
+
const trimmed = command.trim();
|
|
1293
|
+
return trimmed ? trimmed : void 0;
|
|
1294
|
+
}
|
|
1295
|
+
if (Array.isArray(command)) {
|
|
1296
|
+
const joined = command.map((item) => typeof item === "string" ? item : String(item)).filter((item) => item.length > 0).join(" ").trim();
|
|
1297
|
+
return joined ? joined : void 0;
|
|
1298
|
+
}
|
|
1299
|
+
return void 0;
|
|
309
1300
|
}
|
|
310
|
-
function
|
|
311
|
-
const key = agentKey ?? DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY;
|
|
312
|
-
const config = resolveBuiltinPlatformAgentConfig(key);
|
|
1301
|
+
function buildDestructiveShellPermissionRequest(command) {
|
|
313
1302
|
return {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
1303
|
+
id: "permission-shell-destructive-action",
|
|
1304
|
+
tool: "execShell",
|
|
1305
|
+
action: "destructive_shell_command",
|
|
1306
|
+
title: "\u786E\u8BA4\u6267\u884C\u7834\u574F\u6027 shell \u547D\u4EE4",
|
|
1307
|
+
body: "\u8BE5\u547D\u4EE4\u53EF\u80FD\u5220\u9664\u6216\u91CD\u7F6E\u7528\u6237\u5185\u5BB9\uFF0C\u9700\u8981\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u624D\u80FD\u6267\u884C\u3002",
|
|
1308
|
+
...command ? { command } : {},
|
|
1309
|
+
suggestedRule: {
|
|
1310
|
+
scope: "once",
|
|
1311
|
+
pattern: { capability: "destructive_action", target: "shell_command" }
|
|
1312
|
+
}
|
|
317
1313
|
};
|
|
318
1314
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
1315
|
+
function buildDestructiveShellBlockedError(reason, request) {
|
|
1316
|
+
const error = new Error(reason);
|
|
1317
|
+
error.code = "destructive_action_requires_confirmation";
|
|
1318
|
+
error.policy = {
|
|
1319
|
+
capability: "destructive_action",
|
|
1320
|
+
target: "shell_command",
|
|
1321
|
+
detail: "execShell destructive command"
|
|
1322
|
+
};
|
|
1323
|
+
error.permissionRequest = request;
|
|
1324
|
+
return error;
|
|
325
1325
|
}
|
|
326
|
-
function
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
1326
|
+
async function evaluateExecShellDestructiveGuard(input, ctx) {
|
|
1327
|
+
const guardEnabled = ctx.enableDestructiveShellGuard !== false;
|
|
1328
|
+
if (!guardEnabled || ctx.confirmed) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
const rawCommand = resolveShellCommandArg(
|
|
1332
|
+
typeof input === "object" && input !== null ? input : { command: input }
|
|
1333
|
+
);
|
|
1334
|
+
const extraInput = typeof input === "object" && input !== null ? input.input : void 0;
|
|
1335
|
+
if (!isDestructiveShellCommand({ command: rawCommand, input: extraInput })) {
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
const displayCommand = toDisplayCommand(rawCommand);
|
|
1339
|
+
const request = buildDestructiveShellPermissionRequest(displayCommand);
|
|
1340
|
+
if (!ctx.confirmDestructiveAction) {
|
|
1341
|
+
if (ctx.blockDestructiveWithoutConfirmation) {
|
|
1342
|
+
throw buildDestructiveShellBlockedError(
|
|
1343
|
+
"destructive shell command blocked: no confirmation channel available",
|
|
1344
|
+
request
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
const confirmed = await ctx.confirmDestructiveAction(request);
|
|
1350
|
+
if (!confirmed) {
|
|
1351
|
+
throw buildDestructiveShellBlockedError(
|
|
1352
|
+
"destructive shell command blocked: user declined confirmation",
|
|
1353
|
+
request
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
331
1356
|
}
|
|
332
|
-
async function
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
1357
|
+
async function evaluateCapabilityPolicy(capability, normalizedInput, ctx) {
|
|
1358
|
+
if (capability.name === "execShell") {
|
|
1359
|
+
await evaluateExecShellDestructiveGuard(normalizedInput, ctx);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// packages/agent-runtime/capabilities/agentRunService.ts
|
|
1364
|
+
var activityCounter = 0;
|
|
1365
|
+
function generateActivityId(prefix = "act") {
|
|
1366
|
+
activityCounter = (activityCounter + 1) % 1e6;
|
|
1367
|
+
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}-${activityCounter}`;
|
|
1368
|
+
}
|
|
1369
|
+
function combineSignals(signals) {
|
|
1370
|
+
const activeSignals = signals.filter((s) => Boolean(s));
|
|
1371
|
+
if (activeSignals.length === 0) return void 0;
|
|
1372
|
+
if (activeSignals.length === 1) return activeSignals[0];
|
|
1373
|
+
if (typeof AbortSignal.any === "function") {
|
|
1374
|
+
return AbortSignal.any(activeSignals);
|
|
1375
|
+
}
|
|
1376
|
+
const controller = new AbortController();
|
|
1377
|
+
for (const s of activeSignals) {
|
|
1378
|
+
if (s.aborted) {
|
|
1379
|
+
controller.abort(s.reason);
|
|
1380
|
+
return controller.signal;
|
|
1381
|
+
}
|
|
1382
|
+
s.addEventListener("abort", () => controller.abort(s.reason), { once: true });
|
|
1383
|
+
}
|
|
1384
|
+
return controller.signal;
|
|
1385
|
+
}
|
|
1386
|
+
async function executeAgentRunLifecycle(service, input, ctx = {}) {
|
|
1387
|
+
const agentKey = input.agentKey ?? input.agentId;
|
|
1388
|
+
if (!agentKey || typeof agentKey !== "string" || !agentKey.trim()) {
|
|
1389
|
+
throw new Error("agents.run: \u7F3A\u5C11\u6709\u6548\u7684 agentId \u6216 agentKey\u3002");
|
|
1390
|
+
}
|
|
1391
|
+
if (!input.task || typeof input.task !== "string" || !input.task.trim()) {
|
|
1392
|
+
throw new Error("agents.run: \u7F3A\u5C11\u6709\u6548\u7684 task \u6587\u672C\u63CF\u8FF0\u3002");
|
|
1393
|
+
}
|
|
1394
|
+
const effectiveSignal = combineSignals([input.signal, ctx.abortSignal]);
|
|
1395
|
+
if (effectiveSignal?.aborted) {
|
|
1396
|
+
const abortErr = new Error("Agent run was aborted before starting");
|
|
1397
|
+
abortErr.name = "AbortError";
|
|
1398
|
+
throw abortErr;
|
|
1399
|
+
}
|
|
1400
|
+
const activityId = generateActivityId("act-run");
|
|
1401
|
+
const parentActivityId = input.parentActivityId ?? ctx.activityContext?.parentActivityId ?? ctx.parentActivityId;
|
|
1402
|
+
const startedAt = Date.now();
|
|
1403
|
+
const activity = {
|
|
1404
|
+
activityId,
|
|
1405
|
+
...parentActivityId ? { parentActivityId } : {},
|
|
1406
|
+
kind: "agent-run",
|
|
1407
|
+
agentId: agentKey,
|
|
1408
|
+
...input.agentName ? { agentName: input.agentName } : {},
|
|
1409
|
+
task: input.task,
|
|
1410
|
+
status: "starting",
|
|
1411
|
+
startedAt
|
|
1412
|
+
};
|
|
1413
|
+
const emit = async (type) => {
|
|
1414
|
+
if (ctx.onActivity) {
|
|
1415
|
+
try {
|
|
1416
|
+
await ctx.onActivity({
|
|
1417
|
+
type,
|
|
1418
|
+
activity: { ...activity }
|
|
1419
|
+
});
|
|
1420
|
+
} catch {
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
await emit("activity-started");
|
|
1425
|
+
let startResult;
|
|
1426
|
+
try {
|
|
1427
|
+
startResult = await service.start(
|
|
1428
|
+
{
|
|
1429
|
+
agentId: agentKey,
|
|
1430
|
+
agentKey,
|
|
1431
|
+
task: input.task,
|
|
1432
|
+
input: input.input,
|
|
1433
|
+
agentName: input.agentName,
|
|
1434
|
+
ephemeral: input.ephemeral,
|
|
1435
|
+
batchId: input.batchId,
|
|
1436
|
+
parentActivityId,
|
|
1437
|
+
signal: effectiveSignal
|
|
1438
|
+
},
|
|
1439
|
+
ctx
|
|
1440
|
+
);
|
|
1441
|
+
} catch (err) {
|
|
1442
|
+
const errorMsg = err?.message || String(err);
|
|
1443
|
+
const finishedAt2 = Date.now();
|
|
1444
|
+
activity.status = "failed";
|
|
1445
|
+
activity.finishedAt = finishedAt2;
|
|
1446
|
+
activity.durationMs = finishedAt2 - startedAt;
|
|
1447
|
+
activity.error = errorMsg;
|
|
1448
|
+
await emit("activity-finished");
|
|
1449
|
+
throw err;
|
|
1450
|
+
}
|
|
1451
|
+
const runId = startResult.runId;
|
|
1452
|
+
activity.runId = runId;
|
|
1453
|
+
if (startResult.status === "done" || startResult.status === "completed") {
|
|
1454
|
+
const finishedAt2 = Date.now();
|
|
1455
|
+
activity.status = "completed";
|
|
1456
|
+
activity.finishedAt = finishedAt2;
|
|
1457
|
+
activity.durationMs = finishedAt2 - startedAt;
|
|
1458
|
+
activity.result = startResult.content;
|
|
1459
|
+
await emit("activity-finished");
|
|
339
1460
|
return {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
1461
|
+
runId,
|
|
1462
|
+
status: "completed",
|
|
1463
|
+
content: startResult.content ?? "",
|
|
1464
|
+
activityId,
|
|
1465
|
+
durationMs: activity.durationMs
|
|
344
1466
|
};
|
|
345
1467
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
1468
|
+
if (startResult.status === "failed") {
|
|
1469
|
+
const finishedAt2 = Date.now();
|
|
1470
|
+
activity.status = "failed";
|
|
1471
|
+
activity.finishedAt = finishedAt2;
|
|
1472
|
+
activity.durationMs = finishedAt2 - startedAt;
|
|
1473
|
+
activity.error = "Agent run start failed";
|
|
1474
|
+
await emit("activity-finished");
|
|
1475
|
+
throw new Error(`Agent run ${runId} failed on start.`);
|
|
1476
|
+
}
|
|
1477
|
+
activity.status = "running";
|
|
1478
|
+
await emit("activity-updated");
|
|
1479
|
+
let abortCleanup;
|
|
1480
|
+
if (effectiveSignal && service.cancel) {
|
|
1481
|
+
const onAbort = () => {
|
|
1482
|
+
service.cancel?.({ runId, signal: effectiveSignal }, ctx).catch(() => {
|
|
1483
|
+
});
|
|
1484
|
+
};
|
|
1485
|
+
if (effectiveSignal.aborted) {
|
|
1486
|
+
onAbort();
|
|
1487
|
+
} else {
|
|
1488
|
+
effectiveSignal.addEventListener("abort", onAbort, { once: true });
|
|
1489
|
+
abortCleanup = () => effectiveSignal.removeEventListener("abort", onAbort);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
let waitResult;
|
|
1493
|
+
try {
|
|
1494
|
+
waitResult = await service.wait(
|
|
1495
|
+
{
|
|
1496
|
+
runId,
|
|
1497
|
+
timeoutMs: input.timeoutMs ?? ctx.agentRunTimeoutMs,
|
|
1498
|
+
signal: effectiveSignal
|
|
1499
|
+
},
|
|
1500
|
+
ctx
|
|
1501
|
+
);
|
|
1502
|
+
} catch (err) {
|
|
1503
|
+
abortCleanup?.();
|
|
1504
|
+
const errorMsg = err?.message || String(err);
|
|
1505
|
+
const finishedAt2 = Date.now();
|
|
1506
|
+
activity.status = effectiveSignal?.aborted ? "cancelled" : "failed";
|
|
1507
|
+
activity.finishedAt = finishedAt2;
|
|
1508
|
+
activity.durationMs = finishedAt2 - startedAt;
|
|
1509
|
+
activity.error = errorMsg;
|
|
1510
|
+
await emit("activity-finished");
|
|
1511
|
+
throw err;
|
|
1512
|
+
} finally {
|
|
1513
|
+
abortCleanup?.();
|
|
1514
|
+
}
|
|
1515
|
+
const finishedAt = Date.now();
|
|
1516
|
+
activity.finishedAt = finishedAt;
|
|
1517
|
+
activity.durationMs = finishedAt - startedAt;
|
|
1518
|
+
if (waitResult.status === "done" || waitResult.status === "completed") {
|
|
1519
|
+
activity.status = "completed";
|
|
1520
|
+
activity.result = waitResult.content;
|
|
1521
|
+
await emit("activity-finished");
|
|
1522
|
+
return {
|
|
1523
|
+
runId,
|
|
1524
|
+
status: "completed",
|
|
1525
|
+
content: waitResult.content ?? "",
|
|
1526
|
+
activityId,
|
|
1527
|
+
durationMs: activity.durationMs,
|
|
1528
|
+
raw: waitResult.raw
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
if (waitResult.status === "cancelled" || waitResult.status === "killed" || effectiveSignal?.aborted) {
|
|
1532
|
+
activity.status = "cancelled";
|
|
1533
|
+
await emit("activity-finished");
|
|
1534
|
+
return {
|
|
1535
|
+
runId,
|
|
1536
|
+
status: "cancelled",
|
|
1537
|
+
content: waitResult.content ?? "",
|
|
1538
|
+
activityId,
|
|
1539
|
+
durationMs: activity.durationMs,
|
|
1540
|
+
raw: waitResult.raw
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
if (waitResult.status === "timeout") {
|
|
1544
|
+
activity.status = "failed";
|
|
1545
|
+
activity.error = "Agent run timed out";
|
|
1546
|
+
if (service.cancel) {
|
|
1547
|
+
service.cancel({ runId }, ctx).catch(() => {
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
await emit("activity-finished");
|
|
1551
|
+
return {
|
|
1552
|
+
runId,
|
|
1553
|
+
status: "timeout",
|
|
1554
|
+
content: waitResult.content ?? "",
|
|
1555
|
+
activityId,
|
|
1556
|
+
durationMs: activity.durationMs,
|
|
1557
|
+
error: "Agent run timed out",
|
|
1558
|
+
raw: waitResult.raw
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
activity.status = "failed";
|
|
1562
|
+
activity.error = waitResult.errorMessage ?? `Agent run finished with status "${waitResult.status}"`;
|
|
1563
|
+
await emit("activity-finished");
|
|
1564
|
+
throw new Error(activity.error);
|
|
1565
|
+
}
|
|
1566
|
+
function createToolBridgeAgentRunService(options) {
|
|
1567
|
+
return {
|
|
1568
|
+
start: async (opts) => {
|
|
1569
|
+
const res = await options.startRunner(opts);
|
|
1570
|
+
const raw = res.rawData;
|
|
1571
|
+
if (typeof raw === "string") {
|
|
1572
|
+
return {
|
|
1573
|
+
runId: generateActivityId("sync-run"),
|
|
1574
|
+
status: "done",
|
|
1575
|
+
content: raw
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
return {
|
|
1579
|
+
runId: raw?.runId ?? generateActivityId("run"),
|
|
1580
|
+
status: raw?.status ?? "pending",
|
|
1581
|
+
agentKey: raw?.agentKey,
|
|
1582
|
+
agentName: raw?.agentName,
|
|
1583
|
+
batchId: raw?.batchId,
|
|
1584
|
+
taskPreview: raw?.taskPreview,
|
|
1585
|
+
content: raw?.content
|
|
1586
|
+
};
|
|
1587
|
+
},
|
|
1588
|
+
wait: async (opts) => {
|
|
1589
|
+
const res = await options.waitRunner(opts);
|
|
1590
|
+
const raw = res.rawData;
|
|
1591
|
+
return {
|
|
1592
|
+
runId: raw?.runId ?? opts.runId,
|
|
1593
|
+
status: raw?.status ?? "done",
|
|
1594
|
+
content: typeof raw === "string" ? raw : raw?.content ?? res.displayData,
|
|
1595
|
+
errorMessage: raw?.errorMessage ?? raw?.error,
|
|
1596
|
+
raw
|
|
1597
|
+
};
|
|
1598
|
+
},
|
|
1599
|
+
cancel: options.cancelRunner ? async (opts) => {
|
|
1600
|
+
const res = await options.cancelRunner({ runId: opts.runId });
|
|
1601
|
+
return {
|
|
1602
|
+
runId: res.rawData?.runId ?? opts.runId,
|
|
1603
|
+
status: res.rawData?.status ?? "cancelled",
|
|
1604
|
+
wasActive: res.rawData?.wasActive
|
|
1605
|
+
};
|
|
1606
|
+
} : void 0,
|
|
1607
|
+
inspect: options.inspectRunner ? async (opts) => {
|
|
1608
|
+
const res = await options.inspectRunner({ runId: opts.runId, tailLines: opts.tailLines });
|
|
1609
|
+
return {
|
|
1610
|
+
runId: res.rawData?.runId ?? opts.runId,
|
|
1611
|
+
found: res.rawData?.found ?? true,
|
|
1612
|
+
status: res.rawData?.status,
|
|
1613
|
+
logLines: res.rawData?.logLines
|
|
1614
|
+
};
|
|
1615
|
+
} : void 0
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
var toolCallSeq = 0;
|
|
1619
|
+
function createHostToolAgentRunService(hostExecuteTool) {
|
|
1620
|
+
const execTool = async (name, args, opts) => {
|
|
1621
|
+
const id = `sdk-${Date.now().toString(36)}-${(++toolCallSeq).toString(36)}`;
|
|
1622
|
+
const res = await hostExecuteTool({ id, name, arguments: JSON.stringify(args) }, opts);
|
|
1623
|
+
let rawData = res.content;
|
|
1624
|
+
if (typeof res.content === "string" && res.content.length) {
|
|
1625
|
+
try {
|
|
1626
|
+
rawData = JSON.parse(res.content);
|
|
1627
|
+
} catch {
|
|
1628
|
+
rawData = res.content;
|
|
355
1629
|
}
|
|
356
|
-
} catch (e) {
|
|
357
|
-
console.warn("[imagePreprocessing] web vision describe error:", e);
|
|
358
1630
|
}
|
|
1631
|
+
if (rawData && typeof rawData === "object" && typeof rawData.error === "string") {
|
|
1632
|
+
throw new Error(rawData.error);
|
|
1633
|
+
}
|
|
1634
|
+
return {
|
|
1635
|
+
rawData,
|
|
1636
|
+
displayData: typeof res.metadata?.displayData === "string" ? res.metadata.displayData : String(res.content ?? "")
|
|
1637
|
+
};
|
|
1638
|
+
};
|
|
1639
|
+
return createToolBridgeAgentRunService({
|
|
1640
|
+
startRunner: async (opts) => execTool("startAgentRun", {
|
|
1641
|
+
agentKey: opts.agentKey ?? opts.agentId,
|
|
1642
|
+
task: opts.task,
|
|
1643
|
+
input: opts.input,
|
|
1644
|
+
agentName: opts.agentName,
|
|
1645
|
+
ephemeral: opts.ephemeral,
|
|
1646
|
+
batchId: opts.batchId,
|
|
1647
|
+
parentDialogId: opts.parentDialogId,
|
|
1648
|
+
wait: false
|
|
1649
|
+
}),
|
|
1650
|
+
waitRunner: async (opts) => execTool(
|
|
1651
|
+
"controlAgentRun",
|
|
1652
|
+
{
|
|
1653
|
+
action: "wait",
|
|
1654
|
+
runId: opts.runId,
|
|
1655
|
+
timeoutMs: opts.timeoutMs
|
|
1656
|
+
},
|
|
1657
|
+
// Forward the per-run abort signal so the host wait loop can be
|
|
1658
|
+
// cancelled (CLI controlAgentRun wait reads opts.abortSignal).
|
|
1659
|
+
{ abortSignal: opts.signal }
|
|
1660
|
+
),
|
|
1661
|
+
cancelRunner: async ({ runId }) => execTool("controlAgentRun", { action: "stop", runId }),
|
|
1662
|
+
inspectRunner: async ({ runId, tailLines }) => execTool("controlAgentRun", {
|
|
1663
|
+
action: "status",
|
|
1664
|
+
runId,
|
|
1665
|
+
tailLines: tailLines ?? 0
|
|
1666
|
+
})
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
// packages/agent-runtime/capabilities/capabilitySdk.ts
|
|
1671
|
+
var BUILTIN_CAPABILITIES = [
|
|
1672
|
+
execShellCapability
|
|
1673
|
+
];
|
|
1674
|
+
async function invokeCapability(capabilityOrName, input, ctx = {}) {
|
|
1675
|
+
const capability = typeof capabilityOrName === "string" ? BUILTIN_CAPABILITIES.find((c) => c.name === capabilityOrName) : capabilityOrName;
|
|
1676
|
+
if (!capability) {
|
|
1677
|
+
throw new Error(`Capability "${String(capabilityOrName)}" not found.`);
|
|
359
1678
|
}
|
|
360
|
-
|
|
361
|
-
|
|
1679
|
+
const normalizedInput = capability.normalizeInput(input);
|
|
1680
|
+
await evaluateCapabilityPolicy(capability, normalizedInput, ctx);
|
|
1681
|
+
if (ctx.onInvoke) {
|
|
1682
|
+
await ctx.onInvoke(capability.name, normalizedInput);
|
|
362
1683
|
}
|
|
363
|
-
|
|
364
|
-
|
|
1684
|
+
return capability.invoke(ctx, normalizedInput);
|
|
1685
|
+
}
|
|
1686
|
+
function createCapabilitySdk(args) {
|
|
1687
|
+
const registry2 = /* @__PURE__ */ new Map();
|
|
1688
|
+
for (const cap of args.capabilities ?? BUILTIN_CAPABILITIES) {
|
|
1689
|
+
registry2.set(cap.name, cap);
|
|
1690
|
+
}
|
|
1691
|
+
const invoke = async (name, input) => {
|
|
1692
|
+
const capability = registry2.get(name);
|
|
1693
|
+
if (!capability) {
|
|
1694
|
+
throw new Error(`Capability "${name}" not found.`);
|
|
1695
|
+
}
|
|
1696
|
+
return invokeCapability(capability, input, args.context);
|
|
1697
|
+
};
|
|
365
1698
|
return {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
1699
|
+
execShell: (input) => invoke("execShell", input),
|
|
1700
|
+
agents: {
|
|
1701
|
+
run: async (input) => {
|
|
1702
|
+
if (!args.context.agentRunService) {
|
|
1703
|
+
throw new Error("CapabilityExecutionContext missing agentRunService for agents.run()");
|
|
1704
|
+
}
|
|
1705
|
+
return executeAgentRunLifecycle(args.context.agentRunService, input, args.context);
|
|
1706
|
+
}
|
|
1707
|
+
},
|
|
1708
|
+
invoke
|
|
370
1709
|
};
|
|
371
1710
|
}
|
|
372
1711
|
|
|
@@ -1213,19 +2552,9 @@ function prepareHistoryForNextTurn(history, contextReferenceResolver) {
|
|
|
1213
2552
|
};
|
|
1214
2553
|
});
|
|
1215
2554
|
}
|
|
1216
|
-
var IMAGE_OMITTED_PLACEHOLDER2 = "[Image content omitted: model does not support image input]";
|
|
1217
|
-
function stripImagePartsFromContent2(content) {
|
|
1218
|
-
if (!Array.isArray(content)) return content;
|
|
1219
|
-
const filtered = content.filter((part) => part?.type !== "image_url");
|
|
1220
|
-
if (filtered.length === 0) return IMAGE_OMITTED_PLACEHOLDER2;
|
|
1221
|
-
return filtered;
|
|
1222
|
-
}
|
|
1223
2555
|
function filterImagePartsFromMessages(messages, supportsImages) {
|
|
1224
2556
|
if (supportsImages) return messages;
|
|
1225
|
-
return messages
|
|
1226
|
-
...msg,
|
|
1227
|
-
content: stripImagePartsFromContent2(msg.content)
|
|
1228
|
-
}));
|
|
2557
|
+
return stripImagePartsFromMessages(messages);
|
|
1229
2558
|
}
|
|
1230
2559
|
function buildMessages(args) {
|
|
1231
2560
|
if (args.contextBlockScopes?.length) {
|
|
@@ -1357,6 +2686,18 @@ function applyPersistedTurnInput(messages, persistedInput, persistedInputReferen
|
|
|
1357
2686
|
};
|
|
1358
2687
|
});
|
|
1359
2688
|
}
|
|
2689
|
+
function createLocalCapabilitySdk(adapter, execShellContext) {
|
|
2690
|
+
return createCapabilitySdk({
|
|
2691
|
+
context: {
|
|
2692
|
+
agentRunService: createHostToolAgentRunService(adapter.executeTool),
|
|
2693
|
+
// Inject the SAME local execution context the host uses for execShell,
|
|
2694
|
+
// so PTC tools.execShell() is no more permissive than normal local tool
|
|
2695
|
+
// calling. Caller supplies the real values (workspaceRoot, restrictToWorkspace,
|
|
2696
|
+
// timeout/output limits, destructive-shell guard semantics, abortSignal).
|
|
2697
|
+
...execShellContext
|
|
2698
|
+
}
|
|
2699
|
+
});
|
|
2700
|
+
}
|
|
1360
2701
|
async function runLocalAgentTurn(input) {
|
|
1361
2702
|
const agentConfig = await input.adapter.loadAgentConfig(input.agentRef);
|
|
1362
2703
|
if (!agentConfig) {
|
|
@@ -1367,6 +2708,22 @@ async function runLocalAgentTurn(input) {
|
|
|
1367
2708
|
error.agentRef = input.agentRef;
|
|
1368
2709
|
throw error;
|
|
1369
2710
|
}
|
|
2711
|
+
const execShellContext = buildLocalExecShellContext({
|
|
2712
|
+
workspaceRoot: typeof input.runtimeContext?.workspaceRoot === "string" ? input.runtimeContext.workspaceRoot : typeof input.runtimeContext?.cwd === "string" ? input.runtimeContext.cwd : process.cwd(),
|
|
2713
|
+
...typeof input.runtimeContext?.restrictToWorkspace === "boolean" ? { restrictToWorkspace: input.runtimeContext.restrictToWorkspace } : typeof input.runtimeContext?.restrictShellToWorkspace === "boolean" ? { restrictToWorkspace: input.runtimeContext.restrictShellToWorkspace } : {},
|
|
2714
|
+
...typeof input.runtimeContext?.commandTimeoutMs === "number" ? { commandTimeoutMs: input.runtimeContext.commandTimeoutMs } : {},
|
|
2715
|
+
...typeof input.runtimeContext?.commandOutputLimit === "number" ? { commandOutputLimit: input.runtimeContext.commandOutputLimit } : {},
|
|
2716
|
+
...Array.isArray(input.runtimeContext?.commandPrefix) ? { commandPrefix: input.runtimeContext.commandPrefix } : {},
|
|
2717
|
+
...input.abortSignal ? { abortSignal: input.abortSignal } : {},
|
|
2718
|
+
...typeof input.runtimeContext?.detachMs === "number" ? { detachMs: input.runtimeContext.detachMs } : {},
|
|
2719
|
+
...typeof input.runtimeContext?.enableDestructiveShellGuard === "boolean" ? { enableDestructiveShellGuard: input.runtimeContext.enableDestructiveShellGuard } : {},
|
|
2720
|
+
...typeof input.runtimeContext?.confirmDestructiveAction === "function" ? { confirmDestructiveAction: input.runtimeContext.confirmDestructiveAction } : {},
|
|
2721
|
+
...typeof input.runtimeContext?.blockDestructiveWithoutConfirmation === "boolean" ? { blockDestructiveWithoutConfirmation: input.runtimeContext.blockDestructiveWithoutConfirmation } : {}
|
|
2722
|
+
});
|
|
2723
|
+
const tools = createLocalCapabilitySdk(input.adapter, execShellContext);
|
|
2724
|
+
if (input.__testProgram) {
|
|
2725
|
+
await input.__testProgram(tools);
|
|
2726
|
+
}
|
|
1370
2727
|
const rawBillingConfig = agentConfig.rawRecord ?? {};
|
|
1371
2728
|
const billingConfig = {
|
|
1372
2729
|
model: agentConfig.model || "unknown",
|
|
@@ -1468,26 +2825,12 @@ async function runLocalAgentTurn(input) {
|
|
|
1468
2825
|
hasVision: agentConfig.hasVision
|
|
1469
2826
|
});
|
|
1470
2827
|
if (!supportsImages && hasImageInRuntimeMessages(messages)) {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
messages = preprocessed;
|
|
1478
|
-
emitLoopEvent(input, {
|
|
1479
|
-
kind: "image-preprocessed",
|
|
1480
|
-
atMs: Date.now(),
|
|
1481
|
-
imageCount: imageUrls.length
|
|
1482
|
-
});
|
|
1483
|
-
} else {
|
|
1484
|
-
messages = filterImagePartsFromMessages(messages, false);
|
|
1485
|
-
emitLoopEvent(input, {
|
|
1486
|
-
kind: "image-downgraded",
|
|
1487
|
-
reason: "no-vision",
|
|
1488
|
-
atMs: Date.now()
|
|
1489
|
-
});
|
|
1490
|
-
}
|
|
2828
|
+
messages = filterImagePartsFromMessages(messages, false);
|
|
2829
|
+
emitLoopEvent(input, {
|
|
2830
|
+
kind: "image-downgraded",
|
|
2831
|
+
reason: "no-vision",
|
|
2832
|
+
atMs: Date.now()
|
|
2833
|
+
});
|
|
1491
2834
|
}
|
|
1492
2835
|
const userInputText = extractUserInputText(input.input);
|
|
1493
2836
|
let toolCallCount = 0;
|
|
@@ -1528,6 +2871,10 @@ async function runLocalAgentTurn(input) {
|
|
|
1528
2871
|
messages: requestMessages,
|
|
1529
2872
|
options: {
|
|
1530
2873
|
...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
|
|
2874
|
+
// 用户取消信号穿进 provider options:providerStreamRetry 据此在
|
|
2875
|
+
// 取消后跳过重试,provider 分支(如 antigravity/openai-compatible)
|
|
2876
|
+
// 也可把 fetch 绑定到同一信号,实现真正的传输层取消。
|
|
2877
|
+
...input.abortSignal ? { signal: input.abortSignal } : {},
|
|
1531
2878
|
// 计费归因:续聊轮次带上 dialogId,platform proxy 才能把 token 记录
|
|
1532
2879
|
// 归到具体对话而不是 chat-proxy 兜底桶。新对话首轮 id 尚未分配。
|
|
1533
2880
|
...input.continueDialogId ? { dialogId: input.continueDialogId } : {},
|
|
@@ -1788,9 +3135,27 @@ async function runLocalAgentTurn(input) {
|
|
|
1788
3135
|
export {
|
|
1789
3136
|
readCommandActionGatePayload,
|
|
1790
3137
|
readActionGate,
|
|
1791
|
-
|
|
3138
|
+
isDestructiveShellCommand,
|
|
3139
|
+
evaluateShellCommandPolicy,
|
|
3140
|
+
resolveExecutableOnPath,
|
|
3141
|
+
createGlob,
|
|
3142
|
+
spawnToWebStreams,
|
|
3143
|
+
getProcessRegistry,
|
|
3144
|
+
extractActivity,
|
|
3145
|
+
buildWorkspaceShellCommand,
|
|
3146
|
+
findWorkspaceShellEscapeToken,
|
|
3147
|
+
buildWorkspaceShellEscapeBlockedResult,
|
|
3148
|
+
truncateToolOutput,
|
|
3149
|
+
readNodeStream,
|
|
3150
|
+
waitForNodeProcessExit,
|
|
3151
|
+
deriveLabel,
|
|
3152
|
+
isSpawnFailureError,
|
|
3153
|
+
runWorkspaceCommand,
|
|
3154
|
+
buildExecShellToolDefinition,
|
|
3155
|
+
evaluateExecShellDestructiveGuard,
|
|
3156
|
+
invokeCapability,
|
|
1792
3157
|
summarizeToolArguments,
|
|
1793
|
-
|
|
3158
|
+
stripImagePartsForTextOnlyAgent,
|
|
1794
3159
|
resolveHistoricalToolContentCap,
|
|
1795
3160
|
projectToolMessageContent,
|
|
1796
3161
|
LOCAL_AGENT_CONFIG_MISSING_CODE,
|