nolo-cli 0.11.0 → 0.12.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{_executeModel-ZEDPX6OF.js → _executeModel-4F7VZR5U.js} +52 -50
- package/{action-AA4IJ5K2.js → action-OTOWUMPW.js} +8 -9
- package/{addContentAction-QYVO4SYP.js → addContentAction-JGBUVR7K.js} +7 -9
- package/{addDialogAgentAction-WVVKT5WQ.js → addDialogAgentAction-V6CL2UCF.js} +9 -10
- package/{agentDeleteCommand-RDPGYIWF.js → agentDeleteCommand-MMRSG6AB.js} +39 -28
- package/{agentEmailCommands-2HJJKS5F.js → agentEmailCommands-YFVKOGM7.js} +65 -62
- package/{agentListCommands-HFEVV3ZW.js → agentListCommands-2PZHIQ3I.js} +128 -31
- package/{agentMachineCommands-25ZOENJU.js → agentMachineCommands-55UUZNHF.js} +38 -26
- package/{agentPullCommand-GOL2S4W2.js → agentPullCommand-KRBRIFIY.js} +7 -7
- package/{agentRecordCommands-GTCOAM3L.js → agentRecordCommands-BTP3OAPL.js} +57 -54
- package/{agentRunCommand-YBS3JDLD.js → agentRunCommand-AA4QLRQZ.js} +60 -59
- package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
- package/{agentRuntimeLocal-UQBMQI2M.js → agentRuntimeLocal-QDJCBXM2.js} +31 -15
- package/{agentSlice-6EKOBCCS.js → agentSlice-6JAJOU3X.js} +8 -9
- package/{authCommand-O2OWACN4.js → authCommand-DMW336GT.js} +15 -15
- package/{authCommands-H7365TZP.js → authCommands-GH5T6RYV.js} +6 -6
- package/{chunk-E4HNJSHC.js → chunk-2BVGERJ2.js} +81 -19
- package/{chunk-LP65VRCI.js → chunk-3MI5LDDU.js} +6 -6
- package/{chunk-OPZYGHUC.js → chunk-3WGLLTK4.js} +59 -1
- package/{chunk-KJR5KAUQ.js → chunk-3XF6LCCN.js} +20 -7
- package/{chunk-PXZAAB4L.js → chunk-4LG74DRG.js} +4 -4
- package/{chunk-2FTTDCD4.js → chunk-4ON5AV72.js} +2 -1
- package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
- package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
- package/{chunk-ZPRK73XY.js → chunk-5S4SWV5G.js} +51 -38
- package/{chunk-EVZKZF32.js → chunk-5V524MHX.js} +195 -78
- package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
- package/{chunk-NEM67FX7.js → chunk-ARSC56DC.js} +1 -1
- package/{chunk-AFON4QUU.js → chunk-AS3ZQ3O2.js} +19 -28
- package/{chunk-4SOKXIW4.js → chunk-B4KT7RN2.js} +10 -13
- package/chunk-BYXBJQAS.js +0 -0
- package/{chunk-PXQN7IBQ.js → chunk-C3BFP57W.js} +95 -2
- package/{chunk-AEZAORLG.js → chunk-CGDQDXKZ.js} +5 -5
- package/{chunk-MQI77IRB.js → chunk-DPFWKRHG.js} +2 -2
- package/{chunk-TACHTH5D.js → chunk-EIMX5ER5.js} +18 -18
- package/{chunk-5MOUAAMK.js → chunk-FSUZIGIG.js} +197 -2578
- package/{chunk-FXXSGWAJ.js → chunk-H5Y2H2N6.js} +1 -1
- package/chunk-IKXL2HZ3.js +70 -0
- package/{chunk-QNMFBBZK.js → chunk-IVAOVLM7.js} +1 -1
- package/chunk-JFANOO7I.js +2274 -0
- package/{chunk-YPRZSPF3.js → chunk-JKWWVF2I.js} +1 -1
- package/{chunk-3NSIQZZ6.js → chunk-JU7J7EAV.js} +45 -45
- package/{chunk-X2S7JTIG.js → chunk-KWBH4RAL.js} +1280 -137
- package/{chunk-OUI3ZHYD.js → chunk-LUKQQJFL.js} +39 -2
- package/{chunk-BX6U3ETE.js → chunk-MG6BVV3T.js} +320 -102
- package/{chunk-L53FGHA4.js → chunk-MQRZ5QUH.js} +1 -1
- package/{chunk-2W2VYVI5.js → chunk-MZHSAPZP.js} +675 -177
- package/{chunk-YOCHZD7D.js → chunk-NCGFB7NN.js} +5 -3
- package/{chunk-DWGAEFJL.js → chunk-O4FOPRCP.js} +1 -1
- package/chunk-OGGHETUE.js +4900 -0
- package/{chunk-PNEW2Z3T.js → chunk-OHEKYYJF.js} +7 -7
- package/chunk-OOIN7HSO.js +146 -0
- package/{chunk-SV5MWASH.js → chunk-OPM2DE2J.js} +12 -12
- package/{chunk-5OIFWVX7.js → chunk-QJSTNKJY.js} +1 -1
- package/{chunk-C4FTUXVF.js → chunk-QLUFR47U.js} +11 -1
- package/{chunk-LAX2JSTX.js → chunk-QUHUVG6Y.js} +20 -2
- package/{chunk-D6OQHXVG.js → chunk-RQSOAMPJ.js} +22 -6
- package/{chunk-ZEYDOXHF.js → chunk-RUGEW4P7.js} +1515 -884
- package/{chunk-GBDSZATH.js → chunk-RWO5DGXI.js} +3 -3
- package/{chunk-S3GS2PRL.js → chunk-TA7VKOHU.js} +613 -70
- package/{chunk-XYZA7S7V.js → chunk-TQYYGE6G.js} +6 -6
- package/{chunk-YTBMA7NO.js → chunk-TYPHUVK5.js} +70 -8
- package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
- package/{chunk-C2MDQQ4O.js → chunk-VBSWHE5W.js} +1 -1
- package/{chunk-O26GI2JP.js → chunk-VKG4HXYV.js} +39 -8
- package/{chunk-IPYUUY4E.js → chunk-WPHL6ALY.js} +1 -1
- package/{chunk-7D3JY6C2.js → chunk-WVTBAALN.js} +64 -5
- package/{chunk-XFOLQLDN.js → chunk-XHQWJUW4.js} +2 -2
- package/{chunk-Q3UKJVU4.js → chunk-Y5P3ZRGC.js} +10 -10
- package/{chunk-KQQVGRA3.js → chunk-YUMYYXPI.js} +53 -183
- package/cliEnvHelpers-VYC2OPTB.js +30 -0
- package/{createAgentAutomationAction-LTI5QU35.js → createAgentAutomationAction-4NY4JLOK.js} +26 -26
- package/{createDialogAction-OJJ7L3L3.js → createDialogAction-I5WUWFJ6.js} +26 -26
- package/{createPageAction-5MXWVRGY.js → createPageAction-HFTIQ7TW.js} +26 -26
- package/{dbSlice-RQFQQVHT.js → dbSlice-J4PUZ6Y5.js} +6 -7
- package/{deleteFile-7XP5BGC7.js → deleteFile-M7UNIUOO.js} +4 -4
- package/{dialogCommands-JTJ3RPPW.js → dialogCommands-SVXRJFZS.js} +34 -23
- package/{dialogSlice-OWROKURJ.js → dialogSlice-35LA3ZRG.js} +26 -26
- package/{docCreateCommands-WJBEW2V7.js → docCreateCommands-Z7RIAYKQ.js} +34 -23
- package/{docDeleteCommands-BVEHJOY5.js → docDeleteCommands-NEYHUVZF.js} +35 -25
- package/{docReadCommands-NEWOZIJV.js → docReadCommands-I4US2SDE.js} +34 -23
- package/{docStore-3DXRYBMD.js → docStore-3CP2IO6M.js} +25 -25
- package/{docUpdateCommands-C7WHT7UU.js → docUpdateCommands-UU5HQBRP.js} +34 -23
- package/fetchMessages-KTNM7QAR.js +1 -1
- package/{getPricing-3E6UNPKL.js → getPricing-UGDJHI7M.js} +7 -3
- package/{handleSendMessageAction-B5PRR7BE.js → handleSendMessageAction-FWTLQGOZ.js} +30 -30
- package/index.js +77 -76
- package/{localDialogRead-YSKFFRTT.js → localDialogRead-2VRI24F4.js} +1 -1
- package/{machineCommands-IIIREAEK.js → machineCommands-STHZKBDJ.js} +64 -61
- package/{machineWsRunDispatch-3SIU6ILW.js → machineWsRunDispatch-7YHT4TPT.js} +56 -53
- package/{memoryCommands-O4MIPIWQ.js → memoryCommands-AFMES3LN.js} +5 -5
- package/{models-4OPOOLFS.js → models-LSCRJJ6P.js} +2 -2
- package/package.json +1 -1
- package/{read-QJNKRROM.js → read-2MIMRBZ2.js} +4 -4
- package/{readDocTool-H6X6KO6X.js → readDocTool-QHVGQHUJ.js} +1 -1
- package/{readlineWorkspace-HGP3I3OC.js → readlineWorkspace-OMRWYMB6.js} +1279 -716
- package/{removeDialogAgentAction-TCT35SOF.js → removeDialogAgentAction-XUZYQBV4.js} +13 -13
- package/{runAgentClientLoop-EFM4PTVI.js → runAgentClientLoop-FP2K33ZO.js} +47 -46
- package/{runtimeDoctorCommands-ID43Z7VX.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
- package/{setDialogExtraReferencesAction-CGYLZMFH.js → setDialogExtraReferencesAction-PTAIFKGM.js} +9 -10
- package/{setPrimaryDialogAgentAction-F6BFQDJU.js → setPrimaryDialogAgentAction-HRFTDB44.js} +13 -13
- package/{settingSlice-2I5HMG47.js → settingSlice-VGRJUSE5.js} +7 -8
- package/{skillDiscovery-UGSQGX2U.js → skillDiscovery-6M2BZUFN.js} +3 -3
- package/{spaceCommands-MT7BWCIL.js → spaceCommands-GN73BCOB.js} +38 -29
- package/{spaceSlice-BS5TSW3W.js → spaceSlice-WTFNKOVA.js} +30 -26
- package/{streamAgentChatTurn-I3VBFNR3.js → streamAgentChatTurn-HBAQQVNK.js} +442 -130
- package/{toolApiClient-C7P7YSE4.js → toolApiClient-QI3YBNWB.js} +2 -2
- package/{tools-XSD2BOEN.js → tools-TPQYTLQZ.js} +34 -33
- package/{tuiSummaryLlmCaller-32NRHTT2.js → tuiSummaryLlmCaller-XDC7WG4K.js} +11 -7
- package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
- package/{updateDialogTitleAction-7FYNJOGP.js → updateDialogTitleAction-AWXNPUQJ.js} +30 -29
- package/chunk-AZPCASRE.js +0 -36
- package/chunk-BBYVOSU2.js +0 -792
- package/chunk-DCOA4QSN.js +0 -105
- package/chunk-UAD2WT54.js +0 -64
- package/{chunk-JK7VW2FN.js → chunk-5MIDPSSK.js} +3 -3
- package/{chunk-5CS33IBL.js → chunk-PDL2AV3E.js} +3 -3
- package/{chunk-5MMZUWSG.js → chunk-TOILAPLH.js} +5 -5
- package/{chunk-OOM6SJNR.js → chunk-TSUJOM6N.js} +6 -6
- package/{db-6QED3ZVO.js → db-HCJ5PRB5.js} +3 -3
- package/{offlineMarxistsAgentCommand-BCPJITJL.js → offlineMarxistsAgentCommand-5OOWZLJI.js} +3 -3
- package/{tableCommands-M3DJUETS.js → tableCommands-E4SQWDOX.js} +6 -6
- package/{understanding-ZOESBLF3.js → understanding-3CUQQ6SV.js} +3 -3
|
@@ -2,29 +2,36 @@ import {
|
|
|
2
2
|
buildCurrentTimeBlock,
|
|
3
3
|
buildIdentityBlock,
|
|
4
4
|
buildRuntimeGuidanceBlocks
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LUKQQJFL.js";
|
|
6
6
|
import {
|
|
7
7
|
canonicalizeToolNames
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-B4KT7RN2.js";
|
|
9
|
+
import {
|
|
10
|
+
estimateTokenCount,
|
|
11
|
+
getModelContextWindow,
|
|
12
|
+
planCompression,
|
|
13
|
+
planContextUsage,
|
|
14
|
+
serializeMessageContent
|
|
15
|
+
} from "./chunk-JFANOO7I.js";
|
|
9
16
|
import {
|
|
10
17
|
clipCompactText
|
|
11
18
|
} from "./chunk-5MMAK75Q.js";
|
|
12
19
|
import {
|
|
13
20
|
findModelConfig,
|
|
14
21
|
getProviderByModelName
|
|
15
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-OGGHETUE.js";
|
|
23
|
+
import {
|
|
24
|
+
buildDialogSummaryLayer
|
|
25
|
+
} from "./chunk-QLUFR47U.js";
|
|
26
|
+
import {
|
|
27
|
+
toErrorMessage
|
|
28
|
+
} from "./chunk-HNMXYYJR.js";
|
|
16
29
|
import {
|
|
17
30
|
asRecordOrEmpty
|
|
18
31
|
} from "./chunk-KX6YBIHG.js";
|
|
19
32
|
import {
|
|
20
33
|
asTrimmedLowercaseString
|
|
21
34
|
} from "./chunk-QSJ3HFEF.js";
|
|
22
|
-
import {
|
|
23
|
-
compactWhitespace
|
|
24
|
-
} from "./chunk-DWLEOOGR.js";
|
|
25
|
-
import {
|
|
26
|
-
toErrorMessage
|
|
27
|
-
} from "./chunk-HNMXYYJR.js";
|
|
28
35
|
import {
|
|
29
36
|
isRecord
|
|
30
37
|
} from "./chunk-KUG52IAP.js";
|
|
@@ -52,14 +59,27 @@ function readActionGate(value) {
|
|
|
52
59
|
if (typeof value.id !== "string" || !value.id.trim()) return null;
|
|
53
60
|
if (value.kind !== "confirm" && value.kind !== "handoff" && value.kind !== "input") return null;
|
|
54
61
|
if (typeof value.title !== "string" || !value.title.trim()) return null;
|
|
62
|
+
const titleParams = readTitleParams(value.titleParams);
|
|
55
63
|
return {
|
|
56
64
|
id: value.id.trim(),
|
|
57
65
|
kind: value.kind,
|
|
58
66
|
title: value.title.trim(),
|
|
67
|
+
...titleParams ? { titleParams } : {},
|
|
59
68
|
...asOptionalTrimmedString(value.body) ? { body: asOptionalTrimmedString(value.body) } : {},
|
|
60
69
|
...Object.prototype.hasOwnProperty.call(value, "payload") ? { payload: value.payload } : {}
|
|
61
70
|
};
|
|
62
71
|
}
|
|
72
|
+
function readTitleParams(value) {
|
|
73
|
+
if (!isRecord(value)) return void 0;
|
|
74
|
+
const entries = Object.entries(value).flatMap(([key, raw]) => {
|
|
75
|
+
if (typeof raw === "string" || typeof raw === "number") {
|
|
76
|
+
return [[key, raw]];
|
|
77
|
+
}
|
|
78
|
+
return [];
|
|
79
|
+
});
|
|
80
|
+
if (entries.length === 0) return void 0;
|
|
81
|
+
return Object.fromEntries(entries);
|
|
82
|
+
}
|
|
63
83
|
|
|
64
84
|
// packages/ai/llm/agentCapabilities.ts
|
|
65
85
|
var isCustomAgent = (agent) => {
|
|
@@ -81,11 +101,20 @@ function stripEffortSuffix(model) {
|
|
|
81
101
|
}
|
|
82
102
|
return model;
|
|
83
103
|
}
|
|
104
|
+
function stripPreviewSuffix(model) {
|
|
105
|
+
const suffix = "-preview";
|
|
106
|
+
if (model.endsWith(suffix) && model.length > suffix.length) {
|
|
107
|
+
return model.slice(0, -suffix.length);
|
|
108
|
+
}
|
|
109
|
+
return model;
|
|
110
|
+
}
|
|
84
111
|
var lookupKnownModelVision = (provider, model) => {
|
|
85
112
|
if (!model) return void 0;
|
|
86
113
|
const candidates = [model];
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
114
|
+
const strippedEffort = stripEffortSuffix(model);
|
|
115
|
+
if (strippedEffort !== model) candidates.push(strippedEffort);
|
|
116
|
+
const strippedPreview = stripPreviewSuffix(model);
|
|
117
|
+
if (strippedPreview !== model) candidates.push(strippedPreview);
|
|
89
118
|
for (const candidate of candidates) {
|
|
90
119
|
if (provider) {
|
|
91
120
|
const direct = findModelConfig(provider, candidate);
|
|
@@ -273,6 +302,227 @@ function summarizeToolArguments(toolName, rawArgs) {
|
|
|
273
302
|
);
|
|
274
303
|
}
|
|
275
304
|
|
|
305
|
+
// packages/agent-runtime/contextBlockScope.ts
|
|
306
|
+
var isContextBlockScope = (value) => {
|
|
307
|
+
if (typeof value !== "object" || value === null) return false;
|
|
308
|
+
const candidate = value;
|
|
309
|
+
return typeof candidate.content === "string" && (candidate.cacheScope === "session" || candidate.cacheScope === "turn");
|
|
310
|
+
};
|
|
311
|
+
var normalizeContextBlockScopes = (legacyBlocks = void 0, scopes = void 0) => {
|
|
312
|
+
const trimmedScopes = (Array.isArray(scopes) ? scopes : []).filter(isContextBlockScope).map((scope) => ({ content: scope.content.trim(), cacheScope: scope.cacheScope })).filter((scope) => scope.content.length > 0);
|
|
313
|
+
if (trimmedScopes.length > 0) {
|
|
314
|
+
return trimmedScopes;
|
|
315
|
+
}
|
|
316
|
+
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" }));
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// packages/ai/agent/toolOutputPolicy.ts
|
|
320
|
+
var MAX_HISTORICAL_TOOL_CONTENT_CHARS = 1600;
|
|
321
|
+
var MAX_IN_TURN_TOOL_CONTENT_CHARS = 4e3;
|
|
322
|
+
var FRESH_TOOL_OUTPUT_MAX_CHARS = 32e3;
|
|
323
|
+
var DEFAULT_TOOL_OUTPUT_PROFILE = {
|
|
324
|
+
maxChars: MAX_IN_TURN_TOOL_CONTENT_CHARS,
|
|
325
|
+
headRatio: 0.5
|
|
326
|
+
};
|
|
327
|
+
var TOOL_OUTPUT_PROFILES = {
|
|
328
|
+
readFile: { maxChars: 4800, headRatio: 0.68 },
|
|
329
|
+
read_file: { maxChars: 4800, headRatio: 0.68 },
|
|
330
|
+
readWorkspaceFile: { maxChars: 4800, headRatio: 0.68 },
|
|
331
|
+
searchFiles: { maxChars: 3600, headRatio: 0.78 },
|
|
332
|
+
search_files: { maxChars: 3600, headRatio: 0.78 },
|
|
333
|
+
listFiles: { maxChars: 2800, headRatio: 0.85 },
|
|
334
|
+
globFiles: { maxChars: 2800, headRatio: 0.85 },
|
|
335
|
+
execShell: { maxChars: 4e3, headRatio: 0.35 },
|
|
336
|
+
runCommand: { maxChars: 4e3, headRatio: 0.35 },
|
|
337
|
+
launchProcess: { maxChars: 2800, headRatio: 0.35 },
|
|
338
|
+
editFile: { maxChars: 2800, headRatio: 0.62 },
|
|
339
|
+
writeFile: { maxChars: 2400, headRatio: 0.62 },
|
|
340
|
+
readPastedText: { maxChars: 4800, headRatio: 0.5 }
|
|
341
|
+
};
|
|
342
|
+
function resolveToolOutputProfile(toolName) {
|
|
343
|
+
return (toolName ? TOOL_OUTPUT_PROFILES[toolName] : void 0) ?? DEFAULT_TOOL_OUTPUT_PROFILE;
|
|
344
|
+
}
|
|
345
|
+
function projectToolMessageContent(input) {
|
|
346
|
+
const { content, isFresh, toolName, historicalMaxChars } = input;
|
|
347
|
+
if (isFresh) {
|
|
348
|
+
if (content.length <= FRESH_TOOL_OUTPUT_MAX_CHARS) return content;
|
|
349
|
+
return clipToolText(
|
|
350
|
+
content,
|
|
351
|
+
FRESH_TOOL_OUTPUT_MAX_CHARS,
|
|
352
|
+
resolveToolOutputProfile(toolName).headRatio,
|
|
353
|
+
`
|
|
354
|
+
\u2026[\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${content.length} \u5B57\u7B26]`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
if (content.length <= historicalMaxChars) return content;
|
|
358
|
+
return content.slice(0, historicalMaxChars) + `
|
|
359
|
+
\u2026[\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${content.length} \u5B57\u7B26]`;
|
|
360
|
+
}
|
|
361
|
+
function clipToolText(content, maxChars, headRatio, marker = "\n\n[... tool output middle omitted; head/tail preserved ...]\n\n") {
|
|
362
|
+
const normalized = content.replace(/\r\n/g, "\n").trim();
|
|
363
|
+
if (normalized.length <= maxChars) return normalized;
|
|
364
|
+
if (maxChars <= marker.length + 2) return normalized.slice(0, maxChars);
|
|
365
|
+
const available = maxChars - marker.length;
|
|
366
|
+
const headChars = Math.max(1, Math.floor(available * headRatio));
|
|
367
|
+
const tailChars = Math.max(1, available - headChars);
|
|
368
|
+
return `${normalized.slice(0, headChars)}${marker}${normalized.slice(-tailChars)}`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// packages/agent-runtime/localAutoCompaction.ts
|
|
372
|
+
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
|
|
373
|
+
|
|
374
|
+
\u4E25\u683C\u53EA\u8F93\u51FA\u4E0B\u9762\u4E24\u90E8\u5206\uFF0C\u6807\u9898\u5FC5\u987B\u5B8C\u5168\u4E00\u81F4\uFF1A
|
|
375
|
+
\u5173\u952E\u4E8B\u5B9E\u6863\u6848
|
|
376
|
+
- ...
|
|
377
|
+
\u5BF9\u8BDD\u8FDB\u5C55\u4E0E\u5F85\u529E
|
|
378
|
+
- ...
|
|
379
|
+
|
|
380
|
+
\u8981\u6C42\uFF1A
|
|
381
|
+
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
|
|
382
|
+
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
|
|
383
|
+
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
|
|
384
|
+
4) \u5FFD\u7565\u5BD2\u6684\u3001\u91CD\u590D\u5C1D\u8BD5\u3001\u5DF2\u653E\u5F03\u65B9\u6848\u548C\u65E0\u4EF7\u503C\u5E9F\u8BDD\u3002
|
|
385
|
+
5) \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`;
|
|
386
|
+
function buildLocalAutoCompactionUserContent(previousSummary, messagesText) {
|
|
387
|
+
return `\u3010\u73B0\u6709\u8BB0\u5FC6\u3011\uFF1A
|
|
388
|
+
${previousSummary || "(\u65E0)"}
|
|
389
|
+
|
|
390
|
+
\u3010\u65B0\u589E\u5BF9\u8BDD\u3011\uFF1A
|
|
391
|
+
${messagesText}`.trim();
|
|
392
|
+
}
|
|
393
|
+
function toPlanCompressionMessages(history) {
|
|
394
|
+
return history.map((message, index) => {
|
|
395
|
+
const usage = message.usage;
|
|
396
|
+
return {
|
|
397
|
+
id: `local-${index}`,
|
|
398
|
+
role: message.role,
|
|
399
|
+
content: message.content,
|
|
400
|
+
...Array.isArray(message.tool_calls) ? { tool_calls: message.tool_calls } : {},
|
|
401
|
+
...usage?.completion_tokens != null ? { usage: { completion_tokens: usage.completion_tokens } } : {}
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
function formatMessagesForLocalSummary(msgs) {
|
|
406
|
+
return msgs.map((msg) => {
|
|
407
|
+
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]");
|
|
408
|
+
return `${msg.role}: ${content}`;
|
|
409
|
+
}).join("\n");
|
|
410
|
+
}
|
|
411
|
+
function buildLocalSummaryHistoryMessage(summary) {
|
|
412
|
+
const layer = buildDialogSummaryLayer({ summary });
|
|
413
|
+
return {
|
|
414
|
+
role: "user",
|
|
415
|
+
content: layer?.content ?? `--- \u5386\u53F2\u5BF9\u8BDD\u6458\u8981 ---
|
|
416
|
+
${summary.trim()}`
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function projectHistoryWithSummary(args) {
|
|
420
|
+
const bridged = toPlanCompressionMessages(args.history);
|
|
421
|
+
let startIndex = 0;
|
|
422
|
+
if (args.summarizedBeforeId) {
|
|
423
|
+
const found = bridged.findIndex((m) => m.id === args.summarizedBeforeId);
|
|
424
|
+
if (found !== -1) startIndex = found + 1;
|
|
425
|
+
}
|
|
426
|
+
return [
|
|
427
|
+
buildLocalSummaryHistoryMessage(args.summary),
|
|
428
|
+
...args.history.slice(startIndex)
|
|
429
|
+
];
|
|
430
|
+
}
|
|
431
|
+
var COLD_RESUME_IDLE_MS = 60 * 60 * 1e3;
|
|
432
|
+
function isColdResume(history, nowMs) {
|
|
433
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
434
|
+
const at = history[i]?.createdAt;
|
|
435
|
+
if (typeof at === "number" && Number.isFinite(at)) {
|
|
436
|
+
return nowMs - at > COLD_RESUME_IDLE_MS;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
async function maybeAutoCompactLocalHistory(args) {
|
|
442
|
+
const { adapter, dialogId, history } = args;
|
|
443
|
+
const unchanged = () => ({
|
|
444
|
+
history,
|
|
445
|
+
compressed: false,
|
|
446
|
+
summaryGenerated: false
|
|
447
|
+
});
|
|
448
|
+
if (!dialogId || history.length === 0 || typeof adapter.loadDialogSummary !== "function" || typeof adapter.saveDialogSummary !== "function") {
|
|
449
|
+
return unchanged();
|
|
450
|
+
}
|
|
451
|
+
let stored = null;
|
|
452
|
+
try {
|
|
453
|
+
stored = await adapter.loadDialogSummary(dialogId);
|
|
454
|
+
} catch (error) {
|
|
455
|
+
console.warn("[localLoop] loadDialogSummary failed:", error);
|
|
456
|
+
return unchanged();
|
|
457
|
+
}
|
|
458
|
+
const existingSummary = typeof stored?.summary === "string" ? stored.summary : "";
|
|
459
|
+
const summarizedBeforeId = typeof stored?.summarizedBeforeId === "string" ? stored.summarizedBeforeId : void 0;
|
|
460
|
+
const contextWindow = typeof args.contextWindow === "number" && Number.isFinite(args.contextWindow) && args.contextWindow > 0 ? args.contextWindow : getModelContextWindow(args.model ?? "");
|
|
461
|
+
const allMsgs = toPlanCompressionMessages(history);
|
|
462
|
+
const coldResume = isColdResume(history, args.now?.() ?? Date.now());
|
|
463
|
+
const plan = planCompression({
|
|
464
|
+
allMsgs,
|
|
465
|
+
summarizedBeforeId,
|
|
466
|
+
summary: existingSummary,
|
|
467
|
+
contextWindow,
|
|
468
|
+
...coldResume ? { force: true, reason: "context_budget" } : {}
|
|
469
|
+
});
|
|
470
|
+
const projectExisting = () => {
|
|
471
|
+
if (!existingSummary.trim()) return unchanged();
|
|
472
|
+
return {
|
|
473
|
+
history: projectHistoryWithSummary({
|
|
474
|
+
history,
|
|
475
|
+
summary: existingSummary,
|
|
476
|
+
summarizedBeforeId
|
|
477
|
+
}),
|
|
478
|
+
compressed: true,
|
|
479
|
+
summaryGenerated: false
|
|
480
|
+
};
|
|
481
|
+
};
|
|
482
|
+
if (!plan.shouldCompress) {
|
|
483
|
+
return projectExisting();
|
|
484
|
+
}
|
|
485
|
+
try {
|
|
486
|
+
const provider = await args.resolveProvider();
|
|
487
|
+
const messagesText = formatMessagesForLocalSummary(
|
|
488
|
+
plan.msgsToCompress
|
|
489
|
+
);
|
|
490
|
+
const promptContent = buildLocalAutoCompactionUserContent(
|
|
491
|
+
existingSummary,
|
|
492
|
+
messagesText
|
|
493
|
+
);
|
|
494
|
+
const result = await provider.complete([
|
|
495
|
+
{ role: "system", content: LOCAL_AUTO_COMPACTION_SYSTEM_PROMPT },
|
|
496
|
+
{ role: "user", content: promptContent }
|
|
497
|
+
]);
|
|
498
|
+
const newSummary = typeof result.content === "string" ? result.content.trim() : "";
|
|
499
|
+
if (!newSummary) {
|
|
500
|
+
console.warn(
|
|
501
|
+
"[localLoop] auto-compaction produced empty summary; keeping prior projection"
|
|
502
|
+
);
|
|
503
|
+
return projectExisting();
|
|
504
|
+
}
|
|
505
|
+
await adapter.saveDialogSummary({
|
|
506
|
+
dialogId,
|
|
507
|
+
summary: newSummary,
|
|
508
|
+
summarizedBeforeId: plan.newSummarizedBeforeId
|
|
509
|
+
});
|
|
510
|
+
return {
|
|
511
|
+
history: projectHistoryWithSummary({
|
|
512
|
+
history,
|
|
513
|
+
summary: newSummary,
|
|
514
|
+
summarizedBeforeId: plan.newSummarizedBeforeId
|
|
515
|
+
}),
|
|
516
|
+
compressed: true,
|
|
517
|
+
summaryGenerated: true,
|
|
518
|
+
...result.usage ? { usage: result.usage } : {}
|
|
519
|
+
};
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.warn("[localLoop] auto-compaction failed:", error);
|
|
522
|
+
return projectExisting();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
276
526
|
// packages/agent-runtime/localLoop.ts
|
|
277
527
|
var LOCAL_AGENT_CONFIG_MISSING_CODE = "LOCAL_AGENT_CONFIG_MISSING";
|
|
278
528
|
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";
|
|
@@ -283,7 +533,9 @@ function resolveEmptyAssistantOutcome(args) {
|
|
|
283
533
|
if (args.hasToolCalls || args.hasVisibleOutput) return { kind: "ok" };
|
|
284
534
|
if (args.finishReason === "length") return { kind: "fallback", reason: "length_truncated" };
|
|
285
535
|
if (!args.repairUsed) return { kind: "repair" };
|
|
286
|
-
if (!args.finishReason)
|
|
536
|
+
if (!args.finishReason && !args.streamComplete) {
|
|
537
|
+
return { kind: "fallback", reason: "stream_truncated" };
|
|
538
|
+
}
|
|
287
539
|
return { kind: "fallback", reason: "empty_completion" };
|
|
288
540
|
}
|
|
289
541
|
function resolveEmptyAssistantFallbackMessage(reason) {
|
|
@@ -341,14 +593,43 @@ function buildAbortedError() {
|
|
|
341
593
|
function throwIfAborted(input) {
|
|
342
594
|
if (input.abortSignal?.aborted) throw buildAbortedError();
|
|
343
595
|
}
|
|
596
|
+
async function raceWithAbort(input, promise, pendingToolName) {
|
|
597
|
+
const signal = input.abortSignal;
|
|
598
|
+
if (!signal) return promise;
|
|
599
|
+
let abortListener;
|
|
600
|
+
const abortPromise = new Promise((_resolve, reject) => {
|
|
601
|
+
abortListener = () => {
|
|
602
|
+
const error = buildAbortedError();
|
|
603
|
+
if (pendingToolName) error.pendingToolName = pendingToolName;
|
|
604
|
+
promise.catch(() => {
|
|
605
|
+
});
|
|
606
|
+
reject(error);
|
|
607
|
+
};
|
|
608
|
+
if (signal.aborted) {
|
|
609
|
+
abortListener();
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
613
|
+
});
|
|
614
|
+
try {
|
|
615
|
+
return await Promise.race([promise, abortPromise]);
|
|
616
|
+
} finally {
|
|
617
|
+
if (signal && abortListener) signal.removeEventListener("abort", abortListener);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
344
620
|
function resolveLlmRequestTimeoutMs(input) {
|
|
345
621
|
const raw = input.llmRequestTimeoutMs ?? input.timeoutMs;
|
|
346
622
|
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return void 0;
|
|
347
623
|
return raw;
|
|
348
624
|
}
|
|
349
625
|
async function runCompleteWithTimeout(args) {
|
|
350
|
-
const { provider, messages, options, timeoutMs, round, input } = args;
|
|
351
|
-
emitLoopEvent(input, {
|
|
626
|
+
const { provider, messages, options, timeoutMs, round, input, context } = args;
|
|
627
|
+
emitLoopEvent(input, {
|
|
628
|
+
kind: "llm-start",
|
|
629
|
+
round,
|
|
630
|
+
atMs: Date.now(),
|
|
631
|
+
...context ? { context } : {}
|
|
632
|
+
});
|
|
352
633
|
const complete = provider.complete(messages, options);
|
|
353
634
|
let ok = false;
|
|
354
635
|
const signal = input.abortSignal;
|
|
@@ -511,52 +792,218 @@ function buildActionGate(args) {
|
|
|
511
792
|
toolCallId: args.toolCallId
|
|
512
793
|
};
|
|
513
794
|
}
|
|
514
|
-
var
|
|
515
|
-
|
|
516
|
-
|
|
795
|
+
var TOOL_METADATA_KEYS = [
|
|
796
|
+
"path",
|
|
797
|
+
"query",
|
|
798
|
+
"effectivePattern",
|
|
799
|
+
"startLine",
|
|
800
|
+
"endLine",
|
|
801
|
+
"totalLines",
|
|
802
|
+
"totalBytes",
|
|
803
|
+
"bytes",
|
|
804
|
+
"totalChars",
|
|
805
|
+
"count",
|
|
806
|
+
"matchCount",
|
|
807
|
+
"matchedFiles",
|
|
808
|
+
"truncated",
|
|
809
|
+
"limitedByMaxResults",
|
|
810
|
+
"limitedByMaxDepth",
|
|
811
|
+
"visitedEntries",
|
|
812
|
+
"maxResults",
|
|
813
|
+
"exitCode",
|
|
814
|
+
"status",
|
|
815
|
+
"timedOut",
|
|
816
|
+
"aborted",
|
|
817
|
+
"replacements",
|
|
818
|
+
"code",
|
|
819
|
+
"error",
|
|
820
|
+
"message",
|
|
821
|
+
"pasteId",
|
|
822
|
+
"source"
|
|
823
|
+
];
|
|
824
|
+
function trimHistoryToContextBudget(history, model) {
|
|
825
|
+
if (history.length === 0) return { history, droppedCount: 0 };
|
|
826
|
+
const { rawMessageBudget } = planContextUsage({
|
|
827
|
+
contextWindow: getModelContextWindow(model ?? ""),
|
|
828
|
+
summaryTokens: 0,
|
|
829
|
+
// localLoop 没有 web 端的负载分档器;medium 是中性默认值,
|
|
830
|
+
// 不为了省几个 token 在这里复制一份分类逻辑。
|
|
831
|
+
recentLoad: "medium"
|
|
832
|
+
});
|
|
833
|
+
const messageTokens = (message) => {
|
|
834
|
+
const toolCalls = message.tool_calls;
|
|
835
|
+
return estimateTokenCount(contentAsText(message.content)) + (Array.isArray(toolCalls) ? estimateTokenCount(JSON.stringify(toolCalls)) : 0);
|
|
836
|
+
};
|
|
837
|
+
let used = 0;
|
|
838
|
+
let start = history.length;
|
|
839
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
840
|
+
const cost = messageTokens(history[i]);
|
|
841
|
+
if (used + cost > rawMessageBudget && start < history.length) break;
|
|
842
|
+
used += cost;
|
|
843
|
+
start = i;
|
|
844
|
+
}
|
|
845
|
+
if (start === 0) return { history, droppedCount: 0 };
|
|
846
|
+
return {
|
|
847
|
+
history: sanitizeToolCallPairing(history.slice(start)),
|
|
848
|
+
droppedCount: start
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function contentAsText(content) {
|
|
852
|
+
if (typeof content === "string") return content;
|
|
853
|
+
if (!Array.isArray(content)) return "";
|
|
854
|
+
return content.map((part) => {
|
|
855
|
+
if (part?.type === "text") return part.text;
|
|
856
|
+
if (part?.type === "image_url") return part.image_url.url;
|
|
857
|
+
return "";
|
|
858
|
+
}).join("\n");
|
|
859
|
+
}
|
|
860
|
+
function contentCharCount(content) {
|
|
861
|
+
if (typeof content === "string") return content.length;
|
|
862
|
+
if (!Array.isArray(content)) return 0;
|
|
863
|
+
return content.reduce((total, part) => {
|
|
864
|
+
if (part?.type === "text") return total + part.text.length;
|
|
865
|
+
if (part?.type === "image_url") return total + part.image_url.url.length;
|
|
866
|
+
return total;
|
|
867
|
+
}, 0);
|
|
868
|
+
}
|
|
869
|
+
function compactToolMetadata(metadata) {
|
|
870
|
+
if (!metadata) return "";
|
|
871
|
+
const selected = {};
|
|
872
|
+
for (const key of TOOL_METADATA_KEYS) {
|
|
873
|
+
const value = metadata[key];
|
|
874
|
+
if (value === void 0) continue;
|
|
875
|
+
if (typeof value === "string") {
|
|
876
|
+
selected[key] = clipCompactText(value, 240);
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
if (Array.isArray(value)) {
|
|
880
|
+
selected[key] = value.slice(0, 20).map(
|
|
881
|
+
(item) => typeof item === "string" ? clipCompactText(item, 180) : clipCompactText(JSON.stringify(item), 180)
|
|
882
|
+
);
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
selected[key] = value;
|
|
886
|
+
}
|
|
887
|
+
return Object.keys(selected).length > 0 ? clipCompactText(JSON.stringify(selected), 1200) : "";
|
|
888
|
+
}
|
|
889
|
+
function projectToolContentForProvider(args) {
|
|
890
|
+
const content = args.content;
|
|
517
891
|
if (typeof content !== "string") return content;
|
|
518
|
-
|
|
519
|
-
const
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
892
|
+
const metadataText = compactToolMetadata(args.metadata);
|
|
893
|
+
const embeddedMetadataIndex = metadataText ? content.indexOf("\n\n[tool metadata]\n") : -1;
|
|
894
|
+
const contentForProjection = embeddedMetadataIndex >= 0 ? content.slice(0, embeddedMetadataIndex) : content;
|
|
895
|
+
const metadataSuffix = metadataText ? `
|
|
896
|
+
|
|
897
|
+
[tool metadata]
|
|
898
|
+
${metadataText}` : "";
|
|
899
|
+
if (embeddedMetadataIndex >= 0 && content.length <= args.maxChars) {
|
|
900
|
+
return content;
|
|
901
|
+
}
|
|
902
|
+
const headRatio = resolveToolOutputProfile(args.toolName).headRatio;
|
|
903
|
+
const diagnostic = (clippedLength) => `[${args.label}; originalChars=${content.length}; omittedChars=${Math.max(
|
|
904
|
+
0,
|
|
905
|
+
content.length - clippedLength
|
|
906
|
+
)}]`;
|
|
907
|
+
const suffix = (clippedLength) => [diagnostic(clippedLength), metadataSuffix.trimStart()].filter(Boolean).join("\n\n");
|
|
908
|
+
const initialBudget = Math.max(
|
|
909
|
+
1,
|
|
910
|
+
args.maxChars - suffix(contentForProjection.length).length - 2
|
|
911
|
+
);
|
|
912
|
+
let clipped = clipToolText(contentForProjection, initialBudget, headRatio);
|
|
913
|
+
const wasClipped = clipped.length < contentForProjection.trim().length;
|
|
914
|
+
const needsProjection = wasClipped || Boolean(metadataSuffix) || embeddedMetadataIndex >= 0;
|
|
915
|
+
if (!needsProjection) return args.content;
|
|
916
|
+
let projected = wasClipped || metadataSuffix ? `${clipped}
|
|
917
|
+
|
|
918
|
+
${suffix(clipped.length)}` : clipped;
|
|
919
|
+
if (projected.length > args.maxChars) {
|
|
920
|
+
const boundedBudget = Math.max(
|
|
921
|
+
1,
|
|
922
|
+
args.maxChars - suffix(clipped.length).length - 2
|
|
923
|
+
);
|
|
924
|
+
clipped = clipToolText(contentForProjection, boundedBudget, headRatio);
|
|
925
|
+
projected = `${clipped}
|
|
926
|
+
|
|
927
|
+
${suffix(clipped.length)}`;
|
|
928
|
+
}
|
|
929
|
+
return projected.length <= args.maxChars ? projected : projected.slice(0, args.maxChars);
|
|
526
930
|
}
|
|
527
|
-
function summarizeHistoricalToolContent(content) {
|
|
528
|
-
|
|
931
|
+
function summarizeHistoricalToolContent(content, toolName, metadata) {
|
|
932
|
+
const profile = resolveToolOutputProfile(toolName);
|
|
933
|
+
return projectToolContentForProvider({
|
|
529
934
|
content,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
935
|
+
toolName,
|
|
936
|
+
metadata,
|
|
937
|
+
maxChars: Math.min(profile.maxChars, MAX_HISTORICAL_TOOL_CONTENT_CHARS),
|
|
938
|
+
label: "historical tool result truncated for the next turn"
|
|
939
|
+
});
|
|
533
940
|
}
|
|
534
941
|
function prepareMessagesForProviderCall(messages) {
|
|
535
942
|
const paired = sanitizeToolCallPairing(messages);
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
943
|
+
let freshRunStart = paired.length;
|
|
944
|
+
while (freshRunStart > 0 && paired[freshRunStart - 1].role === "tool") {
|
|
945
|
+
freshRunStart -= 1;
|
|
946
|
+
}
|
|
947
|
+
let toolMessageCount = 0;
|
|
948
|
+
let rawToolContentChars = 0;
|
|
949
|
+
let projectedToolContentChars = 0;
|
|
950
|
+
let truncatedToolResults = 0;
|
|
951
|
+
const projected = paired.map((message, index) => {
|
|
952
|
+
const { context_reference: _contextReference, ...providerMessage } = message;
|
|
953
|
+
const sanitizedContent = providerMessage.content == null ? "" : typeof providerMessage.content === "string" ? providerMessage.content : providerMessage.content;
|
|
954
|
+
if (providerMessage.role !== "tool") {
|
|
539
955
|
return {
|
|
540
|
-
...
|
|
956
|
+
...providerMessage,
|
|
541
957
|
content: sanitizedContent
|
|
542
958
|
};
|
|
543
959
|
}
|
|
960
|
+
toolMessageCount += 1;
|
|
961
|
+
rawToolContentChars += contentCharCount(sanitizedContent);
|
|
962
|
+
const isFresh = index >= freshRunStart;
|
|
963
|
+
const maxChars = isFresh ? FRESH_TOOL_OUTPUT_MAX_CHARS : resolveToolOutputProfile(providerMessage.toolName).maxChars;
|
|
964
|
+
const projectedContent = projectToolContentForProvider({
|
|
965
|
+
content: sanitizedContent,
|
|
966
|
+
toolName: providerMessage.toolName,
|
|
967
|
+
metadata: providerMessage.tool_result_metadata,
|
|
968
|
+
maxChars,
|
|
969
|
+
label: "in-turn tool result truncated/projected before next provider call"
|
|
970
|
+
});
|
|
971
|
+
projectedToolContentChars += contentCharCount(projectedContent);
|
|
972
|
+
if (contentCharCount(projectedContent) < contentCharCount(sanitizedContent)) {
|
|
973
|
+
truncatedToolResults += 1;
|
|
974
|
+
}
|
|
544
975
|
return {
|
|
545
|
-
...
|
|
546
|
-
content:
|
|
547
|
-
sanitizedContent,
|
|
548
|
-
MAX_IN_TURN_TOOL_CONTENT_CHARS,
|
|
549
|
-
"in-turn tool result truncated before next provider call"
|
|
550
|
-
)
|
|
976
|
+
...providerMessage,
|
|
977
|
+
content: projectedContent
|
|
551
978
|
};
|
|
552
979
|
});
|
|
980
|
+
return {
|
|
981
|
+
messages: projected,
|
|
982
|
+
metrics: {
|
|
983
|
+
messageCount: projected.length,
|
|
984
|
+
contentChars: projected.reduce((total, message) => total + contentCharCount(message.content), 0),
|
|
985
|
+
toolMessageCount,
|
|
986
|
+
rawToolContentChars,
|
|
987
|
+
projectedToolContentChars,
|
|
988
|
+
truncatedToolResults,
|
|
989
|
+
stableContextChars: 0,
|
|
990
|
+
dynamicContextChars: 0
|
|
991
|
+
}
|
|
992
|
+
};
|
|
553
993
|
}
|
|
554
|
-
function prepareHistoryForNextTurn(history) {
|
|
994
|
+
function prepareHistoryForNextTurn(history, contextReferenceResolver) {
|
|
555
995
|
return history.map((message) => {
|
|
996
|
+
if (message.role === "user" && message.context_reference !== void 0 && contextReferenceResolver?.(message.context_reference)) {
|
|
997
|
+
return { ...message, content: message.context_reference };
|
|
998
|
+
}
|
|
556
999
|
if (message.role !== "tool") return message;
|
|
557
1000
|
return {
|
|
558
1001
|
...message,
|
|
559
|
-
content: summarizeHistoricalToolContent(
|
|
1002
|
+
content: summarizeHistoricalToolContent(
|
|
1003
|
+
message.content,
|
|
1004
|
+
message.toolName,
|
|
1005
|
+
message.tool_result_metadata
|
|
1006
|
+
)
|
|
560
1007
|
};
|
|
561
1008
|
});
|
|
562
1009
|
}
|
|
@@ -578,25 +1025,33 @@ function buildMessages(args) {
|
|
|
578
1025
|
if (args.contextBlockScopes?.length) {
|
|
579
1026
|
const blocks2 = args.contextBlockScopes.filter((b) => b.content.trim());
|
|
580
1027
|
const stableParts = [args.prompt?.trim(), ...blocks2.filter((b) => b.cacheScope === "session").map((b) => b.content)].filter(Boolean);
|
|
581
|
-
const dynamicParts = blocks2.filter((b) => b.cacheScope === "turn").map((b) => b.content).filter(Boolean);
|
|
1028
|
+
const dynamicParts = blocks2.filter((b) => b.cacheScope === "turn").map((b) => b.content).map((block) => block.trim()).filter(Boolean);
|
|
582
1029
|
const stableContent = stableParts.join("\n\n");
|
|
583
1030
|
const dynamicContent = dynamicParts.join("\n\n");
|
|
584
1031
|
const systemContent2 = dynamicContent ? `${stableContent}
|
|
585
1032
|
|
|
586
1033
|
${dynamicContent}` : stableContent;
|
|
587
|
-
return
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
1034
|
+
return {
|
|
1035
|
+
messages: [
|
|
1036
|
+
...systemContent2 ? [{ role: "system", content: systemContent2 }] : [],
|
|
1037
|
+
...prepareHistoryForNextTurn(args.history, args.contextReferenceResolver),
|
|
1038
|
+
{ role: "user", content: args.input }
|
|
1039
|
+
],
|
|
1040
|
+
stableContextChars: stableContent.length,
|
|
1041
|
+
dynamicContextChars: dynamicContent.length
|
|
1042
|
+
};
|
|
592
1043
|
}
|
|
593
1044
|
const blocks = (args.contextBlocks ?? []).map((block) => block.trim()).filter(Boolean);
|
|
594
1045
|
const systemContent = [args.prompt?.trim(), ...blocks].filter(Boolean).join("\n\n");
|
|
595
|
-
return
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
1046
|
+
return {
|
|
1047
|
+
messages: [
|
|
1048
|
+
...systemContent ? [{ role: "system", content: systemContent }] : [],
|
|
1049
|
+
...prepareHistoryForNextTurn(args.history, args.contextReferenceResolver),
|
|
1050
|
+
{ role: "user", content: args.input }
|
|
1051
|
+
],
|
|
1052
|
+
stableContextChars: (args.prompt?.trim() ?? "").length,
|
|
1053
|
+
dynamicContextChars: blocks.join("\n\n").length
|
|
1054
|
+
};
|
|
600
1055
|
}
|
|
601
1056
|
function mergeTurnUsage(current, next) {
|
|
602
1057
|
if (!next) return current;
|
|
@@ -619,6 +1074,24 @@ function mergeTurnUsage(current, next) {
|
|
|
619
1074
|
cache_creation_input_tokens: left.cacheMiss + right.cacheMiss
|
|
620
1075
|
};
|
|
621
1076
|
}
|
|
1077
|
+
function addOutOfBandUsage(turn, extra) {
|
|
1078
|
+
if (!extra) return turn;
|
|
1079
|
+
const num = (u, ...keys) => {
|
|
1080
|
+
if (!u) return 0;
|
|
1081
|
+
for (const k of keys) {
|
|
1082
|
+
const v = Number(u[k]);
|
|
1083
|
+
if (Number.isFinite(v) && v !== 0) return v;
|
|
1084
|
+
}
|
|
1085
|
+
return 0;
|
|
1086
|
+
};
|
|
1087
|
+
return {
|
|
1088
|
+
...turn ?? {},
|
|
1089
|
+
input_tokens: num(turn, "input_tokens", "prompt_tokens") + num(extra, "input_tokens", "prompt_tokens"),
|
|
1090
|
+
output_tokens: num(turn, "output_tokens", "completion_tokens") + num(extra, "output_tokens", "completion_tokens"),
|
|
1091
|
+
cache_read_input_tokens: num(turn, "cache_read_input_tokens", "prompt_cache_hit_tokens") + num(extra, "cache_read_input_tokens", "prompt_cache_hit_tokens"),
|
|
1092
|
+
cache_creation_input_tokens: num(turn, "cache_creation_input_tokens", "prompt_cache_miss_tokens") + num(extra, "cache_creation_input_tokens", "prompt_cache_miss_tokens")
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
622
1095
|
function extractUserInputText(content) {
|
|
623
1096
|
if (typeof content === "string") return content;
|
|
624
1097
|
if (!Array.isArray(content)) return "";
|
|
@@ -655,6 +1128,21 @@ async function persistFailedLocalTurn(args) {
|
|
|
655
1128
|
return args.input.continueDialogId;
|
|
656
1129
|
}
|
|
657
1130
|
}
|
|
1131
|
+
function applyPersistedTurnInput(messages, persistedInput, persistedInputReference) {
|
|
1132
|
+
if (persistedInput === void 0 && persistedInputReference === void 0) {
|
|
1133
|
+
return messages;
|
|
1134
|
+
}
|
|
1135
|
+
let replaced = false;
|
|
1136
|
+
return messages.map((message) => {
|
|
1137
|
+
if (replaced || message.role !== "user") return message;
|
|
1138
|
+
replaced = true;
|
|
1139
|
+
return {
|
|
1140
|
+
...message,
|
|
1141
|
+
...persistedInput !== void 0 ? { content: persistedInput } : {},
|
|
1142
|
+
...persistedInputReference !== void 0 ? { context_reference: persistedInputReference } : {}
|
|
1143
|
+
};
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
658
1146
|
async function runLocalAgentTurn(input) {
|
|
659
1147
|
const agentConfig = await input.adapter.loadAgentConfig(input.agentRef);
|
|
660
1148
|
if (!agentConfig) {
|
|
@@ -694,26 +1182,52 @@ async function runLocalAgentTurn(input) {
|
|
|
694
1182
|
const currentTimeScope = [
|
|
695
1183
|
{ content: buildCurrentTimeBlock(/* @__PURE__ */ new Date(), void 0), cacheScope: "turn" }
|
|
696
1184
|
];
|
|
1185
|
+
const callerScopes = normalizeContextBlockScopes(
|
|
1186
|
+
input.contextBlocks,
|
|
1187
|
+
input.contextBlockScopes
|
|
1188
|
+
);
|
|
697
1189
|
const mergedContextBlockScopes = [
|
|
698
1190
|
{ content: identityBlock, cacheScope: "session" },
|
|
699
1191
|
...guidanceScopes,
|
|
700
1192
|
...currentTimeScope,
|
|
701
|
-
...
|
|
1193
|
+
...callerScopes
|
|
702
1194
|
];
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1195
|
+
let resolvedProvider;
|
|
1196
|
+
const resolveProviderOnce = async () => {
|
|
1197
|
+
if (!resolvedProvider) {
|
|
1198
|
+
resolvedProvider = await input.adapter.resolveProvider(agentConfig);
|
|
1199
|
+
}
|
|
1200
|
+
return resolvedProvider;
|
|
1201
|
+
};
|
|
1202
|
+
let compactionUsage;
|
|
1203
|
+
try {
|
|
1204
|
+
const compacted = await maybeAutoCompactLocalHistory({
|
|
1205
|
+
adapter: input.adapter,
|
|
1206
|
+
dialogId: input.continueDialogId,
|
|
1207
|
+
history,
|
|
1208
|
+
model: agentConfig.model,
|
|
1209
|
+
resolveProvider: resolveProviderOnce
|
|
1210
|
+
});
|
|
1211
|
+
history = compacted.history;
|
|
1212
|
+
compactionUsage = compacted.usage;
|
|
1213
|
+
} catch (error) {
|
|
1214
|
+
console.warn("[localLoop] auto-compaction unexpected error:", error);
|
|
1215
|
+
}
|
|
1216
|
+
const trimmedHistory = trimHistoryToContextBudget(history, agentConfig.model);
|
|
1217
|
+
if (trimmedHistory.droppedCount > 0) {
|
|
1218
|
+
history = trimmedHistory.history;
|
|
1219
|
+
}
|
|
1220
|
+
const hasContextBlocks = callerScopes.some((block) => block.content.trim()) || mergedContextBlockScopes.some((block) => block.content.trim());
|
|
708
1221
|
const promptMessageCount = agentConfig.prompt?.trim() || hasContextBlocks ? 1 : 0;
|
|
709
1222
|
const turnStartIndex = promptMessageCount + history.length;
|
|
710
|
-
const
|
|
1223
|
+
const builtMessages = buildMessages({
|
|
711
1224
|
prompt: agentConfig.prompt,
|
|
712
|
-
contextBlocks: input.contextBlocks,
|
|
713
1225
|
contextBlockScopes: mergedContextBlockScopes,
|
|
714
1226
|
history,
|
|
715
|
-
input: input.input
|
|
1227
|
+
input: input.input,
|
|
1228
|
+
contextReferenceResolver: input.adapter.host === "cli" ? input.contextReferenceResolver : void 0
|
|
716
1229
|
});
|
|
1230
|
+
const messages = builtMessages.messages;
|
|
717
1231
|
const supportsImages = resolveAgentImageInputSupport({
|
|
718
1232
|
apiSource: agentConfig.apiSource,
|
|
719
1233
|
provider: agentConfig.provider,
|
|
@@ -732,12 +1246,13 @@ async function runLocalAgentTurn(input) {
|
|
|
732
1246
|
let skipFinalAppend = false;
|
|
733
1247
|
let partialContent = "";
|
|
734
1248
|
try {
|
|
735
|
-
const provider = await
|
|
1249
|
+
const provider = await resolveProviderOnce();
|
|
736
1250
|
while (true) {
|
|
737
1251
|
partialContent = "";
|
|
738
1252
|
throwIfAborted(input);
|
|
1253
|
+
const preparedMessages = prepareMessagesForProviderCall(messages);
|
|
739
1254
|
const baseRequestMessages = filterImagePartsFromMessages(
|
|
740
|
-
|
|
1255
|
+
preparedMessages.messages,
|
|
741
1256
|
supportsImages
|
|
742
1257
|
);
|
|
743
1258
|
if (!supportsImages && !imageDowngradeNotified && messages.some((m) => Array.isArray(m.content) && m.content.some((p) => p?.type === "image_url"))) {
|
|
@@ -749,6 +1264,16 @@ async function runLocalAgentTurn(input) {
|
|
|
749
1264
|
});
|
|
750
1265
|
}
|
|
751
1266
|
const requestMessages = emptyAssistantRepairPending ? [...baseRequestMessages, { role: "user", content: EMPTY_ASSISTANT_REPAIR_PROMPT }] : baseRequestMessages;
|
|
1267
|
+
const contextMetrics = {
|
|
1268
|
+
...preparedMessages.metrics,
|
|
1269
|
+
messageCount: requestMessages.length,
|
|
1270
|
+
contentChars: requestMessages.reduce(
|
|
1271
|
+
(total, message) => total + contentCharCount(message.content),
|
|
1272
|
+
0
|
|
1273
|
+
),
|
|
1274
|
+
stableContextChars: builtMessages.stableContextChars,
|
|
1275
|
+
dynamicContextChars: builtMessages.dynamicContextChars
|
|
1276
|
+
};
|
|
752
1277
|
emptyAssistantRepairPending = false;
|
|
753
1278
|
result = await runCompleteWithTimeout({
|
|
754
1279
|
provider,
|
|
@@ -765,7 +1290,8 @@ async function runLocalAgentTurn(input) {
|
|
|
765
1290
|
},
|
|
766
1291
|
timeoutMs: resolveLlmRequestTimeoutMs(input),
|
|
767
1292
|
round,
|
|
768
|
-
input
|
|
1293
|
+
input,
|
|
1294
|
+
context: contextMetrics
|
|
769
1295
|
});
|
|
770
1296
|
turnUsage = mergeTurnUsage(turnUsage, result.usage);
|
|
771
1297
|
const toolCalls = result.tool_calls ?? [];
|
|
@@ -775,7 +1301,8 @@ async function runLocalAgentTurn(input) {
|
|
|
775
1301
|
hasToolCalls: rawToolCallsCount > 0,
|
|
776
1302
|
hasVisibleOutput: hasAssistantVisibleOutput(result.content),
|
|
777
1303
|
repairUsed: emptyAssistantRepairUsed,
|
|
778
|
-
finishReason: result.finish_reason
|
|
1304
|
+
finishReason: result.finish_reason,
|
|
1305
|
+
streamComplete: result.stream_complete
|
|
779
1306
|
});
|
|
780
1307
|
if (outcome.kind === "repair") {
|
|
781
1308
|
emptyAssistantRepairPending = true;
|
|
@@ -835,13 +1362,14 @@ async function runLocalAgentTurn(input) {
|
|
|
835
1362
|
argumentsPreview: summarizeToolArguments(toolName, toolCall.function.arguments)
|
|
836
1363
|
});
|
|
837
1364
|
try {
|
|
838
|
-
|
|
1365
|
+
const executePromise = input.adapter.executeTool({
|
|
839
1366
|
id: toolCall.id,
|
|
840
1367
|
name: toolName,
|
|
841
1368
|
arguments: toolCall.function.arguments,
|
|
842
1369
|
...userInputText ? { userInput: userInputText } : {},
|
|
843
1370
|
...input.abortSignal ? { abortSignal: input.abortSignal } : {}
|
|
844
1371
|
});
|
|
1372
|
+
toolResult = await raceWithAbort(input, executePromise, toolName);
|
|
845
1373
|
const actionGate = buildActionGate({
|
|
846
1374
|
toolName,
|
|
847
1375
|
toolCallId: toolCall.id,
|
|
@@ -866,6 +1394,9 @@ async function runLocalAgentTurn(input) {
|
|
|
866
1394
|
});
|
|
867
1395
|
} catch (error) {
|
|
868
1396
|
emitLoopEvent(input, { kind: "tool-end", name: toolName, atMs: Date.now(), ok: false });
|
|
1397
|
+
if (error && typeof error === "object" && error.code === LOCAL_TURN_ABORTED_CODE) {
|
|
1398
|
+
throw error;
|
|
1399
|
+
}
|
|
869
1400
|
if (!shouldReturnToolExecutionErrors(input.adapter)) throw error;
|
|
870
1401
|
emitToolEvent(input, {
|
|
871
1402
|
type: "tool-error",
|
|
@@ -903,7 +1434,11 @@ async function runLocalAgentTurn(input) {
|
|
|
903
1434
|
loopError = error;
|
|
904
1435
|
}
|
|
905
1436
|
if (loopError) {
|
|
906
|
-
const turnMessages2 =
|
|
1437
|
+
const turnMessages2 = applyPersistedTurnInput(
|
|
1438
|
+
messages.slice(turnStartIndex),
|
|
1439
|
+
input.persistedInput,
|
|
1440
|
+
input.persistedInputReference
|
|
1441
|
+
);
|
|
907
1442
|
const dialogId = await persistFailedLocalTurn({
|
|
908
1443
|
adapter: input.adapter,
|
|
909
1444
|
agentKey: agentConfig.key,
|
|
@@ -927,7 +1462,11 @@ async function runLocalAgentTurn(input) {
|
|
|
927
1462
|
...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {}
|
|
928
1463
|
});
|
|
929
1464
|
}
|
|
930
|
-
const turnMessages =
|
|
1465
|
+
const turnMessages = applyPersistedTurnInput(
|
|
1466
|
+
messages.slice(turnStartIndex),
|
|
1467
|
+
input.persistedInput,
|
|
1468
|
+
input.persistedInputReference
|
|
1469
|
+
);
|
|
931
1470
|
const saved = await input.adapter.saveTurn({
|
|
932
1471
|
agentKey: agentConfig.key,
|
|
933
1472
|
messages: turnMessages,
|
|
@@ -945,7 +1484,10 @@ async function runLocalAgentTurn(input) {
|
|
|
945
1484
|
});
|
|
946
1485
|
return {
|
|
947
1486
|
...result,
|
|
948
|
-
...
|
|
1487
|
+
...(() => {
|
|
1488
|
+
const merged = addOutOfBandUsage(turnUsage, compactionUsage);
|
|
1489
|
+
return merged ? { usage: merged } : {};
|
|
1490
|
+
})(),
|
|
949
1491
|
...toolCallCount > 0 ? { toolCallCount } : {},
|
|
950
1492
|
...agentConfig.toolSurface ? { runtimeToolSurface: agentConfig.toolSurface } : {},
|
|
951
1493
|
// 透出最后一轮 provider 调用的 finish_reason;多轮工具循环里只有最后一轮收尾状态有意义。
|
|
@@ -961,6 +1503,7 @@ export {
|
|
|
961
1503
|
parseToolArgumentsJson,
|
|
962
1504
|
summarizeToolArguments,
|
|
963
1505
|
resolveAgentImageInputSupport,
|
|
1506
|
+
projectToolMessageContent,
|
|
964
1507
|
LOCAL_AGENT_CONFIG_MISSING_CODE,
|
|
965
1508
|
EMPTY_ASSISTANT_REPAIR_PROMPT,
|
|
966
1509
|
LOCAL_TURN_ABORTED_CODE,
|