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.
Files changed (118) hide show
  1. package/{_executeModel-RWPVI3YV.js → _executeModel-XKWVTZAY.js} +35 -34
  2. package/{action-SMLG6OTB.js → action-Q4KO7DDW.js} +4 -4
  3. package/{addContentAction-SFO244QN.js → addContentAction-FRXDZKSI.js} +3 -3
  4. package/{addDialogAgentAction-W44ZCEHB.js → addDialogAgentAction-XL5G3LPR.js} +2 -2
  5. package/{agentDeleteCommand-KU2RXQPP.js → agentDeleteCommand-ZU4H6F6M.js} +19 -18
  6. package/{agentEmailCommands-MPCMDENS.js → agentEmailCommands-6ULFXW4A.js} +41 -40
  7. package/{agentListCommands-EPD4N6FX.js → agentListCommands-VN7WCW7Z.js} +21 -20
  8. package/{agentMachineCommands-AF7C3YRT.js → agentMachineCommands-5PALVQBD.js} +21 -20
  9. package/{agentPullCommand-YH5GEBK4.js → agentPullCommand-TSWAJPS2.js} +1 -1
  10. package/{agentRecordCommands-EYE6MZZG.js → agentRecordCommands-XEB66KRX.js} +40 -39
  11. package/{agentRunCommand-LL22SGTN.js → agentRunCommand-OJX27NII.js} +42 -41
  12. package/{agentRuntimeLocal-JX34G47Z.js → agentRuntimeLocal-UWDOE55A.js} +20 -19
  13. package/{agentSlice-TNMFQQOV.js → agentSlice-SZRRJ3ZX.js} +5 -5
  14. package/{appDeleteCommands-43K5VXQB.js → appDeleteCommands-R63PZAY7.js} +20 -19
  15. package/{appDeployCommands-FEEJSVDZ.js → appDeployCommands-VNRT54YO.js} +20 -19
  16. package/{appDeployStatusCommands-H76CG45R.js → appDeployStatusCommands-6IPTKLEM.js} +20 -19
  17. package/{appGetCommands-MDZYFTYZ.js → appGetCommands-JPFVMVAI.js} +20 -19
  18. package/{appListCommands-TUJJXPWG.js → appListCommands-B3SLTPII.js} +20 -19
  19. package/{authCommand-T7UK4CWS.js → authCommand-EX5G5PF4.js} +12 -11
  20. package/{authCommands-EAPFGNHN.js → authCommands-VR6Q6GU2.js} +4 -4
  21. package/{chatQueueReduxAdapter-LVQXC4ZY.js → chatQueueReduxAdapter-IEX32ZVP.js} +15 -14
  22. package/{chunk-2H3XIBY4.js → chunk-2CNX3CJE.js} +4 -82
  23. package/{chunk-MPZJVZDC.js → chunk-2UDMTJOM.js} +9 -9
  24. package/{chunk-WHD6BG7M.js → chunk-37LDAHQN.js} +20 -18
  25. package/{chunk-2UECQ7RE.js → chunk-3AOLVADY.js} +1 -1
  26. package/{chunk-HUCT7HID.js → chunk-42IF53CR.js} +1 -1
  27. package/{chunk-AAGCMVFT.js → chunk-46JSKTPF.js} +189 -38
  28. package/{chunk-3WXRAHCG.js → chunk-6MSSX7FI.js} +1 -1
  29. package/{chunk-57QJXIB6.js → chunk-6YH4UR3E.js} +3 -3
  30. package/{chunk-HDKRSRYU.js → chunk-7BNPRRDS.js} +150 -29
  31. package/{chunk-FMEVI5TG.js → chunk-7CH6U3FL.js} +1 -1
  32. package/{chunk-OR2HNLVO.js → chunk-7EPNNOL4.js} +6 -6
  33. package/{chunk-PREQU5QV.js → chunk-7OGIWPNK.js} +1 -1
  34. package/{chunk-DFUPZE6V.js → chunk-7SLMYTSF.js} +1 -1
  35. package/{chunk-3GZXL6FM.js → chunk-AOWETAXX.js} +1 -1
  36. package/{chunk-2IVEXF7P.js → chunk-BHILFABL.js} +1 -1
  37. package/{chunk-TFPUAELY.js → chunk-DDET6246.js} +1 -1
  38. package/{chunk-ZURFOV5J.js → chunk-DMT4NJXR.js} +1 -1
  39. package/{chunk-RAZOLMJR.js → chunk-EQ6HOWEJ.js} +2 -2
  40. package/{chunk-6HYVJV7B.js → chunk-ESHYE7WU.js} +2 -1
  41. package/{chunk-JZ5AMMXU.js → chunk-FH7VUJN5.js} +2 -2
  42. package/{chunk-I6N2UVVY.js → chunk-FV63ICZC.js} +1 -1
  43. package/{chunk-U4TRT5V4.js → chunk-GEPHJJW4.js} +1 -1
  44. package/{chunk-VH2F7ZB6.js → chunk-GTEHJWXU.js} +1 -1
  45. package/{chunk-AOXVOC6D.js → chunk-HCOIKLSH.js} +2 -2
  46. package/{chunk-GTYM4HKV.js → chunk-IEHZHWN7.js} +2 -2
  47. package/{chunk-2QTEYCR7.js → chunk-JFUW3UTO.js} +1 -1
  48. package/{chunk-73GINUCW.js → chunk-JL2PUYVS.js} +102 -23
  49. package/{chunk-37V5LNEQ.js → chunk-KIL7V27H.js} +1 -1
  50. package/{chunk-JTSGWTYZ.js → chunk-KMMQFY5O.js} +1 -1
  51. package/{chunk-UK5JGQVU.js → chunk-KZB5SNYH.js} +9 -14
  52. package/{chunk-XCCGU6QB.js → chunk-L5XKDBSH.js} +1 -1
  53. package/{chunk-DPMBNHOJ.js → chunk-LAZTMWIU.js} +1 -1
  54. package/{chunk-M4NC7NF6.js → chunk-LJGEEYGC.js} +2 -2
  55. package/{chunk-B2BSG6QQ.js → chunk-LLKHGZME.js} +4 -4
  56. package/{chunk-7E72JMBB.js → chunk-N2WDGMD5.js} +45 -4
  57. package/{chunk-Q5ITEGIN.js → chunk-N4XRDM26.js} +1 -1
  58. package/{chunk-AFV4FOT2.js → chunk-OQVRTSZ5.js} +4 -91
  59. package/{chunk-3VAQHAXT.js → chunk-PU47HKTF.js} +11 -11
  60. package/{chunk-CLP2K7U4.js → chunk-QKCMP3LU.js} +2 -2
  61. package/{chunk-JZPBVBIY.js → chunk-QTZD6HBK.js} +18 -8
  62. package/{chunk-LOD75GOS.js → chunk-REOW4YJX.js} +80 -1099
  63. package/{chunk-6SXC35F4.js → chunk-RO3AEN3L.js} +5 -5
  64. package/{chunk-5CS33IBL.js → chunk-RQFO3CAF.js} +8 -9
  65. package/{chunk-GMXWYXAF.js → chunk-SBPGB6TF.js} +3 -3
  66. package/{chunk-FSSX3XRY.js → chunk-U2FRXKC7.js} +1 -1
  67. package/{chunk-NUO3QFWD.js → chunk-UNHKVD2N.js} +1 -1
  68. package/{chunk-GE6IWA5N.js → chunk-UNZDCI3O.js} +126 -1
  69. package/{chunk-JMPYDFHY.js → chunk-V44VE7R4.js} +2 -2
  70. package/{chunk-IL545V3E.js → chunk-V4MT7AEN.js} +1 -1
  71. package/{chunk-FXFGNOT2.js → chunk-VFVCYIRQ.js} +1 -1
  72. package/chunk-VIRTYWWE.js +97 -0
  73. package/{chunk-ZJMME4DS.js → chunk-XS543SNR.js} +5 -5
  74. package/{chunk-O6GEBMIY.js → chunk-XTSVOICC.js} +195 -47
  75. package/{chunk-6PO2SYCS.js → chunk-YH3FPBG6.js} +1569 -204
  76. package/{chunk-WLH5BAKX.js → chunk-YYTTDOWA.js} +2 -2
  77. package/{chunk-5SBK2MTI.js → chunk-ZKUATCEY.js} +20 -69
  78. package/{codingSkills-DY5MINPG.js → codingSkills-5VOV3JVB.js} +2 -2
  79. package/{createAgentAutomationAction-INQT23LX.js → createAgentAutomationAction-72NNXELT.js} +16 -15
  80. package/{createDialogAction-LDCPDI73.js → createDialogAction-3CJ2GXNO.js} +18 -17
  81. package/{createPageAction-5YXOG2M4.js → createPageAction-HZHQGQO6.js} +16 -15
  82. package/{dbSlice-3SD5L5H2.js → dbSlice-2YD2PZER.js} +2 -2
  83. package/{dialogCommands-CHJO47AX.js → dialogCommands-2BF4ZSWH.js} +18 -17
  84. package/{dialogSlice-DOWKOILN.js → dialogSlice-5R4O4377.js} +15 -14
  85. package/{docCreateCommands-OVZARUFX.js → docCreateCommands-HMZUBFGL.js} +19 -18
  86. package/{docDeleteCommands-E2XJ62LD.js → docDeleteCommands-DFR7GCOR.js} +19 -18
  87. package/{docReadCommands-I7OSREPJ.js → docReadCommands-YM7RDUAZ.js} +19 -18
  88. package/{docStore-UAWSKZ2Y.js → docStore-A4SRW2DJ.js} +17 -16
  89. package/{docUpdateCommands-X4Z26VJM.js → docUpdateCommands-TTQBD7IU.js} +19 -18
  90. package/{getPricing-L2ENXWHV.js → getPricing-J4VXWR5B.js} +3 -3
  91. package/{handleSendMessageAction-C3TSDWKH.js → handleSendMessageAction-CIFBZCI2.js} +15 -14
  92. package/index.js +70 -70
  93. package/{localRuntimeAuthority-E3R24J4T.js → localRuntimeAuthority-S77B5QVB.js} +6 -2
  94. package/{localRuntimeDb-DDZCJFQZ.js → localRuntimeDb-N3H3HQ7G.js} +1 -1
  95. package/{machineCommands-BUOIYJAD.js → machineCommands-QSW4JKR2.js} +40 -39
  96. package/{machineInfo-M6KB6HZN.js → machineInfo-66ZFUMLE.js} +1 -1
  97. package/{machineWsRunDispatch-7CRWAA6O.js → machineWsRunDispatch-X3ZC7DZJ.js} +39 -38
  98. package/{models-OX2S5C6Q.js → models-SQFOI4FI.js} +2 -2
  99. package/package.json +1 -1
  100. package/{read-6G73BMEY.js → read-526GJR3R.js} +1 -1
  101. package/{readDocTool-DK4EI2GA.js → readDocTool-JNEZUOSM.js} +1 -1
  102. package/{readlineWorkspace-ZDWEASJV.js → readlineWorkspace-J2ARMEUC.js} +2701 -2083
  103. package/{removeDialogAgentAction-E7H557UO.js → removeDialogAgentAction-NPMEFEJW.js} +4 -4
  104. package/{runAgentClientLoop-U7HYA3O5.js → runAgentClientLoop-5I2R74NR.js} +31 -30
  105. package/{runtimeDoctorCommands-KIKM6BIO.js → runtimeDoctorCommands-KHGQKT3B.js} +3 -3
  106. package/{setDialogExtraReferencesAction-RMMGBR5F.js → setDialogExtraReferencesAction-ZZ6QWAG2.js} +3 -3
  107. package/{setPrimaryDialogAgentAction-FWBKKLVA.js → setPrimaryDialogAgentAction-BG72QA6S.js} +4 -4
  108. package/{settingSlice-73PV4KG4.js → settingSlice-JCCLCXZB.js} +3 -3
  109. package/{skillDiscovery-KLLKXPUO.js → skillDiscovery-AYBKAG6X.js} +3 -3
  110. package/{spaceCommands-GEWZXGHM.js → spaceCommands-EZYMMJJH.js} +18 -17
  111. package/{spaceSlice-B6MOVY4N.js → spaceSlice-TL2FR4AF.js} +16 -15
  112. package/{streamAgentChatTurn-T527EQTT.js → streamAgentChatTurn-UDYSE2ES.js} +45 -54
  113. package/{switchDialogAgentAction-US6QNL3N.js → switchDialogAgentAction-YPGPM2LL.js} +2 -2
  114. package/{tableCommands-QE47K72R.js → tableCommands-64FZBAI3.js} +1 -1
  115. package/{tools-XMWCVMPM.js → tools-KWYWDJ77.js} +25 -24
  116. package/{tuiSummaryLlmCaller-NCAJSVSD.js → tuiSummaryLlmCaller-WUVAOIYU.js} +9 -8
  117. package/{updateCommands-AK2RDOFR.js → updateCommands-MNDUIKN7.js} +2 -2
  118. 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-5SBK2MTI.js";
