nolo-cli 0.29.2 → 0.30.0-alpha.2

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 (135) hide show
  1. package/{_executeModel-BFVTGZXE.js → _executeModel-VBHHG7LT.js} +62 -68
  2. package/{action-LG32A6DJ.js → action-FENWPT3C.js} +14 -15
  3. package/{addContentAction-MPGMBJTQ.js → addContentAction-Z5S5ZQ7I.js} +12 -12
  4. package/{addDialogAgentAction-ZH23WDID.js → addDialogAgentAction-VUNVD2XR.js} +11 -11
  5. package/{agentDeleteCommand-4S4STG42.js → agentDeleteCommand-LLGZXUYJ.js} +60 -39
  6. package/{agentEmailCommands-3F4IP4DI.js → agentEmailCommands-LC2FPN6G.js} +76 -81
  7. package/{agentGrantCommands-JNDE4RWR.js → agentGrantCommands-E7ZZFRHK.js} +2 -1
  8. package/{agentListCommands-JCRV2XF6.js → agentListCommands-A6OBQ3QX.js} +57 -40
  9. package/{agentMachineCommands-KYXPYSFP.js → agentMachineCommands-4O4Q5B5X.js} +53 -35
  10. package/{agentPullCommand-ZZGU5KOY.js → agentPullCommand-4V36XNGV.js} +4 -3
  11. package/{agentRecordCommands-UADUPIXO.js → agentRecordCommands-W22RIQRV.js} +73 -78
  12. package/{agentRunCommand-MJWSR53W.js → agentRunCommand-V4OBU4FB.js} +77 -82
  13. package/{agentRunControl-SNUKXN6J.js → agentRunControl-AKBN2366.js} +13 -1
  14. package/{agentRuntimeLocal-PC5RRRPT.js → agentRuntimeLocal-I57YSGH4.js} +48 -30
  15. package/{agentSlice-C5OYLNBT.js → agentSlice-3ZHY7RAO.js} +12 -13
  16. package/{appDeleteCommands-ZXODZ5AD.js → appDeleteCommands-DJJF466K.js} +53 -35
  17. package/{appDeployCommands-AWC6JPZH.js → appDeployCommands-WJZGIOEZ.js} +53 -35
  18. package/{appDeployStatusCommands-74NXMDAI.js → appDeployStatusCommands-ICYXUJNO.js} +53 -35
  19. package/{appGetCommands-6YGIAP5L.js → appGetCommands-UOCWYJPV.js} +53 -35
  20. package/{appListCommands-DZWBHMNK.js → appListCommands-EOKWTXPW.js} +53 -35
  21. package/{authCommand-XMQHBK5L.js → authCommand-H5KE7K7K.js} +16 -16
  22. package/{authCommands-JSOG2H4E.js → authCommands-CXJ6PI2O.js} +7 -7
  23. package/{chunk-OEWEE4VW.js → chunk-2XM5KUNE.js} +4 -2
  24. package/{chunk-7H3UTGPM.js → chunk-3AUWC6EM.js} +82 -3
  25. package/{chunk-HEXXIS77.js → chunk-3RIWR23B.js} +1 -1
  26. package/{chunk-R7LOFGRE.js → chunk-446VANPP.js} +18 -14
  27. package/{chunk-RZNYL4LW.js → chunk-4SAV7BD7.js} +65 -1
  28. package/{chunk-QC6ZWDSV.js → chunk-56IUA7RU.js} +3 -3
  29. package/{chunk-R3JSTDQO.js → chunk-66O5MZYH.js} +1 -1
  30. package/{chunk-PSK2RUHV.js → chunk-7CD24PU4.js} +360 -2
  31. package/{chunk-G2BZ43DV.js → chunk-A4JDTA5T.js} +13 -13
  32. package/{chunk-DZ3XJVET.js → chunk-AOS5IN2I.js} +1 -1
  33. package/{chunk-SKDNBYZK.js → chunk-BOL2D2JW.js} +1 -1
  34. package/{chunk-YTA2QOEE.js → chunk-CAENRKNJ.js} +15 -0
  35. package/{chunk-2TYLTEAH.js → chunk-CV6I4HBA.js} +1 -1
  36. package/chunk-DCDL3GC4.js +171 -0
  37. package/{chunk-FVOSIYXM.js → chunk-DCWBP2O7.js} +42 -393
  38. package/{chunk-OLWMGSW2.js → chunk-DI6MXJK5.js} +37 -37
  39. package/{chunk-HRPWRVBI.js → chunk-DKRT67FG.js} +11 -11
  40. package/{chunk-L6RBX4L4.js → chunk-DWQNDGO7.js} +20 -8
  41. package/{chunk-VKBTAQWT.js → chunk-FS5TTXPS.js} +6 -6
  42. package/chunk-G5JHHUAK.js +73 -0
  43. package/{chunk-TOGGHWHI.js → chunk-G6VITLCZ.js} +3 -3
  44. package/{chunk-RJBTU7AS.js → chunk-GAGUBI42.js} +302 -14
  45. package/{chunk-NXMJ55RU.js → chunk-GVKTKT3O.js} +3 -3
  46. package/{chunk-5ZXNLOAG.js → chunk-HI7QZNWX.js} +17 -10
  47. package/{chunk-DF7SMWEV.js → chunk-HLG6GAUD.js} +1 -1
  48. package/{chunk-MQRZ5QUH.js → chunk-IXZX5CJK.js} +1 -1
  49. package/{chunk-IC7W63RM.js → chunk-IZ3CBMD5.js} +3 -3
  50. package/{chunk-VHBWHNVT.js → chunk-K6QEHQZ3.js} +8 -4
  51. package/chunk-KW47OSRJ.js +3246 -0
  52. package/{chunk-Q5RXHXKU.js → chunk-KWKD3BCC.js} +189 -122
  53. package/{chunk-L65QKSVJ.js → chunk-L3GCUVOH.js} +1 -1
  54. package/{chunk-4BDPDTJU.js → chunk-L3VQOVKK.js} +8 -8
  55. package/{chunk-HALKDXKP.js → chunk-MEUZFIAJ.js} +8 -4
  56. package/{chunk-H5S4XUGI.js → chunk-MMCVBEAD.js} +174 -149
  57. package/{chunk-XOIF2TNF.js → chunk-OB6FBSH3.js} +13 -0
  58. package/chunk-OYK7PA5U.js +1707 -0
  59. package/{chunk-6HTWA77J.js → chunk-Q24P6TUS.js} +1 -1
  60. package/{chunk-GMJS5AYP.js → chunk-QLV2GKU5.js} +16 -178
  61. package/{chunk-WMB7GVPN.js → chunk-SWOTB2TX.js} +134 -1166
  62. package/{chunk-GC34RRTQ.js → chunk-SWYMCHM7.js} +1 -69
  63. package/{chunk-BBGRJB5E.js → chunk-SZ5CBYMZ.js} +4 -4
  64. package/chunk-T27IEBNQ.js +149 -0
  65. package/{chunk-WX3ZOSTL.js → chunk-T3Y5EMJS.js} +24 -26
  66. package/{chunk-UFL6WHHJ.js → chunk-THTAR67C.js} +1056 -137
  67. package/{chunk-2FX23RGA.js → chunk-TJYWZ3LO.js} +4 -4
  68. package/{chunk-KEF5OBOZ.js → chunk-UKC5HU2C.js} +33 -27
  69. package/{chunk-HRJEPMZY.js → chunk-UTQZ73QQ.js} +2 -2
  70. package/{chunk-GT3GG2H7.js → chunk-VSTUQGLT.js} +1 -1
  71. package/{chunk-32ZM6HVI.js → chunk-W2YKDXT7.js} +95 -95
  72. package/{chunk-L3QFIBZG.js → chunk-WTGCTVJG.js} +1 -1
  73. package/{chunk-XS7UPVR7.js → chunk-XD5NQEOH.js} +5 -5
  74. package/{chunk-WSQLK2KR.js → chunk-XFGM5PJP.js} +6 -6
  75. package/{chunk-JSB4N6NU.js → chunk-YLADSPDU.js} +3 -3
  76. package/{chunk-FRN5QOKN.js → chunk-YWGSCF37.js} +19 -4
  77. package/{codingSkills-EN6Z6G6U.js → codingSkills-V35TRR4Y.js} +1 -2
  78. package/{createAgentAutomationAction-BK4UZJKU.js → createAgentAutomationAction-3GMH6ORX.js} +30 -37
  79. package/{createDialogAction-MJJUI2MK.js → createDialogAction-R5MCL4RE.js} +32 -38
  80. package/{createPageAction-YMZ7K363.js → createPageAction-QYTG656R.js} +43 -50
  81. package/{dbSlice-6QJYQUBV.js → dbSlice-OBV7VFCF.js} +11 -11
  82. package/{deleteFile-WDZ3VM7B.js → deleteFile-AC45HYSM.js} +5 -5
  83. package/{dialogCommands-OKH45ASS.js → dialogCommands-NUQGIXVZ.js} +53 -37
  84. package/{dialogSlice-UDOKNDHB.js → dialogSlice-CFDECXEU.js} +27 -34
  85. package/{docCreateCommands-MCLMR4TE.js → docCreateCommands-UNFCXB5N.js} +52 -36
  86. package/{docDeleteCommands-ONDHI7JV.js → docDeleteCommands-NG5IVKE5.js} +54 -38
  87. package/{docReadCommands-FCXBMISW.js → docReadCommands-6J7MUUX6.js} +52 -36
  88. package/{docStore-N66O4CGH.js → docStore-MZZWJM4F.js} +28 -35
  89. package/{docUpdateCommands-T3XN6XOX.js → docUpdateCommands-6AW3VZOX.js} +55 -39
  90. package/{fetchMessages-W4IKHPCS.js → fetchMessages-VPPQDDZ4.js} +3 -3
  91. package/{getPricing-X6I23RN6.js → getPricing-XQYSSVV6.js} +6 -7
  92. package/{handleSendMessageAction-KC4AYUZP.js → handleSendMessageAction-IU2QNTLM.js} +30 -37
  93. package/index.js +72 -72
  94. package/{localDialogRead-UFLBS6OT.js → localDialogRead-P72W2U2R.js} +1 -1
  95. package/{machineCommands-DIMRFA3Y.js → machineCommands-Z2QEWTHZ.js} +76 -81
  96. package/{machineWsRunDispatch-2DYJZI6Z.js → machineWsRunDispatch-SJTYHZHN.js} +71 -76
  97. package/{models-YT32CTT5.js → models-WDUTTYFS.js} +2 -2
  98. package/{offlineMarxistsAgentCommand-5OOWZLJI.js → offlineMarxistsAgentCommand-E6LUG63A.js} +6 -2
  99. package/package.json +1 -1
  100. package/{read-C7Y6LSBM.js → read-QK7ZRX6G.js} +6 -6
  101. package/{readDocTool-TOHRYJHP.js → readDocTool-EYDMVRBL.js} +2 -1
  102. package/{readlineWorkspace-HUPHMLDG.js → readlineWorkspace-BGKPABK4.js} +208 -183
  103. package/{removeDialogAgentAction-WZXPOH5S.js → removeDialogAgentAction-TBW3UZJW.js} +15 -16
  104. package/{runAgentClientLoop-5TGSUKHV.js → runAgentClientLoop-N3YDLTMS.js} +61 -66
  105. package/{setDialogExtraReferencesAction-INZY2ENI.js → setDialogExtraReferencesAction-Q3ANF2NR.js} +12 -12
  106. package/{setPrimaryDialogAgentAction-WCVLPYCF.js → setPrimaryDialogAgentAction-FQ5R3EFJ.js} +15 -16
  107. package/{settingSlice-IDUM4LWU.js → settingSlice-JKKV2T7O.js} +13 -14
  108. package/{skillDiscovery-ITA7BI7A.js → skillDiscovery-I3QRA7NE.js} +2 -2
  109. package/{spaceCommands-XSSDB3YK.js → spaceCommands-R6AEQNLG.js} +57 -40
  110. package/{spaceSlice-BXDCO6PT.js → spaceSlice-MCH7RLLP.js} +27 -34
  111. package/{streamAgentChatTurn-HRFMVSVC.js → streamAgentChatTurn-Q4U2CVMI.js} +130 -1300
  112. package/{switchDialogAgentAction-YBFFCBOD.js → switchDialogAgentAction-QQGVE4SL.js} +11 -11
  113. package/{toolApiClient-WVTUYOFM.js → toolApiClient-ARGFDJAG.js} +2 -2
  114. package/{tools-5V6FHWQU.js → tools-Y3UBLLYW.js} +50 -56
  115. package/{tuiSummaryLlmCaller-GCUPULOV.js → tuiSummaryLlmCaller-4474IBTI.js} +12 -12
  116. package/{updateDialogTitleAction-W62C4W4K.js → updateDialogTitleAction-3J7GH67I.js} +35 -43
  117. package/chunk-5CS33IBL.js +0 -301
  118. package/chunk-5MMAK75Q.js +0 -15
  119. package/chunk-HYSJHRWD.js +0 -669
  120. package/chunk-MRC4Q34M.js +0 -188
  121. package/chunk-SIZQB7BD.js +0 -91
  122. package/chunk-UEOPA6MV.js +0 -641
  123. package/chunk-VIDPVFP3.js +0 -20
  124. package/chunk-W2LZV5GE.js +0 -231
  125. package/chunk-Y5GCAMGP.js +0 -9
  126. package/chunk-YYLHWIW6.js +0 -48
  127. package/chunk-Z54ZAZZC.js +0 -1791
  128. package/chunk-ZZ2MH7LE.js +0 -84
  129. package/{chunk-OMP5L5NF.js → chunk-44HACRCK.js} +3 -3
  130. package/{chunk-ZVZMT3EA.js → chunk-KWCLUYGQ.js} +3 -3
  131. package/{chunk-NRY4GCKJ.js → chunk-WHLVPK32.js} +3 -3
  132. package/{chunk-RQSOAMPJ.js → chunk-Z5MXSEOJ.js} +2 -2
  133. package/{chunk-XMLRJUU5.js → chunk-ZFTCLBEX.js} +9 -9
  134. package/{runtimeDoctorCommands-HSU632DY.js → runtimeDoctorCommands-E2OUORHK.js} +3 -3
  135. package/{tableCommands-QSZILI7I.js → tableCommands-PRJNWN6Q.js} +6 -6