6
+ } from "./chunk-ZKUATCEY.js";
7
7
  import {
8
- getModelContextWindow,
9
- resolveBuiltinPlatformAgentConfig
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-AOXVOC6D.js";
21
+ } from "./chunk-HCOIKLSH.js";
23
22
  import {
24
23
  findModelConfig,
25
24
  getProviderByModelName
26
- } from "./chunk-DFUPZE6V.js";
25
+ } from "./chunk-7SLMYTSF.js";
27
26
  import {
28
27
  buildDialogSummaryLayer
29
- } from "./chunk-2UECQ7RE.js";
28
+ } from "./chunk-3AOLVADY.js";
30
29
  import {
31
30
  buildUserResponseLanguageContext
32
- } from "./chunk-FMEVI5TG.js";
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
- PUBLIC_QWEN_37_FLASH_AGENT_KEY
51
- } from "./chunk-UK5JGQVU.js";
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 IMAGE_DESCRIPTION_CACHE = /* @__PURE__ */ new Map();
215
- function cacheKeyForUrls(urls) {
216
- return urls.slice().sort().join("||");
217
- }
218
- async function preprocessImagesForTextOnlyAgent(messages, describeImage) {
219
- const imageUrls = extractImageUrlsFromMessages(messages);
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
- async function describeImageWithLocalProvider(adapter, imageUrls, visionAgentConfig) {
244
- if (imageUrls.length === 0) return null;
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
- const config = visionAgentConfig ?? resolveBuiltinPlatformAgentConfig(DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY);
247
- if (!config) {
248
- console.warn("[imagePreprocessing] vision agent config not found, skipping");
249
- return null;
250
- }
251
- const visionProvider = await adapter.resolveProvider(config);
252
- const result = await visionProvider.complete(
253
- [{ role: "user", content: buildDescribeContentParts(imageUrls) }],
254
- { timeoutMs: 3e4 }
255
- );
256
- const text = result.content?.trim();
257
- if (!text) return null;
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
- async function describeImageViaServerProxy(serverUrl, authToken, imageUrls, visionAgentKey) {
265
- if (imageUrls.length === 0) return null;
266
- const agentKey = visionAgentKey ?? DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY;
267
- const { model: visionModel, provider: visionProvider } = resolveDefaultVisionModelConfig(agentKey);
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
- const payload = {
270
- model: visionModel,
271
- messages: [{ role: "user", content: buildDescribeContentParts(imageUrls) }],
272
- stream: false,
273
- provider: visionProvider,
274
- agentKey,
275
- // server proxy 需要 url 字段,但 nolo 平台 agent 的上游由 server 端路由,
276
- // 这里放一个占位 url,server 端会根据 provider/model 覆盖。
277
- url: "https://api.ollama.com/v1/chat/completions"
278
- };
279
- const response = await fetch(`${serverUrl}/api/chat`, {
280
- method: "POST",
281
- headers: {
282
- "Content-Type": "application/json",
283
- Authorization: `Bearer ${authToken}`
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
- body: JSON.stringify(payload),
286
- signal: AbortSignal.timeout(3e4)
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
- if (!response.ok) {
289
- console.warn(`[imagePreprocessing] server proxy vision call failed: ${response.status}`);
290
- return null;
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
- const data = await response.json();
293
- const text = data?.choices?.[0]?.message?.content;
294
- if (typeof text === "string" && text.trim()) {
295
- return text.trim();
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
- return null;
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
- console.warn("[imagePreprocessing] server proxy vision describe failed:", error);
300
- return null;
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 buildDescribeContentParts(imageUrls) {
304
- const parts = [{ type: "text", text: DESCRIBE_IMAGE_PROMPT }];
305
- for (const url of imageUrls) {
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
- return parts;
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 resolveDefaultVisionModelConfig(agentKey) {
311
- const key = agentKey ?? DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY;
312
- const config = resolveBuiltinPlatformAgentConfig(key);
1301
+ function buildDestructiveShellPermissionRequest(command) {
313
1302
  return {
314
- model: config?.model ?? PLATFORM_HOSTED_QWEN_37_FLASH_MODEL,
315
- provider: config?.provider ?? "nolo",
316
- config
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
- var IMAGE_OMITTED_PLACEHOLDER = "[Image content omitted: model does not support image input]";
320
- function stripImagePartsFromContent(content) {
321
- if (!Array.isArray(content)) return content;
322
- const filtered = content.filter((part) => part?.type !== "image_url");
323
- if (filtered.length === 0) return IMAGE_OMITTED_PLACEHOLDER;
324
- return filtered;
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 stripImagePartsFromMessages(messages) {
327
- return messages.map((msg) => ({
328
- ...msg,
329
- content: stripImagePartsFromContent(msg.content)
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 tryPreprocessWebImageOrReject(messages, agentConfig, initialHistoryIds, serverUrl, authToken) {
333
- const supportsVision = resolveAgentImageInputSupport(agentConfig);
334
- const hasImages = hasImageInRuntimeMessages(messages);
335
- const needsPreprocessing = !supportsVision && hasImages;
336
- if (!needsPreprocessing) {
337
- const firstDynamicIdx = messages.findIndex((m) => m.id && !initialHistoryIds.has(m.id));
338
- const splitIdx2 = firstDynamicIdx === -1 ? messages.length : firstDynamicIdx;
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
- kind: "ok",
341
- messages,
342
- stableMessages: messages.slice(0, splitIdx2),
343
- dynamicMessages: messages.slice(splitIdx2)
1461
+ runId,
1462
+ status: "completed",
1463
+ content: startResult.content ?? "",
1464
+ activityId,
1465
+ durationMs: activity.durationMs
344
1466
  };
345
1467
  }
346
- let finalMessages = messages;
347
- if (serverUrl && authToken) {
348
- try {
349
- const preprocessed = await preprocessImagesForTextOnlyAgent(
350
- messages,
351
- (urls) => describeImageViaServerProxy(serverUrl, authToken, urls)
352
- );
353
- if (preprocessed !== messages) {
354
- finalMessages = preprocessed;
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
- if (finalMessages === messages) {
361
- finalMessages = stripImagePartsFromMessages(messages);
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
- const newFirstDynamicIdx = finalMessages.findIndex((m) => m.id && !initialHistoryIds.has(m.id));
364
- const splitIdx = newFirstDynamicIdx === -1 ? finalMessages.length : newFirstDynamicIdx;
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
- kind: "ok",
367
- messages: finalMessages,
368
- stableMessages: finalMessages.slice(0, splitIdx),
369
- dynamicMessages: finalMessages.slice(splitIdx)
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.map((msg) => ({
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
- const imageUrls = extractImageUrlsFromMessages(messages);
1472
- const preprocessed = await preprocessImagesForTextOnlyAgent(
1473
- messages,
1474
- (urls) => describeImageWithLocalProvider(input.adapter, urls)
1475
- );
1476
- if (preprocessed !== messages) {
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
- parseToolArgumentsJson,
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
- tryPreprocessWebImageOrReject,
3158
+ stripImagePartsForTextOnlyAgent,
1794
3159
  resolveHistoricalToolContentCap,
1795
3160
  projectToolMessageContent,
1796
3161
  LOCAL_AGENT_CONFIG_MISSING_CODE,