package/chunk-Z54ZAZZC.js DELETED
@@ -1,1791 +0,0 @@
1
- import {
2
- buildCurrentTimeBlock,
3
- buildIdentityBlock,
4
- buildRuntimeGuidanceBlocks,
5
- resolveToolGuidedSections
6
- } from "./chunk-PSK2RUHV.js";
7
- import {
8
- estimateTokenCount,
9
- getModelContextWindow,
10
- planCompression,
11
- planContextUsage,
12
- resolveBuiltinPlatformAgentConfig,
13
- serializeMessageContent
14
- } from "./chunk-HYSJHRWD.js";
15
- import {
16
- clipCompactText
17
- } from "./chunk-5MMAK75Q.js";
18
- import {
19
- findModelConfig,
20
- getProviderByModelName
21
- } from "./chunk-GT3GG2H7.js";
22
- import {
23
- buildDialogSummaryLayer
24
- } from "./chunk-PJY5AH5A.js";
25
- import {
26
- canonicalizeToolName,
27
- canonicalizeToolNames
28
- } from "./chunk-ZZ2MH7LE.js";
29
- import {
30
- toErrorMessage
31
- } from "./chunk-HNMXYYJR.js";
32
- import {
33
- PLATFORM_HOSTED_KIMI_K26_MODEL
34
- } from "./chunk-YTA2QOEE.js";
35
- import {
36
- asRecordOrEmpty
37
- } from "./chunk-KX6YBIHG.js";
38
- import {
39
- asTrimmedLowercaseString
40
- } from "./chunk-QSJ3HFEF.js";
41
- import {
42
- PUBLIC_KIMI_K26_IMAGE_AGENT_KEY
43
- } from "./chunk-L6RBX4L4.js";
44
- import {
45
- isRecord
46
- } from "./chunk-KUG52IAP.js";
47
- import {
48
- asOptionalTrimmedString
49
- } from "./chunk-G3SUCT4W.js";
50
-
51
- // packages/agent-runtime/actionGate.ts
52
- function readCommandActionGatePayload(payload) {
53
- if (!isRecord(payload)) return null;
54
- if (!Array.isArray(payload.command)) return null;
55
- const command = payload.command.flatMap((item) => {
56
- const value = asOptionalTrimmedString(item);
57
- return value ? [value] : [];
58
- });
59
- if (command.length === 0) return null;
60
- const displayCommand = asOptionalTrimmedString(payload.displayCommand);
61
- return {
62
- command,
63
- ...displayCommand ? { displayCommand } : {}
64
- };
65
- }
66
- function readActionGate(value) {
67
- if (!isRecord(value)) return null;
68
- if (typeof value.id !== "string" || !value.id.trim()) return null;
69
- if (value.kind !== "confirm" && value.kind !== "handoff" && value.kind !== "input") return null;
70
- if (typeof value.title !== "string" || !value.title.trim()) return null;
71
- const titleParams = readTitleParams(value.titleParams);
72
- return {
73
- id: value.id.trim(),
74
- kind: value.kind,
75
- title: value.title.trim(),
76
- ...titleParams ? { titleParams } : {},
77
- ...asOptionalTrimmedString(value.body) ? { body: asOptionalTrimmedString(value.body) } : {},
78
- ...Object.prototype.hasOwnProperty.call(value, "payload") ? { payload: value.payload } : {}
79
- };
80
- }
81
- function readTitleParams(value) {
82
- if (!isRecord(value)) return void 0;
83
- const entries = Object.entries(value).flatMap(([key, raw]) => {
84
- if (typeof raw === "string" || typeof raw === "number") {
85
- return [[key, raw]];
86
- }
87
- return [];
88
- });
89
- if (entries.length === 0) return void 0;
90
- return Object.fromEntries(entries);
91
- }
92
-
93
- // packages/ai/llm/agentCapabilities.ts
94
- var isCustomAgent = (agent) => {
95
- const apiSource = agent.apiSource?.toLowerCase();
96
- const provider = agent.provider?.toLowerCase();
97
- return apiSource === "custom" || provider === "custom";
98
- };
99
- var ANTIGRAVITY_EFFORT_SUFFIXES = [
100
- "-tiered",
101
- "-extra-low",
102
- "-low",
103
- "-medium",
104
- "-high"
105
- ];
106
- function stripEffortSuffix(model) {
107
- for (const suffix of ANTIGRAVITY_EFFORT_SUFFIXES) {
108
- if (model.endsWith(suffix) && model.length > suffix.length) {
109
- return model.slice(0, -suffix.length);
110
- }
111
- }
112
- return model;
113
- }
114
- function stripPreviewSuffix(model) {
115
- const suffix = "-preview";
116
- if (model.endsWith(suffix) && model.length > suffix.length) {
117
- return model.slice(0, -suffix.length);
118
- }
119
- return model;
120
- }
121
- var lookupKnownModelVision = (provider, model) => {
122
- if (!model) return void 0;
123
- const candidates = [model];
124
- const strippedEffort = stripEffortSuffix(model);
125
- if (strippedEffort !== model) candidates.push(strippedEffort);
126
- const strippedPreview = stripPreviewSuffix(model);
127
- if (strippedPreview !== model) candidates.push(strippedPreview);
128
- for (const candidate of candidates) {
129
- if (provider) {
130
- const direct = findModelConfig(provider, candidate);
131
- if (direct) return direct.hasVision;
132
- }
133
- const detected = getProviderByModelName(candidate);
134
- if (detected) {
135
- const found = findModelConfig(detected, candidate)?.hasVision;
136
- if (found !== void 0) return found;
137
- }
138
- if (!provider && candidate.includes("/")) {
139
- const slash = candidate.indexOf("/");
140
- const modelProvider = candidate.slice(0, slash).toLowerCase();
141
- const modelName = candidate.slice(slash + 1);
142
- const nested = findModelConfig(modelProvider, modelName);
143
- if (nested) return nested.hasVision;
144
- }
145
- }
146
- return void 0;
147
- };
148
- var resolveAgentImageInputSupport = (agent) => {
149
- if (!agent) return true;
150
- const provider = asTrimmedLowercaseString(agent.provider) || null;
151
- const model = asOptionalTrimmedString(agent.model) ?? "";
152
- const custom = isCustomAgent(agent);
153
- const catalogHasVision = lookupKnownModelVision(
154
- custom ? null : provider,
155
- model
156
- );
157
- if (!custom && catalogHasVision !== void 0) {
158
- return catalogHasVision;
159
- }
160
- if (custom) {
161
- if (catalogHasVision === true) return true;
162
- return true;
163
- }
164
- if (typeof agent.hasVision === "boolean") {
165
- return agent.hasVision;
166
- }
167
- return true;
168
- };
169
-
170
- // packages/ai/agent/imagePreprocessing.ts
171
- var DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY = PUBLIC_KIMI_K26_IMAGE_AGENT_KEY;
172
- var DESCRIBE_IMAGE_PROMPT = "\u8BF7\u8BE6\u7EC6\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9\uFF0C\u5305\u62EC\u573A\u666F\u3001\u7269\u4F53\u3001\u6587\u5B57\u3001\u989C\u8272\u548C\u5E03\u5C40\u7B49\u5173\u952E\u4FE1\u606F\u3002";
173
- function extractImageUrlsFromMessages(messages) {
174
- const urls = [];
175
- for (const msg of messages) {
176
- if (!Array.isArray(msg.content)) continue;
177
- for (const part of msg.content) {
178
- if (part && typeof part === "object" && part.type === "image_url" && part.image_url && typeof part.image_url.url === "string" && part.image_url.url.trim()) {
179
- urls.push(part.image_url.url);
180
- }
181
- }
182
- }
183
- return urls;
184
- }
185
- function hasImageInRuntimeMessages(messages) {
186
- return extractImageUrlsFromMessages(messages).length > 0;
187
- }
188
- async function preprocessImagesForTextOnlyAgent(messages, describeImage) {
189
- const imageUrls = extractImageUrlsFromMessages(messages);
190
- if (imageUrls.length === 0) return messages;
191
- const description = await describeImage(imageUrls);
192
- if (description === null || !description.trim()) return messages;
193
- const descriptionText = `[\u56FE\u7247\u63CF\u8FF0] ${description.trim()}`;
194
- return messages.map((msg) => {
195
- if (!Array.isArray(msg.content)) return msg;
196
- const hasImage = msg.content.some((p) => p?.type === "image_url");
197
- if (!hasImage) return msg;
198
- const newContent = msg.content.map((part) => {
199
- if (part && typeof part === "object" && part.type === "image_url") {
200
- return { type: "text", text: descriptionText };
201
- }
202
- return part;
203
- }).filter((p) => p !== null && p !== void 0);
204
- return { ...msg, content: newContent };
205
- });
206
- }
207
- async function describeImageWithLocalProvider(adapter, imageUrls, visionAgentConfig) {
208
- if (imageUrls.length === 0) return null;
209
- try {
210
- const config = visionAgentConfig ?? resolveBuiltinPlatformAgentConfig(DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY);
211
- if (!config) {
212
- console.warn("[imagePreprocessing] vision agent config not found, skipping");
213
- return null;
214
- }
215
- const visionProvider = await adapter.resolveProvider(config);
216
- const contentParts = [
217
- { type: "text", text: DESCRIBE_IMAGE_PROMPT }
218
- ];
219
- for (const url of imageUrls) {
220
- contentParts.push({ type: "image_url", image_url: { url } });
221
- }
222
- const result = await visionProvider.complete(
223
- [{ role: "user", content: contentParts }],
224
- { timeoutMs: 3e4 }
225
- );
226
- const text = result.content?.trim();
227
- if (!text) return null;
228
- return text;
229
- } catch (error) {
230
- console.warn("[imagePreprocessing] vision describe failed, will fallback:", error);
231
- return null;
232
- }
233
- }
234
- async function describeImageViaServerProxy(serverUrl, authToken, imageUrls, visionAgentKey) {
235
- if (imageUrls.length === 0) return null;
236
- const agentKey = visionAgentKey ?? DEFAULT_IMAGE_PREPROCESSOR_AGENT_KEY;
237
- const visionConfig = resolveBuiltinPlatformAgentConfig(agentKey);
238
- const visionModel = visionConfig?.model ?? PLATFORM_HOSTED_KIMI_K26_MODEL;
239
- const visionProvider = visionConfig?.provider ?? "nolo";
240
- try {
241
- const contentParts = [
242
- { type: "text", text: DESCRIBE_IMAGE_PROMPT }
243
- ];
244
- for (const url of imageUrls) {
245
- contentParts.push({ type: "image_url", image_url: { url } });
246
- }
247
- const payload = {
248
- model: visionModel,
249
- messages: [{ role: "user", content: contentParts }],
250
- stream: false,
251
- provider: visionProvider,
252
- agentKey,
253
- // server proxy 需要 url 字段,但 nolo 平台 agent 的上游由 server 端路由,
254
- // 这里放一个占位 url,server 端会根据 provider/model 覆盖。
255
- url: "https://api.ollama.com/v1/chat/completions"
256
- };
257
- const response = await fetch(`${serverUrl}/api/chat`, {
258
- method: "POST",
259
- headers: {
260
- "Content-Type": "application/json",
261
- Authorization: `Bearer ${authToken}`
262
- },
263
- body: JSON.stringify(payload),
264
- signal: AbortSignal.timeout(3e4)
265
- });
266
- if (!response.ok) {
267
- console.warn(`[imagePreprocessing] server proxy vision call failed: ${response.status}`);
268
- return null;
269
- }
270
- const data = await response.json();
271
- const text = data?.choices?.[0]?.message?.content;
272
- if (typeof text === "string" && text.trim()) {
273
- return text.trim();
274
- }
275
- return null;
276
- } catch (error) {
277
- console.warn("[imagePreprocessing] server proxy vision describe failed:", error);
278
- return null;
279
- }
280
- }
281
-
282
- // packages/agent-runtime/toolCallPairing.ts
283
- function readToolCallId(message) {
284
- if (typeof message.tool_call_id === "string" && message.tool_call_id) {
285
- return message.tool_call_id;
286
- }
287
- if (typeof message.toolCallId === "string" && message.toolCallId) {
288
- return message.toolCallId;
289
- }
290
- return void 0;
291
- }
292
- function isContentBlank(content) {
293
- if (content == null) return true;
294
- if (typeof content === "string") return content.trim().length === 0;
295
- if (Array.isArray(content)) {
296
- if (content.length === 0) return true;
297
- return !content.some((part) => {
298
- if (part == null || typeof part !== "object") return false;
299
- const p = part;
300
- if (p.type === "text") return typeof p.text === "string" && p.text.trim().length > 0;
301
- if (p.type === "image_url") {
302
- const url = p.image_url?.url;
303
- return typeof url === "string" && url.trim().length > 0;
304
- }
305
- return true;
306
- });
307
- }
308
- return false;
309
- }
310
- function sanitizeToolCallPairing(messages) {
311
- const assistantIndexByCallId = /* @__PURE__ */ new Map();
312
- for (let i = 0; i < messages.length; i += 1) {
313
- const m = messages[i];
314
- if (m == null) continue;
315
- if (m.role !== "assistant") continue;
316
- const toolCalls = m.tool_calls;
317
- if (!Array.isArray(toolCalls)) continue;
318
- for (const call of toolCalls) {
319
- const id = call?.id;
320
- if (typeof id !== "string" || !id) continue;
321
- if (!assistantIndexByCallId.has(id)) {
322
- assistantIndexByCallId.set(id, i);
323
- }
324
- }
325
- }
326
- const toolByCallId = /* @__PURE__ */ new Map();
327
- for (let i = 0; i < messages.length; i += 1) {
328
- const m = messages[i];
329
- if (m == null) continue;
330
- if (m.role !== "tool") continue;
331
- const id = readToolCallId(m);
332
- if (typeof id !== "string" || !id) continue;
333
- if (!toolByCallId.has(id)) {
334
- toolByCallId.set(id, m);
335
- }
336
- }
337
- const output = [];
338
- const consumedToolCallIds = /* @__PURE__ */ new Set();
339
- for (let i = 0; i < messages.length; i += 1) {
340
- const m = messages[i];
341
- if (m == null) continue;
342
- if (m.role === "tool") continue;
343
- if (m.role !== "assistant") {
344
- output.push(m);
345
- continue;
346
- }
347
- const toolCalls = m.tool_calls;
348
- if (!Array.isArray(toolCalls) || toolCalls.length === 0) {
349
- output.push(m);
350
- continue;
351
- }
352
- const matchedToolCalls = [];
353
- const matchedToolMessages = [];
354
- for (const call of toolCalls) {
355
- const id = call?.id;
356
- if (typeof id !== "string" || !id) continue;
357
- const toolMessage = toolByCallId.get(id);
358
- if (!toolMessage) continue;
359
- if (assistantIndexByCallId.get(id) !== i) continue;
360
- if (consumedToolCallIds.has(id)) continue;
361
- matchedToolCalls.push(call);
362
- matchedToolMessages.push(toolMessage);
363
- consumedToolCallIds.add(id);
364
- }
365
- if (matchedToolCalls.length === 0) {
366
- const { tool_calls: _drop, ...rest } = m;
367
- const stripped = rest;
368
- if (isContentBlank(stripped.content)) {
369
- continue;
370
- }
371
- output.push(stripped);
372
- continue;
373
- }
374
- const normalizedAssistant = { ...m, tool_calls: matchedToolCalls };
375
- output.push(normalizedAssistant);
376
- for (const toolMessage of matchedToolMessages) {
377
- output.push(toolMessage);
378
- }
379
- }
380
- return output;
381
- }
382
-
383
- // packages/agent-runtime/parseToolArguments.ts
384
- function parseToolArgumentsJson(raw) {
385
- if (!raw?.trim()) return {};
386
- try {
387
- return asRecordOrEmpty(JSON.parse(raw));
388
- } catch {
389
- return {};
390
- }
391
- }
392
-
393
- // packages/agent-runtime/summarizeToolArguments.ts
394
- function clip(value, max = 240) {
395
- return clipCompactText(value, max);
396
- }
397
- function summarizeToolArguments(toolName, rawArgs) {
398
- const args = parseToolArgumentsJson(rawArgs);
399
- const pick = (...keys2) => {
400
- for (const key of keys2) {
401
- const value = asOptionalTrimmedString(args[key]);
402
- if (value) return value;
403
- }
404
- return "";
405
- };
406
- const command = pick("command", "cmd", "runCommand", "executeCommand", "bash");
407
- if (command) return clip(command);
408
- const query = pick("query", "pattern", "search", "q");
409
- if (query) return clip(query);
410
- const filePath = pick("filePath", "file_path", "path", "filename", "file");
411
- if (filePath) return clip(filePath);
412
- const keys = Object.keys(args);
413
- if (keys.length === 0) return "";
414
- return clip(
415
- keys.slice(0, 6).map((key) => {
416
- const value = args[key];
417
- if (typeof value === "string") return `${key}=${clip(value, 80)}`;
418
- if (typeof value === "number" || typeof value === "boolean") {
419
- return `${key}=${String(value)}`;
420
- }
421
- if (Array.isArray(value)) return `${key}[${value.length}]`;
422
- return `${key}=${value === null ? "null" : typeof value}`;
423
- }).join(" ")
424
- );
425
- }
426
-
427
- // packages/agent-runtime/contextBlockScope.ts
428
- var isContextBlockScope = (value) => {
429
- if (typeof value !== "object" || value === null) return false;
430
- const candidate = value;
431
- return typeof candidate.content === "string" && (candidate.cacheScope === "session" || candidate.cacheScope === "turn");
432
- };
433
- var normalizeContextBlockScopes = (legacyBlocks = void 0, scopes = void 0) => {
434
- const trimmedScopes = (Array.isArray(scopes) ? scopes : []).filter(isContextBlockScope).map((scope) => ({ content: scope.content.trim(), cacheScope: scope.cacheScope })).filter((scope) => scope.content.length > 0);
435
- if (trimmedScopes.length > 0) {
436
- return trimmedScopes;
437
- }
438
- return (Array.isArray(legacyBlocks) ? legacyBlocks : []).filter((block) => typeof block === "string").map((block) => block.trim()).filter((block) => block.length > 0).map((block) => ({ content: block, cacheScope: "turn" }));
439
- };
440
-
441
- // packages/ai/agent/toolOutputPolicy.ts
442
- var MAX_HISTORICAL_TOOL_CONTENT_CHARS = 1600;
443
- var MAX_IN_TURN_TOOL_CONTENT_CHARS = 4e3;
444
- var FRESH_TOOL_OUTPUT_MAX_CHARS = 32e3;
445
- var DEFAULT_TOOL_OUTPUT_PROFILE = {
446
- maxChars: MAX_IN_TURN_TOOL_CONTENT_CHARS,
447
- headRatio: 0.5
448
- };
449
- var TOOL_OUTPUT_PROFILES = {
450
- readFile: { maxChars: 4800, headRatio: 0.68, historicalMaxChars: 4800 },
451
- read_file: { maxChars: 4800, headRatio: 0.68, historicalMaxChars: 4800 },
452
- readWorkspaceFile: { maxChars: 4800, headRatio: 0.68, historicalMaxChars: 4800 },
453
- searchFiles: { maxChars: 3600, headRatio: 0.78 },
454
- search_files: { maxChars: 3600, headRatio: 0.78 },
455
- listFiles: { maxChars: 2800, headRatio: 0.85 },
456
- globFiles: { maxChars: 2800, headRatio: 0.85 },
457
- execShell: { maxChars: 4e3, headRatio: 0.35 },
458
- runCommand: { maxChars: 4e3, headRatio: 0.35 },
459
- launchProcess: { maxChars: 2800, headRatio: 0.35 },
460
- editFile: { maxChars: 2800, headRatio: 0.62 },
461
- writeFile: { maxChars: 2400, headRatio: 0.62 },
462
- readPastedText: { maxChars: 4800, headRatio: 0.5, historicalMaxChars: 4800 }
463
- };
464
- function resolveToolOutputProfile(toolName) {
465
- return (toolName ? TOOL_OUTPUT_PROFILES[toolName] : void 0) ?? DEFAULT_TOOL_OUTPUT_PROFILE;
466
- }
467
- function resolveHistoricalToolContentCap(toolName, historicalMaxChars) {
468
- const profile = toolName ? TOOL_OUTPUT_PROFILES[toolName] : void 0;
469
- return profile?.historicalMaxChars !== void 0 ? Math.max(historicalMaxChars, profile.historicalMaxChars) : historicalMaxChars;
470
- }
471
- function projectToolMessageContent(input) {
472
- const { content, isFresh, toolName, historicalMaxChars } = input;
473
- if (isFresh) {
474
- if (content.length <= FRESH_TOOL_OUTPUT_MAX_CHARS) return content;
475
- return clipToolText(
476
- content,
477
- FRESH_TOOL_OUTPUT_MAX_CHARS,
478
- resolveToolOutputProfile(toolName).headRatio,
479
- `
480
- \u2026[\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${content.length} \u5B57\u7B26]`
481
- );
482
- }
483
- const cap = resolveHistoricalToolContentCap(toolName, historicalMaxChars);
484
- if (content.length <= cap) return content;
485
- return content.slice(0, cap) + `
486
- \u2026[\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${content.length} \u5B57\u7B26]`;
487
- }
488
- function clipToolText(content, maxChars, headRatio, marker = "\n\n[... tool output middle omitted; head/tail preserved ...]\n\n") {
489
- const normalized = content.replace(/\r\n/g, "\n").trim();
490
- if (normalized.length <= maxChars) return normalized;
491
- if (maxChars <= marker.length + 2) return normalized.slice(0, maxChars);
492
- const available = maxChars - marker.length;
493
- const headChars = Math.max(1, Math.floor(available * headRatio));
494
- const tailChars = Math.max(1, available - headChars);
495
- return `${normalized.slice(0, headChars)}${marker}${normalized.slice(-tailChars)}`;
496
- }
497
-
498
- // packages/agent-runtime/localAutoCompaction.ts
499
- var LOCAL_AUTO_COMPACTION_SYSTEM_PROMPT = `\u4F60\u662F\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u538B\u7F29\u5668\u3002\u6839\u636E\u3010\u73B0\u6709\u8BB0\u5FC6\u3011\u548C\u3010\u65B0\u589E\u5BF9\u8BDD\u3011\uFF0C\u8F93\u51FA\u53EF\u66FF\u4EE3\u539F\u59CB\u6D88\u606F\u7684\u4E8B\u5B9E\u6027\u8981\u70B9\uFF0C\u4F9B\u540E\u7EED\u5BF9\u8BDD\u76F4\u63A5\u4F7F\u7528\u3002
500
-
501
- \u4E25\u683C\u53EA\u8F93\u51FA\u4E0B\u9762\u4E09\u90E8\u5206\uFF0C\u6807\u9898\u5FC5\u987B\u5B8C\u5168\u4E00\u81F4\uFF1A
502
- \u5173\u952E\u4E8B\u5B9E\u6863\u6848
503
- - ...
504
- \u5BF9\u8BDD\u8FDB\u5C55\u4E0E\u5F85\u529E
505
- - ...
506
- \u6587\u4EF6\u64CD\u4F5C\u6E05\u5355
507
- - ...
508
-
509
- \u8981\u6C42\uFF1A
510
- 1) \u4F7F\u7528\u5BF9\u8BDD\u4E3B\u8BED\u8A00\uFF1B\u6DF7\u5408\u8BED\u8A00\u65F6\u4F18\u5148\u7528\u6237\u4E3B\u8981\u8BED\u8A00\uFF1B\u4E13\u6709\u540D\u8BCD\u3001\u6587\u4EF6\u8DEF\u5F84\u3001\u6807\u8BC6\u7B26\u3001\u547D\u4EE4\u4FDD\u7559\u539F\u6587\u3002
511
- 2) \u5173\u952E\u4E8B\u5B9E\u6863\u6848\u53EA\u4FDD\u7559\u4E4B\u540E\u7EE7\u7EED\u5BF9\u8BDD\u4ECD\u6709\u4EF7\u503C\u7684\u4FE1\u606F\uFF1A\u7528\u6237\u76EE\u6807/\u504F\u597D\u3001\u7EA6\u675F\u3001\u6280\u672F\u6808\u3001\u786E\u5B9A\u7684\u6587\u4EF6\u8DEF\u5F84\u3001\u6838\u5FC3\u51B3\u7B56\u3001\u672A\u5B8C\u6210\u5F85\u529E\u3002
512
- 3) \u5BF9\u8BDD\u8FDB\u5C55\u4E0E\u5F85\u529E\uFF1A\u5148\u6781\u7B80\u6982\u62EC\u65E7\u4E0A\u4E0B\u6587\uFF0C\u518D\u66F4\u8BE6\u7EC6\u8BB0\u5F55\u6700\u8FD1\u8FDB\u5C55\u3001\u7ED3\u8BBA\u3001\u5206\u6B67\u4E0E\u4E0B\u4E00\u6B65\u3002
513
- 4) \u6587\u4EF6\u64CD\u4F5C\u6E05\u5355\uFF1A\u5217\u51FA\u672C\u6B21\u538B\u7F29\u8303\u56F4\u5185\u8BFB\u53D6\u3001\u5199\u5165\u3001\u7F16\u8F91\u8FC7\u7684\u6587\u4EF6\u8DEF\u5F84\u53CA\u64CD\u4F5C\u7C7B\u578B\uFF08\u5982: - \u8BFB\u53D6: path/to/file\uFF1B\u82E5\u672A\u6D89\u53CA\u6587\u4EF6\u64CD\u4F5C\u5199"\u65E0"\uFF09\u3002
514
- 5) \u5FFD\u7565\u5BD2\u6684\u3001\u91CD\u590D\u5C1D\u8BD5\u3001\u5DF2\u653E\u5F03\u65B9\u6848\u548C\u65E0\u4EF7\u503C\u5E9F\u8BDD\u3002
515
- 6) \u4E0D\u8981\u7F16\u9020\u672A\u51FA\u73B0\u7684\u4FE1\u606F\uFF1B\u4E0D\u8981\u5F00\u573A\u767D\u3001\u7ED3\u675F\u8BED\u3001markdown \u4EE3\u7801\u5757\u6216\u989D\u5916\u7AE0\u8282\uFF1B\u4E0D\u8981\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\u3002`;
516
- function extractFileOperations(msgs) {
517
- const result = [];
518
- const seen = /* @__PURE__ */ new Set();
519
- for (const msg of msgs) {
520
- if (!Array.isArray(msg.tool_calls)) continue;
521
- for (const call of msg.tool_calls) {
522
- const name = call.function?.name || call.name || "";
523
- if (!name) continue;
524
- const canonicalName = canonicalizeToolName(name);
525
- const fileOperationByTool = {
526
- readFile: "read",
527
- writeFile: "write",
528
- editFile: "edit"
529
- };
530
- const opType = fileOperationByTool[canonicalName];
531
- if (!opType) continue;
532
- let rawArgs = call.function?.arguments ?? call.arguments;
533
- let parsedArgs = null;
534
- if (typeof rawArgs === "string") {
535
- try {
536
- parsedArgs = JSON.parse(rawArgs);
537
- } catch {
538
- }
539
- } else if (typeof rawArgs === "object" && rawArgs !== null) {
540
- parsedArgs = rawArgs;
541
- }
542
- const path = parsedArgs?.path ?? parsedArgs?.filePath ?? parsedArgs?.file;
543
- if (typeof path === "string" && path.trim()) {
544
- const key = `${opType}:${path.trim()}`;
545
- if (!seen.has(key)) {
546
- seen.add(key);
547
- result.push({ type: opType, path: path.trim() });
548
- }
549
- }
550
- }
551
- }
552
- return result;
553
- }
554
- function formatFileOperations(msgs) {
555
- const ops = extractFileOperations(msgs);
556
- if (ops.length === 0) {
557
- return "\u65E0";
558
- }
559
- const typeLabelMap = {
560
- read: "\u8BFB\u53D6",
561
- write: "\u5199\u5165",
562
- edit: "\u7F16\u8F91"
563
- };
564
- return ops.map((op) => `- ${typeLabelMap[op.type]}: ${op.path}`).join("\n");
565
- }
566
- function buildLocalAutoCompactionUserContent(previousSummary, messagesText, fileOpsText) {
567
- const parts = [`\u3010\u73B0\u6709\u8BB0\u5FC6\u3011\uFF1A
568
- ${previousSummary || "(\u65E0)"}`];
569
- if (fileOpsText !== void 0) {
570
- parts.push(`\u3010\u6587\u4EF6\u64CD\u4F5C\u6E05\u5355\u3011\uFF1A
571
- ${fileOpsText || "\u65E0"}`);
572
- }
573
- parts.push(`\u3010\u65B0\u589E\u5BF9\u8BDD\u3011\uFF1A
574
- ${messagesText}`);
575
- return parts.join("\n\n").trim();
576
- }
577
- function toPlanCompressionMessages(history) {
578
- return history.map((message, index) => {
579
- const usage = message.usage;
580
- return {
581
- id: `local-${index}`,
582
- role: message.role,
583
- content: message.content,
584
- ...Array.isArray(message.tool_calls) ? { tool_calls: message.tool_calls } : {},
585
- ...usage?.completion_tokens != null ? { usage: { completion_tokens: usage.completion_tokens } } : {}
586
- };
587
- });
588
- }
589
- function formatMessagesForLocalSummary(msgs) {
590
- return msgs.map((msg) => {
591
- const content = serializeMessageContent(msg.content) || (Array.isArray(msg.tool_calls) ? `[tool_calls:${msg.tool_calls.map((c) => c.function?.name).filter(Boolean).join(",")}]` : "[\u975E\u6587\u672C\u5185\u5BB9]");
592
- return `${msg.role}: ${content}`;
593
- }).join("\n");
594
- }
595
- function buildLocalSummaryHistoryMessage(summary) {
596
- const layer = buildDialogSummaryLayer({ summary });
597
- return {
598
- role: "user",
599
- content: layer?.content ?? `--- \u5386\u53F2\u5BF9\u8BDD\u6458\u8981 ---
600
- ${summary.trim()}`
601
- };
602
- }
603
- function projectHistoryWithSummary(args) {
604
- const bridged = toPlanCompressionMessages(args.history);
605
- let startIndex = 0;
606
- if (args.summarizedBeforeId) {
607
- const found = bridged.findIndex((m) => m.id === args.summarizedBeforeId);
608
- if (found !== -1) startIndex = found + 1;
609
- }
610
- return [
611
- buildLocalSummaryHistoryMessage(args.summary),
612
- ...args.history.slice(startIndex)
613
- ];
614
- }
615
- var COLD_RESUME_IDLE_MS = 60 * 60 * 1e3;
616
- function isColdResume(history, nowMs) {
617
- for (let i = history.length - 1; i >= 0; i -= 1) {
618
- const at = history[i]?.createdAt;
619
- if (typeof at === "number" && Number.isFinite(at)) {
620
- return nowMs - at > COLD_RESUME_IDLE_MS;
621
- }
622
- }
623
- return false;
624
- }
625
- async function maybeAutoCompactLocalHistory(args) {
626
- const { adapter, dialogId, history } = args;
627
- const unchanged = () => ({
628
- history,
629
- compressed: false,
630
- summaryGenerated: false
631
- });
632
- if (!dialogId || history.length === 0 || typeof adapter.loadDialogSummary !== "function" || typeof adapter.saveDialogSummary !== "function") {
633
- return unchanged();
634
- }
635
- let stored = null;
636
- try {
637
- stored = await adapter.loadDialogSummary(dialogId);
638
- } catch (error) {
639
- console.warn("[localLoop] loadDialogSummary failed:", error);
640
- return unchanged();
641
- }
642
- const existingSummary = typeof stored?.summary === "string" ? stored.summary : "";
643
- const summarizedBeforeId = typeof stored?.summarizedBeforeId === "string" ? stored.summarizedBeforeId : void 0;
644
- const contextWindow = typeof args.contextWindow === "number" && Number.isFinite(args.contextWindow) && args.contextWindow > 0 ? args.contextWindow : getModelContextWindow(args.model ?? "");
645
- const allMsgs = toPlanCompressionMessages(history);
646
- const coldResume = isColdResume(history, args.now?.() ?? Date.now());
647
- const plan = planCompression({
648
- allMsgs,
649
- summarizedBeforeId,
650
- summary: existingSummary,
651
- contextWindow,
652
- ...coldResume ? { force: true, reason: "context_budget" } : {}
653
- });
654
- const projectExisting = () => {
655
- if (!existingSummary.trim()) return unchanged();
656
- return {
657
- history: projectHistoryWithSummary({
658
- history,
659
- summary: existingSummary,
660
- summarizedBeforeId
661
- }),
662
- compressed: true,
663
- summaryGenerated: false
664
- };
665
- };
666
- if (!plan.shouldCompress) {
667
- return projectExisting();
668
- }
669
- try {
670
- const provider = await args.resolveProvider();
671
- const msgsToCompress = plan.msgsToCompress;
672
- const messagesText = formatMessagesForLocalSummary(msgsToCompress);
673
- const fileOpsText = formatFileOperations(msgsToCompress);
674
- const promptContent = buildLocalAutoCompactionUserContent(
675
- existingSummary,
676
- messagesText,
677
- fileOpsText
678
- );
679
- const result = await provider.complete([
680
- { role: "system", content: LOCAL_AUTO_COMPACTION_SYSTEM_PROMPT },
681
- { role: "user", content: promptContent }
682
- ]);
683
- const newSummary = typeof result.content === "string" ? result.content.trim() : "";
684
- if (!newSummary) {
685
- console.warn(
686
- "[localLoop] auto-compaction produced empty summary; keeping prior projection"
687
- );
688
- return projectExisting();
689
- }
690
- await adapter.saveDialogSummary({
691
- dialogId,
692
- summary: newSummary,
693
- summarizedBeforeId: plan.newSummarizedBeforeId
694
- });
695
- return {
696
- history: projectHistoryWithSummary({
697
- history,
698
- summary: newSummary,
699
- summarizedBeforeId: plan.newSummarizedBeforeId
700
- }),
701
- compressed: true,
702
- summaryGenerated: true,
703
- ...result.usage ? { usage: result.usage } : {}
704
- };
705
- } catch (error) {
706
- console.warn("[localLoop] auto-compaction failed:", error);
707
- return projectExisting();
708
- }
709
- }
710
-
711
- // packages/agent-runtime/localLoop.ts
712
- var LOCAL_AGENT_CONFIG_MISSING_CODE = "LOCAL_AGENT_CONFIG_MISSING";
713
- var EMPTY_ASSISTANT_REPAIR_PROMPT = "\u8BF7\u7ED9\u51FA\u660E\u786E\u7684\u6587\u5B57\u56DE\u7B54\u6216\u6267\u884C\u4E0B\u4E00\u6B65\uFF1A\u5982\u679C\u4EFB\u52A1\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u76F4\u63A5\u603B\u7ED3\u7ED3\u679C\uFF1B\u5982\u679C\u9700\u8981\u8C03\u7528\u5DE5\u5177\uFF0C\u8BF7\u76F4\u63A5\u8F93\u51FA tool_calls\u3002\u8BF7\u5207\u52FF\u8FD4\u56DE\u7A7A\u5185\u5BB9\u3002";
714
- var EMPTY_ASSISTANT_FALLBACK_MESSAGE = "\u6A21\u578B\u8FDE\u7EED\u8FD4\u56DE\u7A7A\u6D88\u606F\uFF0C\u5F53\u524D\u4EFB\u52A1\u672A\u5B8C\u6210\u3002\u8BF7\u91CD\u8BD5\u5F53\u524D\u6B65\u9AA4\uFF0C\u6216\u7ED9\u51FA\u66F4\u5177\u4F53\u7684\u4FEE\u6539\u8303\u56F4\u3002";
715
- var LENGTH_TRUNCATED_FALLBACK_MESSAGE = "\u8F93\u51FA\u8FBE\u5230\u957F\u5EA6\u4E0A\u9650\u88AB\u622A\u65AD\uFF0C\u5EFA\u8BAE\u7F29\u77ED\u4EFB\u52A1\u6216\u63D0\u9AD8\u8F93\u51FA\u4E0A\u9650\u3002";
716
- var STREAM_TRUNCATED_FALLBACK_MESSAGE = "\u4E0A\u6E38\u54CD\u5E94\u6D41\u5728\u6536\u5C3E\u524D\u88AB\u4E2D\u65AD\uFF08\u672A\u6536\u5230\u7ED3\u675F\u6807\u8BB0\uFF09\uFF0C\u672C\u8F6E\u8F93\u51FA\u4E0D\u5B8C\u6574\u3002\u8BF7\u91CD\u8BD5\u5F53\u524D\u6B65\u9AA4\u3002";
717
- function resolveEmptyAssistantOutcome(args) {
718
- if (args.hasToolCalls || args.hasVisibleOutput) return { kind: "ok" };
719
- if (args.finishReason === "length") return { kind: "fallback", reason: "length_truncated" };
720
- if (!args.repairUsed) return { kind: "repair" };
721
- if (!args.finishReason && !args.streamComplete) {
722
- return { kind: "fallback", reason: "stream_truncated" };
723
- }
724
- return { kind: "fallback", reason: "empty_completion" };
725
- }
726
- function resolveEmptyAssistantFallbackMessage(reason) {
727
- if (reason === "length_truncated") return LENGTH_TRUNCATED_FALLBACK_MESSAGE;
728
- if (reason === "stream_truncated") return STREAM_TRUNCATED_FALLBACK_MESSAGE;
729
- return EMPTY_ASSISTANT_FALLBACK_MESSAGE;
730
- }
731
- function hasAssistantVisibleOutput(content) {
732
- if (typeof content === "string") return content.trim().length > 0;
733
- if (!Array.isArray(content)) return false;
734
- return content.some((part) => {
735
- if (part?.type === "text" && String(part.text ?? "").trim()) return true;
736
- if (part?.type === "image_url") {
737
- const url = part?.image_url?.url;
738
- return typeof url === "string" && url.trim().length > 0;
739
- }
740
- return false;
741
- });
742
- }
743
- function formatToolExecutionError(args) {
744
- const message = toErrorMessage(args.error);
745
- return `${args.toolName} failed: ${message}`;
746
- }
747
- function formatStructuredToolExecutionError(args) {
748
- if (!args.error || typeof args.error !== "object") return null;
749
- const error = args.error;
750
- if (typeof error.code !== "string") return null;
751
- return JSON.stringify({
752
- error: error.code,
753
- message: typeof error.message === "string" ? error.message : formatToolExecutionError(args),
754
- ...error.policy && typeof error.policy === "object" ? { policy: error.policy } : {},
755
- ...error.permissionRequest && typeof error.permissionRequest === "object" ? { permissionRequest: error.permissionRequest } : {}
756
- });
757
- }
758
- function shouldReturnToolExecutionErrors(adapter) {
759
- return adapter.capabilities.includes("local-tools");
760
- }
761
- function emitToolEvent(input, event) {
762
- input.onToolEvent?.(event);
763
- }
764
- function emitLoopEvent(input, event) {
765
- if (!input.onLoopEvent) return;
766
- try {
767
- input.onLoopEvent(event);
768
- } catch {
769
- }
770
- }
771
- var LLM_REQUEST_TIMEOUT = "LLM_REQUEST_TIMEOUT";
772
- var LOCAL_TURN_ABORTED_CODE = "LOCAL_TURN_ABORTED";
773
- function buildAbortedError() {
774
- const error = new Error("local agent turn aborted by user");
775
- error.code = LOCAL_TURN_ABORTED_CODE;
776
- return error;
777
- }
778
- function throwIfAborted(input) {
779
- if (input.abortSignal?.aborted) throw buildAbortedError();
780
- }
781
- async function raceWithAbort(input, promise, pendingToolName) {
782
- const signal = input.abortSignal;
783
- if (!signal) return promise;
784
- let abortListener;
785
- const abortPromise = new Promise((_resolve, reject) => {
786
- abortListener = () => {
787
- const error = buildAbortedError();
788
- if (pendingToolName) error.pendingToolName = pendingToolName;
789
- promise.catch(() => {
790
- });
791
- reject(error);
792
- };
793
- if (signal.aborted) {
794
- abortListener();
795
- return;
796
- }
797
- signal.addEventListener("abort", abortListener, { once: true });
798
- });
799
- try {
800
- return await Promise.race([promise, abortPromise]);
801
- } finally {
802
- if (signal && abortListener) signal.removeEventListener("abort", abortListener);
803
- }
804
- }
805
- function resolveLlmRequestTimeoutMs(input) {
806
- const raw = input.llmRequestTimeoutMs ?? input.timeoutMs;
807
- if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return void 0;
808
- return raw;
809
- }
810
- async function runCompleteWithTimeout(args) {
811
- const { provider, messages, options, timeoutMs, round, input, context } = args;
812
- emitLoopEvent(input, {
813
- kind: "llm-start",
814
- round,
815
- atMs: Date.now(),
816
- ...context ? { context } : {}
817
- });
818
- const complete = provider.complete(messages, options);
819
- let ok = false;
820
- const signal = input.abortSignal;
821
- let abortListener;
822
- const racers = [];
823
- if (signal) {
824
- racers.push(
825
- new Promise((_resolve, reject) => {
826
- abortListener = () => reject(buildAbortedError());
827
- if (signal.aborted) {
828
- abortListener();
829
- return;
830
- }
831
- signal.addEventListener("abort", abortListener, { once: true });
832
- })
833
- );
834
- }
835
- let timer;
836
- if (typeof timeoutMs === "number") {
837
- racers.push(
838
- new Promise((_resolve, reject) => {
839
- timer = setTimeout(() => {
840
- reject(new Error(`__llm_timeout__:${timeoutMs}`));
841
- }, timeoutMs);
842
- })
843
- );
844
- }
845
- let result;
846
- try {
847
- result = racers.length === 0 ? await complete : await Promise.race([complete, ...racers]);
848
- ok = true;
849
- return result;
850
- } catch (error) {
851
- const isTimeout = error instanceof Error && error.message.startsWith("__llm_timeout__:");
852
- if (!isTimeout) throw error;
853
- const timeoutError = new Error(
854
- `LLM request timed out after ${timeoutMs}ms (round ${round})`
855
- );
856
- timeoutError.code = LLM_REQUEST_TIMEOUT;
857
- throw timeoutError;
858
- } finally {
859
- if (timer) clearTimeout(timer);
860
- if (signal && abortListener) signal.removeEventListener("abort", abortListener);
861
- const usage = result?.usage;
862
- const cacheHit = Number(usage?.cache_read_input_tokens ?? usage?.prompt_cache_hit_tokens ?? 0);
863
- const cacheMiss = Number(usage?.cache_creation_input_tokens ?? usage?.prompt_cache_miss_tokens ?? 0);
864
- const inputTokens = Number(usage?.input_tokens ?? usage?.prompt_tokens ?? 0);
865
- const outputTokens = Number(usage?.output_tokens ?? usage?.completion_tokens ?? 0);
866
- emitLoopEvent(input, {
867
- kind: "llm-end",
868
- round,
869
- atMs: Date.now(),
870
- ok,
871
- ...inputTokens > 0 || cacheHit > 0 || cacheMiss > 0 ? {
872
- cache: {
873
- inputTokens,
874
- outputTokens,
875
- cacheHitTokens: cacheHit,
876
- cacheMissTokens: cacheMiss,
877
- hitRatio: inputTokens > 0 ? Math.round(cacheHit / inputTokens * 1e4) / 1e4 : 0
878
- }
879
- } : {}
880
- });
881
- }
882
- }
883
- function clip2(value, max = 240) {
884
- return clipCompactText(value, max);
885
- }
886
- function summarizeToolResult(content, metadata) {
887
- const parts = [];
888
- const exitCode = metadata?.exitCode;
889
- if (typeof exitCode === "number") parts.push(`exit=${exitCode}`);
890
- if (typeof content === "string") {
891
- const trimmed = content.trim();
892
- if (trimmed) {
893
- const lines = trimmed.split(/\r?\n/).length;
894
- parts.push(`${lines} line${lines === 1 ? "" : "s"}`);
895
- parts.push(`${trimmed.length} chars`);
896
- const tail = clip2(trimmed.slice(-160), 160);
897
- if (tail) parts.push(`tail="${tail}"`);
898
- } else {
899
- parts.push("empty");
900
- }
901
- }
902
- return parts.join(" ");
903
- }
904
- function formatToolMessageContent(args) {
905
- if (args.toolName !== "listFiles" && args.toolName !== "globFiles" && args.toolName !== "searchFiles" && args.toolName !== "readFile" || !args.metadata || Object.keys(args.metadata).length === 0) {
906
- return args.content;
907
- }
908
- return `${args.content}
909
-
910
- [tool metadata]
911
- ${JSON.stringify(args.metadata)}`;
912
- }
913
- function blocksToOpenAiMessages(blocks) {
914
- const out = [];
915
- let text = "";
916
- let reasoning = "";
917
- let pendingToolCalls = [];
918
- let pendingToolResults = [];
919
- const flushSegment = () => {
920
- if (text === "" && pendingToolCalls.length === 0 && reasoning === "") return;
921
- out.push({
922
- role: "assistant",
923
- content: text || null,
924
- ...reasoning ? { reasoning_content: reasoning } : {},
925
- ...pendingToolCalls.length > 0 ? { tool_calls: pendingToolCalls } : {}
926
- });
927
- for (let i = 0; i < pendingToolCalls.length; i += 1) {
928
- const tc = pendingToolCalls[i];
929
- const res = pendingToolResults[i];
930
- out.push({
931
- role: "tool",
932
- content: formatToolMessageContent({
933
- toolName: tc.function.name,
934
- content: res?.content ?? "",
935
- ...res?.metadata ? { metadata: res.metadata } : {}
936
- }),
937
- tool_call_id: tc.id,
938
- toolName: tc.function.name,
939
- ...res?.metadata ? { tool_result_metadata: res.metadata } : {}
940
- });
941
- }
942
- text = "";
943
- reasoning = "";
944
- pendingToolCalls = [];
945
- pendingToolResults = [];
946
- };
947
- for (const block of blocks) {
948
- if (block.type === "text") {
949
- if (pendingToolCalls.length > 0) {
950
- flushSegment();
951
- }
952
- text += block.text;
953
- continue;
954
- }
955
- if (block.type === "thinking") {
956
- reasoning += block.thinking;
957
- continue;
958
- }
959
- if (block.type === "toolCall") {
960
- pendingToolCalls.push(block.toolCall);
961
- pendingToolResults.push({
962
- content: block.result?.content ?? "",
963
- ...block.result?.metadata ? { metadata: block.result.metadata } : {}
964
- });
965
- }
966
- }
967
- flushSegment();
968
- return out;
969
- }
970
- function buildActionGate(args) {
971
- const gate = readActionGate(args.metadata?.actionGate);
972
- if (!gate) return null;
973
- if (gate.kind === "handoff" && !readCommandActionGatePayload(gate.payload)) return null;
974
- return {
975
- ...gate,
976
- toolName: args.toolName,
977
- toolCallId: args.toolCallId
978
- };
979
- }
980
- var TOOL_METADATA_KEYS = [
981
- "path",
982
- "query",
983
- "effectivePattern",
984
- "startLine",
985
- "endLine",
986
- "totalLines",
987
- "totalBytes",
988
- "bytes",
989
- "totalChars",
990
- "count",
991
- "matchCount",
992
- "matchedFiles",
993
- "truncated",
994
- "limitedByMaxResults",
995
- "limitedByMaxDepth",
996
- "visitedEntries",
997
- "maxResults",
998
- "exitCode",
999
- "status",
1000
- "timedOut",
1001
- "aborted",
1002
- "replacements",
1003
- "code",
1004
- "error",
1005
- "message",
1006
- "warnings",
1007
- "pasteId",
1008
- "source"
1009
- ];
1010
- function trimHistoryToContextBudget(history, model) {
1011
- if (history.length === 0) return { history, droppedCount: 0 };
1012
- const { rawMessageBudget } = planContextUsage({
1013
- contextWindow: getModelContextWindow(model ?? ""),
1014
- summaryTokens: 0,
1015
- // localLoop 没有 web 端的负载分档器;medium 是中性默认值,
1016
- // 不为了省几个 token 在这里复制一份分类逻辑。
1017
- recentLoad: "medium"
1018
- });
1019
- const messageTokens = (message) => {
1020
- const toolCalls = message.tool_calls;
1021
- return estimateTokenCount(contentAsText(message.content)) + (Array.isArray(toolCalls) ? estimateTokenCount(JSON.stringify(toolCalls)) : 0);
1022
- };
1023
- let used = 0;
1024
- let start = history.length;
1025
- for (let i = history.length - 1; i >= 0; i -= 1) {
1026
- const cost = messageTokens(history[i]);
1027
- if (used + cost > rawMessageBudget && start < history.length) break;
1028
- used += cost;
1029
- start = i;
1030
- }
1031
- if (start === 0) return { history, droppedCount: 0 };
1032
- return {
1033
- history: sanitizeToolCallPairing(history.slice(start)),
1034
- droppedCount: start
1035
- };
1036
- }
1037
- function contentAsText(content) {
1038
- if (typeof content === "string") return content;
1039
- if (!Array.isArray(content)) return "";
1040
- return content.map((part) => {
1041
- if (part?.type === "text") return part.text;
1042
- if (part?.type === "image_url") return part.image_url.url;
1043
- return "";
1044
- }).join("\n");
1045
- }
1046
- function contentCharCount(content) {
1047
- if (typeof content === "string") return content.length;
1048
- if (!Array.isArray(content)) return 0;
1049
- return content.reduce((total, part) => {
1050
- if (part?.type === "text") return total + part.text.length;
1051
- if (part?.type === "image_url") return total + part.image_url.url.length;
1052
- return total;
1053
- }, 0);
1054
- }
1055
- function compactToolMetadata(metadata) {
1056
- if (!metadata) return "";
1057
- const selected = {};
1058
- for (const key of TOOL_METADATA_KEYS) {
1059
- const value = metadata[key];
1060
- if (value === void 0) continue;
1061
- if (typeof value === "string") {
1062
- selected[key] = clipCompactText(value, 240);
1063
- continue;
1064
- }
1065
- if (Array.isArray(value)) {
1066
- selected[key] = value.slice(0, 20).map(
1067
- (item) => typeof item === "string" ? clipCompactText(item, 180) : clipCompactText(JSON.stringify(item), 180)
1068
- );
1069
- continue;
1070
- }
1071
- selected[key] = value;
1072
- }
1073
- return Object.keys(selected).length > 0 ? clipCompactText(JSON.stringify(selected), 1200) : "";
1074
- }
1075
- function projectToolContentForProvider(args) {
1076
- const content = args.content;
1077
- if (typeof content !== "string") return content;
1078
- const metadataText = compactToolMetadata(args.metadata);
1079
- const embeddedMetadataIndex = metadataText ? content.indexOf("\n\n[tool metadata]\n") : -1;
1080
- const contentForProjection = embeddedMetadataIndex >= 0 ? content.slice(0, embeddedMetadataIndex) : content;
1081
- const metadataSuffix = metadataText ? `
1082
-
1083
- [tool metadata]
1084
- ${metadataText}` : "";
1085
- if (embeddedMetadataIndex >= 0 && content.length <= args.maxChars) {
1086
- return content;
1087
- }
1088
- const headRatio = resolveToolOutputProfile(args.toolName).headRatio;
1089
- const diagnostic = (clippedLength) => `[${args.label}; originalChars=${content.length}; omittedChars=${Math.max(
1090
- 0,
1091
- content.length - clippedLength
1092
- )}]`;
1093
- const suffix = (clippedLength) => [diagnostic(clippedLength), metadataSuffix.trimStart()].filter(Boolean).join("\n\n");
1094
- const initialBudget = Math.max(
1095
- 1,
1096
- args.maxChars - suffix(contentForProjection.length).length - 2
1097
- );
1098
- let clipped = clipToolText(contentForProjection, initialBudget, headRatio);
1099
- const wasClipped = clipped.length < contentForProjection.trim().length;
1100
- const needsProjection = wasClipped || Boolean(metadataSuffix) || embeddedMetadataIndex >= 0;
1101
- if (!needsProjection) return args.content;
1102
- let projected = wasClipped || metadataSuffix ? `${clipped}
1103
-
1104
- ${suffix(clipped.length)}` : clipped;
1105
- if (projected.length > args.maxChars) {
1106
- const boundedBudget = Math.max(
1107
- 1,
1108
- args.maxChars - suffix(clipped.length).length - 2
1109
- );
1110
- clipped = clipToolText(contentForProjection, boundedBudget, headRatio);
1111
- projected = `${clipped}
1112
-
1113
- ${suffix(clipped.length)}`;
1114
- }
1115
- return projected.length <= args.maxChars ? projected : projected.slice(0, args.maxChars);
1116
- }
1117
- function summarizeHistoricalToolContent(content, toolName, metadata) {
1118
- return projectToolContentForProvider({
1119
- content,
1120
- toolName,
1121
- metadata,
1122
- // Same single source of truth as the server path and the read ledgers:
1123
- // read-family tools keep their profile historical cap (4800) cross-turn,
1124
- // unprofiled tools fall back to the flat historical budget.
1125
- maxChars: resolveHistoricalToolContentCap(toolName, MAX_HISTORICAL_TOOL_CONTENT_CHARS),
1126
- label: "historical tool result truncated for the next turn"
1127
- });
1128
- }
1129
- function prepareMessagesForProviderCall(messages) {
1130
- const paired = sanitizeToolCallPairing(messages);
1131
- let freshRunStart = paired.length;
1132
- while (freshRunStart > 0 && paired[freshRunStart - 1].role === "tool") {
1133
- freshRunStart -= 1;
1134
- }
1135
- let toolMessageCount = 0;
1136
- let rawToolContentChars = 0;
1137
- let projectedToolContentChars = 0;
1138
- let truncatedToolResults = 0;
1139
- const projected = paired.map((message, index) => {
1140
- const { context_reference: _contextReference, ...providerMessage } = message;
1141
- const sanitizedContent = providerMessage.content == null ? "" : typeof providerMessage.content === "string" ? providerMessage.content : providerMessage.content;
1142
- if (providerMessage.role !== "tool") {
1143
- return {
1144
- ...providerMessage,
1145
- content: sanitizedContent
1146
- };
1147
- }
1148
- toolMessageCount += 1;
1149
- rawToolContentChars += contentCharCount(sanitizedContent);
1150
- const isFresh = index >= freshRunStart;
1151
- const maxChars = isFresh ? FRESH_TOOL_OUTPUT_MAX_CHARS : resolveToolOutputProfile(providerMessage.toolName).maxChars;
1152
- const projectedContent = projectToolContentForProvider({
1153
- content: sanitizedContent,
1154
- toolName: providerMessage.toolName,
1155
- metadata: providerMessage.tool_result_metadata,
1156
- maxChars,
1157
- label: "in-turn tool result truncated/projected before next provider call"
1158
- });
1159
- projectedToolContentChars += contentCharCount(projectedContent);
1160
- if (contentCharCount(projectedContent) < contentCharCount(sanitizedContent)) {
1161
- truncatedToolResults += 1;
1162
- }
1163
- return {
1164
- ...providerMessage,
1165
- content: projectedContent
1166
- };
1167
- });
1168
- return {
1169
- messages: projected,
1170
- metrics: {
1171
- messageCount: projected.length,
1172
- contentChars: projected.reduce((total, message) => total + contentCharCount(message.content), 0),
1173
- toolMessageCount,
1174
- rawToolContentChars,
1175
- projectedToolContentChars,
1176
- truncatedToolResults,
1177
- stableContextChars: 0,
1178
- dynamicContextChars: 0
1179
- }
1180
- };
1181
- }
1182
- function prepareHistoryForNextTurn(history, contextReferenceResolver) {
1183
- return history.map((message) => {
1184
- if (message.role === "user" && message.context_reference !== void 0 && contextReferenceResolver?.(message.context_reference)) {
1185
- return { ...message, content: message.context_reference };
1186
- }
1187
- if (message.role !== "tool") return message;
1188
- return {
1189
- ...message,
1190
- content: summarizeHistoricalToolContent(
1191
- message.content,
1192
- message.toolName,
1193
- message.tool_result_metadata
1194
- )
1195
- };
1196
- });
1197
- }
1198
- var IMAGE_OMITTED_PLACEHOLDER = "[Image content omitted: model does not support image input]";
1199
- function stripImagePartsFromContent(content) {
1200
- if (!Array.isArray(content)) return content;
1201
- const filtered = content.filter((part) => part?.type !== "image_url");
1202
- if (filtered.length === 0) return IMAGE_OMITTED_PLACEHOLDER;
1203
- return filtered;
1204
- }
1205
- function filterImagePartsFromMessages(messages, supportsImages) {
1206
- if (supportsImages) return messages;
1207
- return messages.map((msg) => ({
1208
- ...msg,
1209
- content: stripImagePartsFromContent(msg.content)
1210
- }));
1211
- }
1212
- function buildMessages(args) {
1213
- if (args.contextBlockScopes?.length) {
1214
- const blocks2 = args.contextBlockScopes.filter((b) => b.content.trim());
1215
- const stableParts = [args.prompt?.trim(), ...blocks2.filter((b) => b.cacheScope === "session").map((b) => b.content)].filter(Boolean);
1216
- const dynamicParts = blocks2.filter((b) => b.cacheScope === "turn").map((b) => b.content).map((block) => block.trim()).filter(Boolean);
1217
- const stableContent = stableParts.join("\n\n");
1218
- const dynamicContent = dynamicParts.join("\n\n");
1219
- const systemContent2 = dynamicContent ? `${stableContent}
1220
-
1221
- ${dynamicContent}` : stableContent;
1222
- return {
1223
- messages: [
1224
- ...systemContent2 ? [{
1225
- role: "system",
1226
- content: systemContent2,
1227
- ...stableContent ? { stable_prefix_chars: stableContent.length } : {}
1228
- }] : [],
1229
- ...prepareHistoryForNextTurn(args.history, args.contextReferenceResolver),
1230
- { role: "user", content: args.input }
1231
- ],
1232
- stableContextChars: stableContent.length,
1233
- dynamicContextChars: dynamicContent.length
1234
- };
1235
- }
1236
- const blocks = (args.contextBlocks ?? []).map((block) => block.trim()).filter(Boolean);
1237
- const systemContent = [args.prompt?.trim(), ...blocks].filter(Boolean).join("\n\n");
1238
- return {
1239
- messages: [
1240
- ...systemContent ? [{ role: "system", content: systemContent }] : [],
1241
- ...prepareHistoryForNextTurn(args.history, args.contextReferenceResolver),
1242
- { role: "user", content: args.input }
1243
- ],
1244
- stableContextChars: (args.prompt?.trim() ?? "").length,
1245
- dynamicContextChars: blocks.join("\n\n").length
1246
- };
1247
- }
1248
- function mergeTurnUsage(current, next) {
1249
- if (!next) return current;
1250
- const read = (usage) => ({
1251
- input: Number(usage.input_tokens ?? usage.prompt_tokens ?? 0),
1252
- output: Number(usage.output_tokens ?? usage.completion_tokens ?? 0),
1253
- cacheHit: Number(
1254
- usage.cache_read_input_tokens ?? usage.prompt_cache_hit_tokens ?? 0
1255
- ),
1256
- cacheMiss: Number(
1257
- usage.cache_creation_input_tokens ?? usage.prompt_cache_miss_tokens ?? 0
1258
- )
1259
- });
1260
- const right = read(next);
1261
- const left = current ? read(current) : { input: 0, output: 0, cacheHit: 0, cacheMiss: 0 };
1262
- return {
1263
- input_tokens: left.input + right.input,
1264
- output_tokens: left.output + right.output,
1265
- cache_read_input_tokens: left.cacheHit + right.cacheHit,
1266
- cache_creation_input_tokens: left.cacheMiss + right.cacheMiss
1267
- };
1268
- }
1269
- function addOutOfBandUsage(turn, extra) {
1270
- if (!extra) return turn;
1271
- const num = (u, ...keys) => {
1272
- if (!u) return 0;
1273
- for (const k of keys) {
1274
- const v = Number(u[k]);
1275
- if (Number.isFinite(v) && v !== 0) return v;
1276
- }
1277
- return 0;
1278
- };
1279
- return {
1280
- ...turn ?? {},
1281
- input_tokens: num(turn, "input_tokens", "prompt_tokens") + num(extra, "input_tokens", "prompt_tokens"),
1282
- output_tokens: num(turn, "output_tokens", "completion_tokens") + num(extra, "output_tokens", "completion_tokens"),
1283
- cache_read_input_tokens: num(turn, "cache_read_input_tokens", "prompt_cache_hit_tokens") + num(extra, "cache_read_input_tokens", "prompt_cache_hit_tokens"),
1284
- cache_creation_input_tokens: num(turn, "cache_creation_input_tokens", "prompt_cache_miss_tokens") + num(extra, "cache_creation_input_tokens", "prompt_cache_miss_tokens")
1285
- };
1286
- }
1287
- function extractUserInputText(content) {
1288
- if (typeof content === "string") return content;
1289
- if (!Array.isArray(content)) return "";
1290
- return content.flatMap((part) => part?.type === "text" && part.text ? [part.text] : []).join("\n").trim();
1291
- }
1292
- function attachDialogIdToError(error, dialogId) {
1293
- if (!dialogId) return;
1294
- if (typeof error === "object" && error !== null) {
1295
- error.dialogId = dialogId;
1296
- }
1297
- }
1298
- async function persistFailedLocalTurn(args) {
1299
- const errorMessage = toErrorMessage(args.error);
1300
- try {
1301
- const saved = await args.adapter.saveTurn({
1302
- agentKey: args.agentKey,
1303
- messages: args.messages,
1304
- result: {
1305
- content: args.partialContent || `[nolo] Agent run failed: ${errorMessage}`,
1306
- model: args.model ?? "unknown",
1307
- toolCallCount: args.toolCallCount ?? 0,
1308
- error: true,
1309
- errorMessage,
1310
- ...args.usage ? { usage: args.usage } : {}
1311
- },
1312
- ...args.accountingUsage ? { accountingUsage: args.accountingUsage } : {},
1313
- ...args.usageRecords?.length ? { usageRecords: args.usageRecords } : {},
1314
- ...args.billingConfig ? { billingConfig: args.billingConfig } : {},
1315
- ...args.input.runtimeContext ? { runtimeContext: args.input.runtimeContext } : {},
1316
- ...args.input.continueDialogId ? { continueDialogId: args.input.continueDialogId } : {},
1317
- ...args.input.spaceId ? { spaceId: args.input.spaceId } : {},
1318
- ...args.input.category ? { category: args.input.category } : {},
1319
- ...args.input.inheritedFromDialogKey ? { inheritedFromDialogKey: args.input.inheritedFromDialogKey } : {},
1320
- ...args.input.parentDialogId ? { parentDialogId: args.input.parentDialogId } : {}
1321
- });
1322
- return saved?.dialogId;
1323
- } catch {
1324
- return args.input.continueDialogId;
1325
- }
1326
- }
1327
- function applyPersistedTurnInput(messages, persistedInput, persistedInputReference) {
1328
- if (persistedInput === void 0 && persistedInputReference === void 0) {
1329
- return messages;
1330
- }
1331
- let replaced = false;
1332
- return messages.map((message) => {
1333
- if (replaced || message.role !== "user") return message;
1334
- replaced = true;
1335
- return {
1336
- ...message,
1337
- ...persistedInput !== void 0 ? { content: persistedInput } : {},
1338
- ...persistedInputReference !== void 0 ? { context_reference: persistedInputReference } : {}
1339
- };
1340
- });
1341
- }
1342
- async function runLocalAgentTurn(input) {
1343
- const agentConfig = await input.adapter.loadAgentConfig(input.agentRef);
1344
- if (!agentConfig) {
1345
- const error = new Error(
1346
- `agentRef "${input.agentRef}" \u672A\u5339\u914D\u5230\u672C\u5730 agent\u3002\u8FD9\u4E0D\u662F\u914D\u7F6E\u7F3A\u5931\uFF0C\u8BF7\u7528 listAgents \u67E5\u770B\u53EF\u7528 agent\uFF0C\u518D\u7528 readAgent \u89E3\u6790 agentKey\uFF0C\u52FF\u624B\u5DE5\u62FC key\u3002`
1347
- );
1348
- error.code = LOCAL_AGENT_CONFIG_MISSING_CODE;
1349
- error.agentRef = input.agentRef;
1350
- throw error;
1351
- }
1352
- const rawBillingConfig = agentConfig.rawRecord ?? {};
1353
- const billingConfig = {
1354
- model: agentConfig.model || "unknown",
1355
- ...agentConfig.provider ? { provider: agentConfig.provider } : {},
1356
- ...agentConfig.apiSource ? { apiSource: agentConfig.apiSource } : {},
1357
- ...agentConfig.apiKeyRef !== void 0 ? { apiKeyRef: agentConfig.apiKeyRef } : {},
1358
- ...typeof rawBillingConfig.inputPrice === "number" ? { inputPrice: rawBillingConfig.inputPrice } : {},
1359
- ...typeof rawBillingConfig.outputPrice === "number" ? { outputPrice: rawBillingConfig.outputPrice } : {},
1360
- ...rawBillingConfig.sharingLevel ? { sharingLevel: rawBillingConfig.sharingLevel } : {},
1361
- id: agentConfig.key,
1362
- ...typeof rawBillingConfig.userId === "string" ? { userId: rawBillingConfig.userId } : {}
1363
- };
1364
- let history = [];
1365
- try {
1366
- history = input.continueDialogId ? await input.adapter.loadDialogHistory(input.continueDialogId) : [];
1367
- } catch (error) {
1368
- const dialogId = await persistFailedLocalTurn({
1369
- adapter: input.adapter,
1370
- agentKey: agentConfig.key,
1371
- messages: [{ role: "user", content: input.input }],
1372
- error,
1373
- model: agentConfig.model,
1374
- input
1375
- });
1376
- attachDialogIdToError(error, dialogId);
1377
- throw error;
1378
- }
1379
- const identityBlock = buildIdentityBlock({
1380
- agentName: agentConfig.name,
1381
- agentId: agentConfig.key,
1382
- model: agentConfig.model
1383
- });
1384
- const agentTools = canonicalizeToolNames(
1385
- agentConfig.exposedToolNames ?? agentConfig.toolNames ?? []
1386
- );
1387
- const guidanceBlocks = buildRuntimeGuidanceBlocks(agentTools);
1388
- const toolGuidedSections = resolveToolGuidedSections(agentTools);
1389
- const guidanceScopes = [
1390
- guidanceBlocks.startupProtocol,
1391
- guidanceBlocks.contextLayerContract,
1392
- guidanceBlocks.emailRegistrationWorkflow,
1393
- guidanceBlocks.webResearchToolPolicy,
1394
- ...Object.values(toolGuidedSections)
1395
- ].map((content) => content.trim()).filter((content) => content.length > 0).map((content) => ({ content, cacheScope: "session" }));
1396
- const currentTimeScope = [
1397
- { content: buildCurrentTimeBlock(/* @__PURE__ */ new Date(), void 0), cacheScope: "turn" }
1398
- ];
1399
- const callerScopes = normalizeContextBlockScopes(
1400
- input.contextBlocks,
1401
- input.contextBlockScopes
1402
- );
1403
- const mergedContextBlockScopes = [
1404
- { content: identityBlock, cacheScope: "session" },
1405
- ...guidanceScopes,
1406
- ...currentTimeScope,
1407
- ...callerScopes
1408
- ];
1409
- let resolvedProvider;
1410
- const resolveProviderOnce = async () => {
1411
- if (!resolvedProvider) {
1412
- resolvedProvider = await input.adapter.resolveProvider(agentConfig);
1413
- }
1414
- return resolvedProvider;
1415
- };
1416
- let compactionUsage;
1417
- try {
1418
- const compacted = await maybeAutoCompactLocalHistory({
1419
- adapter: input.adapter,
1420
- dialogId: input.continueDialogId,
1421
- history,
1422
- model: agentConfig.model,
1423
- resolveProvider: resolveProviderOnce
1424
- });
1425
- history = compacted.history;
1426
- compactionUsage = compacted.usage;
1427
- } catch (error) {
1428
- console.warn("[localLoop] auto-compaction unexpected error:", error);
1429
- }
1430
- const trimmedHistory = trimHistoryToContextBudget(history, agentConfig.model);
1431
- if (trimmedHistory.droppedCount > 0) {
1432
- history = trimmedHistory.history;
1433
- }
1434
- const hasContextBlocks = callerScopes.some((block) => block.content.trim()) || mergedContextBlockScopes.some((block) => block.content.trim());
1435
- const promptMessageCount = agentConfig.prompt?.trim() || hasContextBlocks ? 1 : 0;
1436
- const turnStartIndex = promptMessageCount + history.length;
1437
- const builtMessages = buildMessages({
1438
- prompt: agentConfig.prompt,
1439
- contextBlockScopes: mergedContextBlockScopes,
1440
- history,
1441
- input: input.input,
1442
- contextReferenceResolver: input.adapter.host === "cli" ? input.contextReferenceResolver : void 0
1443
- });
1444
- const messages = builtMessages.messages;
1445
- const supportsImages = resolveAgentImageInputSupport({
1446
- apiSource: agentConfig.apiSource,
1447
- provider: agentConfig.provider,
1448
- model: agentConfig.model,
1449
- hasVision: agentConfig.hasVision
1450
- });
1451
- const userInputText = extractUserInputText(input.input);
1452
- let toolCallCount = 0;
1453
- let result;
1454
- let turnUsage;
1455
- let contextUsage;
1456
- const usageRecords = [];
1457
- let loopError;
1458
- let round = 0;
1459
- let emptyAssistantRepairPending = false;
1460
- let emptyAssistantRepairUsed = false;
1461
- let imageDowngradeNotified = false;
1462
- let skipFinalAppend = false;
1463
- let partialContent = "";
1464
- try {
1465
- const provider = await resolveProviderOnce();
1466
- while (true) {
1467
- partialContent = "";
1468
- throwIfAborted(input);
1469
- const preparedMessages = prepareMessagesForProviderCall(messages);
1470
- let baseRequestMessages;
1471
- if (!supportsImages && hasImageInRuntimeMessages(preparedMessages.messages)) {
1472
- const imageUrls = extractImageUrlsFromMessages(preparedMessages.messages);
1473
- const preprocessed = await preprocessImagesForTextOnlyAgent(
1474
- preparedMessages.messages,
1475
- (urls) => describeImageWithLocalProvider(input.adapter, urls)
1476
- );
1477
- if (preprocessed !== preparedMessages.messages) {
1478
- baseRequestMessages = preprocessed;
1479
- if (!imageDowngradeNotified) {
1480
- imageDowngradeNotified = true;
1481
- emitLoopEvent(input, {
1482
- kind: "image-preprocessed",
1483
- atMs: Date.now(),
1484
- imageCount: imageUrls.length
1485
- });
1486
- }
1487
- } else {
1488
- baseRequestMessages = filterImagePartsFromMessages(
1489
- preparedMessages.messages,
1490
- supportsImages
1491
- );
1492
- if (!imageDowngradeNotified) {
1493
- imageDowngradeNotified = true;
1494
- emitLoopEvent(input, {
1495
- kind: "image-downgraded",
1496
- reason: "no-vision",
1497
- atMs: Date.now()
1498
- });
1499
- }
1500
- }
1501
- } else {
1502
- baseRequestMessages = filterImagePartsFromMessages(
1503
- preparedMessages.messages,
1504
- supportsImages
1505
- );
1506
- }
1507
- const requestMessages = emptyAssistantRepairPending ? [...baseRequestMessages, { role: "user", content: EMPTY_ASSISTANT_REPAIR_PROMPT }] : baseRequestMessages;
1508
- const contextMetrics = {
1509
- ...preparedMessages.metrics,
1510
- messageCount: requestMessages.length,
1511
- contentChars: requestMessages.reduce(
1512
- (total, message) => total + contentCharCount(message.content),
1513
- 0
1514
- ),
1515
- stableContextChars: builtMessages.stableContextChars,
1516
- dynamicContextChars: builtMessages.dynamicContextChars
1517
- };
1518
- emptyAssistantRepairPending = false;
1519
- result = await runCompleteWithTimeout({
1520
- provider,
1521
- messages: requestMessages,
1522
- options: {
1523
- ...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
1524
- ...input.onTextDelta ? { onTextDelta: (chunk) => {
1525
- partialContent += chunk;
1526
- input.onTextDelta(chunk);
1527
- } } : {},
1528
- ...input.onReasoningDelta ? { onReasoningDelta: input.onReasoningDelta } : {},
1529
- ...input.onToolEvent ? { onToolEvent: input.onToolEvent } : {},
1530
- ...input.onToolEvent ? { toolEventRound: round } : {}
1531
- },
1532
- timeoutMs: resolveLlmRequestTimeoutMs(input),
1533
- round,
1534
- input,
1535
- context: contextMetrics
1536
- });
1537
- turnUsage = mergeTurnUsage(turnUsage, result.usage);
1538
- contextUsage = result.usage;
1539
- if (result.usage && Object.keys(result.usage).length > 0) {
1540
- usageRecords.push({
1541
- callId: typeof result.usage.provider_call_id === "string" && result.usage.provider_call_id.trim() ? result.usage.provider_call_id.trim() : crypto.randomUUID(),
1542
- usage: result.usage,
1543
- model: result.model || agentConfig.model || "unknown",
1544
- ...result.provider || agentConfig.provider ? { provider: result.provider || agentConfig.provider } : {}
1545
- });
1546
- }
1547
- const toolCalls = result.tool_calls ?? [];
1548
- const rawToolCallsCount = (result.tool_calls?.length ?? 0) || (Array.isArray(result.raw_tool_calls) ? result.raw_tool_calls.length : 0);
1549
- if (toolCalls.length === 0 && rawToolCallsCount === 0) {
1550
- const outcome = resolveEmptyAssistantOutcome({
1551
- hasToolCalls: rawToolCallsCount > 0,
1552
- hasVisibleOutput: hasAssistantVisibleOutput(result.content),
1553
- repairUsed: emptyAssistantRepairUsed,
1554
- finishReason: result.finish_reason,
1555
- streamComplete: result.stream_complete
1556
- });
1557
- if (outcome.kind === "repair") {
1558
- emptyAssistantRepairPending = true;
1559
- emptyAssistantRepairUsed = true;
1560
- continue;
1561
- }
1562
- if (outcome.kind === "fallback") {
1563
- result = {
1564
- ...result,
1565
- content: resolveEmptyAssistantFallbackMessage(outcome.reason)
1566
- };
1567
- break;
1568
- }
1569
- break;
1570
- }
1571
- const outputBlocks = result.output ?? [];
1572
- if (outputBlocks.length > 0) {
1573
- let hasInlineExecutedTools = false;
1574
- for (const block of outputBlocks) {
1575
- if (block.type !== "toolCall") continue;
1576
- toolCallCount += 1;
1577
- const toolName = block.toolCall.function.name;
1578
- if (block.result) {
1579
- hasInlineExecutedTools = true;
1580
- } else {
1581
- throw new Error(
1582
- `provider returned output block with unexecuted toolCall "${toolName}" (id=${block.toolCall.id}); no provider currently emits this shape. Either the provider must fill block.result (like Cursor's exec channel) or it must not set result.output at all.`
1583
- );
1584
- }
1585
- }
1586
- if (hasInlineExecutedTools) {
1587
- messages.push(...blocksToOpenAiMessages(outputBlocks));
1588
- skipFinalAppend = true;
1589
- break;
1590
- }
1591
- round += 1;
1592
- continue;
1593
- }
1594
- toolCallCount += toolCalls.length;
1595
- messages.push({
1596
- role: "assistant",
1597
- content: result.content || null,
1598
- ...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {},
1599
- tool_calls: toolCalls
1600
- });
1601
- for (const toolCall of toolCalls) {
1602
- throwIfAborted(input);
1603
- const toolName = toolCall.function.name;
1604
- let toolResult;
1605
- const startedAt = Date.now();
1606
- emitLoopEvent(input, { kind: "tool-start", name: toolName, atMs: Date.now() });
1607
- emitToolEvent(input, {
1608
- type: "tool-call",
1609
- round,
1610
- toolCallId: toolCall.id,
1611
- toolName,
1612
- argumentsPreview: summarizeToolArguments(toolName, toolCall.function.arguments)
1613
- });
1614
- try {
1615
- const executePromise = input.adapter.executeTool({
1616
- id: toolCall.id,
1617
- name: toolName,
1618
- arguments: toolCall.function.arguments,
1619
- ...userInputText ? { userInput: userInputText } : {},
1620
- ...input.runtimeContext ? { runtimeContext: input.runtimeContext } : {},
1621
- ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
1622
- }, {
1623
- ...input.abortSignal ? { abortSignal: input.abortSignal } : {},
1624
- ...input.runtimeContext ? { runtimeContext: input.runtimeContext } : {}
1625
- });
1626
- toolResult = await raceWithAbort(input, executePromise, toolName);
1627
- const actionGate = buildActionGate({
1628
- toolName,
1629
- toolCallId: toolCall.id,
1630
- metadata: toolResult.metadata
1631
- });
1632
- if (actionGate && input.onActionGate) {
1633
- const replacement = await input.onActionGate(actionGate);
1634
- if (replacement) {
1635
- toolResult = replacement;
1636
- }
1637
- }
1638
- emitLoopEvent(input, { kind: "tool-end", name: toolName, atMs: Date.now(), ok: true });
1639
- emitToolEvent(input, {
1640
- type: "tool-result",
1641
- round,
1642
- toolCallId: toolCall.id,
1643
- toolName,
1644
- elapsedMs: Math.max(0, Date.now() - startedAt),
1645
- summary: summarizeToolResult(toolResult.content, toolResult.metadata),
1646
- ...typeof toolResult.content === "string" ? { content: toolResult.content } : {},
1647
- metadata: toolResult.metadata
1648
- });
1649
- } catch (error) {
1650
- emitLoopEvent(input, { kind: "tool-end", name: toolName, atMs: Date.now(), ok: false });
1651
- if (error && typeof error === "object" && error.code === LOCAL_TURN_ABORTED_CODE) {
1652
- throw error;
1653
- }
1654
- if (!shouldReturnToolExecutionErrors(input.adapter)) throw error;
1655
- emitToolEvent(input, {
1656
- type: "tool-error",
1657
- round,
1658
- toolCallId: toolCall.id,
1659
- toolName,
1660
- elapsedMs: Math.max(0, Date.now() - startedAt),
1661
- message: toErrorMessage(error)
1662
- });
1663
- toolResult = {
1664
- content: formatStructuredToolExecutionError({ toolName, error }) ?? formatToolExecutionError({ toolName, error }),
1665
- metadata: {
1666
- error: true,
1667
- toolName,
1668
- message: toErrorMessage(error),
1669
- ...error && typeof error === "object" && typeof error.code === "string" ? { code: error.code } : {}
1670
- }
1671
- };
1672
- }
1673
- messages.push({
1674
- role: "tool",
1675
- content: formatToolMessageContent({
1676
- toolName,
1677
- content: toolResult.content,
1678
- metadata: toolResult.metadata
1679
- }),
1680
- tool_call_id: toolCall.id,
1681
- toolName,
1682
- ...toolResult.metadata ? { tool_result_metadata: toolResult.metadata } : {}
1683
- });
1684
- }
1685
- round += 1;
1686
- }
1687
- } catch (error) {
1688
- loopError = error;
1689
- }
1690
- if (loopError) {
1691
- const turnMessages2 = applyPersistedTurnInput(
1692
- messages.slice(turnStartIndex),
1693
- input.persistedInput,
1694
- input.persistedInputReference
1695
- );
1696
- const dialogId = await persistFailedLocalTurn({
1697
- adapter: input.adapter,
1698
- agentKey: agentConfig.key,
1699
- messages: turnMessages2,
1700
- error: loopError,
1701
- model: agentConfig.model,
1702
- toolCallCount,
1703
- partialContent,
1704
- usage: contextUsage,
1705
- accountingUsage: addOutOfBandUsage(turnUsage, compactionUsage),
1706
- usageRecords: [
1707
- ...compactionUsage ? [{
1708
- callId: typeof compactionUsage.provider_call_id === "string" && compactionUsage.provider_call_id.trim() ? compactionUsage.provider_call_id.trim() : crypto.randomUUID(),
1709
- usage: compactionUsage,
1710
- model: agentConfig.model || "unknown",
1711
- ...agentConfig.provider ? { provider: agentConfig.provider } : {}
1712
- }] : [],
1713
- ...usageRecords
1714
- ],
1715
- billingConfig,
1716
- input
1717
- });
1718
- attachDialogIdToError(loopError, dialogId);
1719
- throw loopError;
1720
- }
1721
- result = result;
1722
- if (!skipFinalAppend) {
1723
- messages.push({
1724
- role: "assistant",
1725
- content: result.content,
1726
- // 与中间轮(:561)一致带上 reasoning_content,让 saveTurn 持久化思维链,
1727
- // 空轮/异常排查时能回看模型实际想了什么。
1728
- ...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {}
1729
- });
1730
- }
1731
- const turnMessages = applyPersistedTurnInput(
1732
- messages.slice(turnStartIndex),
1733
- input.persistedInput,
1734
- input.persistedInputReference
1735
- );
1736
- if (compactionUsage && Object.keys(compactionUsage).length > 0) {
1737
- usageRecords.unshift({
1738
- callId: typeof compactionUsage.provider_call_id === "string" && compactionUsage.provider_call_id.trim() ? compactionUsage.provider_call_id.trim() : crypto.randomUUID(),
1739
- usage: compactionUsage,
1740
- model: agentConfig.model || result.model || "unknown",
1741
- ...agentConfig.provider ? { provider: agentConfig.provider } : {}
1742
- });
1743
- }
1744
- const accountingUsage = addOutOfBandUsage(turnUsage, compactionUsage);
1745
- const saved = await input.adapter.saveTurn({
1746
- agentKey: agentConfig.key,
1747
- messages: turnMessages,
1748
- result: {
1749
- ...result,
1750
- ...toolCallCount > 0 ? { toolCallCount } : {},
1751
- ...agentConfig.toolSurface ? { runtimeToolSurface: agentConfig.toolSurface } : {}
1752
- },
1753
- ...usageRecords.length > 0 ? { usageRecords } : {},
1754
- ...accountingUsage ? { accountingUsage } : {},
1755
- billingConfig,
1756
- ...input.runtimeContext ? { runtimeContext: input.runtimeContext } : {},
1757
- ...input.continueDialogId ? { continueDialogId: input.continueDialogId } : {},
1758
- ...input.spaceId ? { spaceId: input.spaceId } : {},
1759
- ...input.category ? { category: input.category } : {},
1760
- ...input.inheritedFromDialogKey ? { inheritedFromDialogKey: input.inheritedFromDialogKey } : {},
1761
- ...input.parentDialogId ? { parentDialogId: input.parentDialogId } : {}
1762
- });
1763
- return {
1764
- ...result,
1765
- ...usageRecords.length > 0 ? { usageRecords } : {},
1766
- ...accountingUsage ? { accountingUsage } : {},
1767
- ...toolCallCount > 0 ? { toolCallCount } : {},
1768
- ...agentConfig.toolSurface ? { runtimeToolSurface: agentConfig.toolSurface } : {},
1769
- // 透出最后一轮 provider 调用的 finish_reason;多轮工具循环里只有最后一轮收尾状态有意义。
1770
- ...result.finish_reason ? { finish_reason: result.finish_reason } : {},
1771
- dialogId: saved.dialogId,
1772
- title: saved.title,
1773
- turnMessages
1774
- };
1775
- }
1776
-
1777
- export {
1778
- readCommandActionGatePayload,
1779
- readActionGate,
1780
- parseToolArgumentsJson,
1781
- summarizeToolArguments,
1782
- resolveAgentImageInputSupport,
1783
- preprocessImagesForTextOnlyAgent,
1784
- describeImageViaServerProxy,
1785
- resolveHistoricalToolContentCap,
1786
- projectToolMessageContent,
1787
- LOCAL_AGENT_CONFIG_MISSING_CODE,
1788
- EMPTY_ASSISTANT_REPAIR_PROMPT,
1789
- LOCAL_TURN_ABORTED_CODE,
1790
- runLocalAgentTurn
1791
- };