nolo-cli 0.11.0 → 0.12.0-alpha.1
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-MWAY74HA.js} +51 -50
- package/{action-AA4IJ5K2.js → action-RAXNQQKC.js} +8 -9
- package/{addContentAction-QYVO4SYP.js → addContentAction-ZHI7TVV3.js} +7 -9
- package/{addDialogAgentAction-WVVKT5WQ.js → addDialogAgentAction-JWXPKXNC.js} +9 -10
- package/{agentDeleteCommand-RDPGYIWF.js → agentDeleteCommand-LXR5A5FV.js} +38 -28
- package/{agentEmailCommands-2HJJKS5F.js → agentEmailCommands-DXRAP7BB.js} +64 -62
- package/{agentListCommands-HFEVV3ZW.js → agentListCommands-ZFMS5ZLY.js} +127 -31
- package/{agentMachineCommands-25ZOENJU.js → agentMachineCommands-ZJLYVV52.js} +37 -26
- package/{agentPullCommand-GOL2S4W2.js → agentPullCommand-KRBRIFIY.js} +7 -7
- package/{agentRecordCommands-GTCOAM3L.js → agentRecordCommands-DFS626TB.js} +56 -54
- package/{agentRunCommand-YBS3JDLD.js → agentRunCommand-GWBIE2TS.js} +59 -59
- package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
- package/{agentRuntimeLocal-UQBMQI2M.js → agentRuntimeLocal-YNZSHMZC.js} +30 -15
- package/{agentSlice-6EKOBCCS.js → agentSlice-T2PXOEPQ.js} +8 -9
- package/{authCommand-O2OWACN4.js → authCommand-DMW336GT.js} +15 -15
- package/{authCommands-H7365TZP.js → authCommands-GH5T6RYV.js} +6 -6
- package/{chunk-7D3JY6C2.js → chunk-3HHTVM6G.js} +64 -5
- package/{chunk-AFON4QUU.js → chunk-3QKO4EHV.js} +3 -3
- package/{chunk-4SOKXIW4.js → chunk-43UD5ZP7.js} +8 -11
- package/{chunk-S3GS2PRL.js → chunk-4BTHUYMY.js} +597 -68
- package/{chunk-SV5MWASH.js → chunk-4EH7AR52.js} +12 -12
- package/{chunk-FXXSGWAJ.js → chunk-4GBNEA5B.js} +1 -1
- 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-X2S7JTIG.js → chunk-5ITRDSHY.js} +1240 -136
- package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
- package/{chunk-GBDSZATH.js → chunk-6LMFFG3K.js} +3 -3
- package/{chunk-OUI3ZHYD.js → chunk-6OVCRC72.js} +1 -1
- package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
- package/{chunk-MQI77IRB.js → chunk-7MYHLZ3F.js} +2 -2
- package/{chunk-5MOUAAMK.js → chunk-AM53Q2SD.js} +194 -2564
- package/chunk-BYXBJQAS.js +0 -0
- package/{chunk-LAX2JSTX.js → chunk-DE5BA4O6.js} +20 -2
- package/{chunk-BX6U3ETE.js → chunk-DRMGJFGQ.js} +293 -89
- package/{chunk-KJR5KAUQ.js → chunk-EBH7G34M.js} +20 -7
- package/{chunk-E4HNJSHC.js → chunk-G3B356GF.js} +16 -18
- package/{chunk-KQQVGRA3.js → chunk-GDBCUWQ3.js} +35 -29
- package/{chunk-Q3UKJVU4.js → chunk-GPZM7QH6.js} +10 -10
- package/chunk-IKXL2HZ3.js +70 -0
- package/{chunk-QNMFBBZK.js → chunk-IVAOVLM7.js} +1 -1
- package/{chunk-EVZKZF32.js → chunk-JUN3HFMN.js} +182 -75
- package/{chunk-NEM67FX7.js → chunk-JZDIW766.js} +1 -1
- package/{chunk-TACHTH5D.js → chunk-LHSJMY5V.js} +18 -18
- package/{chunk-XYZA7S7V.js → chunk-MEVCDATU.js} +6 -6
- package/{chunk-YOCHZD7D.js → chunk-NCGFB7NN.js} +5 -3
- package/{chunk-O26GI2JP.js → chunk-NITP2JDG.js} +39 -8
- package/{chunk-YPRZSPF3.js → chunk-OCDEWRE2.js} +1 -1
- package/{chunk-PNEW2Z3T.js → chunk-OHEKYYJF.js} +7 -7
- package/chunk-OOIN7HSO.js +146 -0
- package/{chunk-DWGAEFJL.js → chunk-OPKKB25T.js} +1 -1
- package/{chunk-AEZAORLG.js → chunk-QCA3EPYK.js} +5 -5
- package/{chunk-C4FTUXVF.js → chunk-QLUFR47U.js} +11 -1
- package/{chunk-C2MDQQ4O.js → chunk-RNQ5BLR2.js} +1 -1
- package/{chunk-L53FGHA4.js → chunk-RSEUHW6T.js} +1 -1
- package/{chunk-2W2VYVI5.js → chunk-RZ735TLX.js} +527 -176
- package/chunk-S3UTETIC.js +4885 -0
- package/chunk-S73OEOHH.js +2286 -0
- package/{chunk-YTBMA7NO.js → chunk-TSKWE74T.js} +58 -8
- package/{chunk-5OIFWVX7.js → chunk-UG3WOFU6.js} +1 -1
- package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
- package/{chunk-IPYUUY4E.js → chunk-WPHL6ALY.js} +1 -1
- package/{chunk-3NSIQZZ6.js → chunk-WTYKCU4W.js} +9 -9
- package/{chunk-LP65VRCI.js → chunk-WWU275LU.js} +6 -6
- package/{chunk-XFOLQLDN.js → chunk-XHQWJUW4.js} +2 -2
- package/{chunk-D6OQHXVG.js → chunk-YDCZVUFP.js} +7 -7
- package/{chunk-PXQN7IBQ.js → chunk-YTQLJYLB.js} +2 -2
- package/{chunk-ZEYDOXHF.js → chunk-ZPFRLGIZ.js} +1467 -869
- package/{chunk-ZPRK73XY.js → chunk-ZR4VHQ5D.js} +14 -13
- package/cliEnvHelpers-VYC2OPTB.js +30 -0
- package/{createAgentAutomationAction-LTI5QU35.js → createAgentAutomationAction-G32GKDGV.js} +25 -26
- package/{createDialogAction-OJJ7L3L3.js → createDialogAction-TISANRZK.js} +26 -27
- package/{createPageAction-5MXWVRGY.js → createPageAction-O7KOYNV4.js} +26 -27
- package/{dbSlice-RQFQQVHT.js → dbSlice-WDP2ZZHK.js} +6 -7
- package/{deleteFile-7XP5BGC7.js → deleteFile-M7UNIUOO.js} +4 -4
- package/{dialogCommands-JTJ3RPPW.js → dialogCommands-6JPB3YQY.js} +33 -23
- package/{dialogSlice-OWROKURJ.js → dialogSlice-PRHHYSP5.js} +26 -27
- package/{docCreateCommands-WJBEW2V7.js → docCreateCommands-75E6TZNN.js} +33 -23
- package/{docDeleteCommands-BVEHJOY5.js → docDeleteCommands-EJHLN7LW.js} +34 -25
- package/{docReadCommands-NEWOZIJV.js → docReadCommands-RJYBC4FS.js} +33 -23
- package/{docStore-3DXRYBMD.js → docStore-H5VWKT5D.js} +25 -26
- package/{docUpdateCommands-C7WHT7UU.js → docUpdateCommands-MG4CNISV.js} +33 -23
- package/fetchMessages-KTNM7QAR.js +1 -1
- package/{getPricing-3E6UNPKL.js → getPricing-6TOZXPAU.js} +7 -3
- package/{handleSendMessageAction-B5PRR7BE.js → handleSendMessageAction-QTUGICSZ.js} +29 -30
- package/index.js +77 -76
- package/{localDialogRead-YSKFFRTT.js → localDialogRead-2VRI24F4.js} +1 -1
- package/{machineCommands-IIIREAEK.js → machineCommands-MAV7M3TK.js} +63 -61
- package/{machineWsRunDispatch-3SIU6ILW.js → machineWsRunDispatch-ZOEGTKHC.js} +55 -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-SWQKC7ZS.js} +1113 -678
- package/{removeDialogAgentAction-TCT35SOF.js → removeDialogAgentAction-GTX5AN54.js} +13 -13
- package/{runAgentClientLoop-EFM4PTVI.js → runAgentClientLoop-P25HACXX.js} +47 -47
- package/{runtimeDoctorCommands-ID43Z7VX.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
- package/{setDialogExtraReferencesAction-CGYLZMFH.js → setDialogExtraReferencesAction-WFQ63KYO.js} +9 -10
- package/{setPrimaryDialogAgentAction-F6BFQDJU.js → setPrimaryDialogAgentAction-P57FXTSU.js} +13 -13
- package/{settingSlice-2I5HMG47.js → settingSlice-CK5YZGNI.js} +7 -8
- package/{skillDiscovery-UGSQGX2U.js → skillDiscovery-ZYHSX7VD.js} +3 -3
- package/{spaceCommands-MT7BWCIL.js → spaceCommands-6O5EFEM5.js} +37 -29
- package/{spaceSlice-BS5TSW3W.js → spaceSlice-4EXTYXS4.js} +28 -27
- package/{streamAgentChatTurn-I3VBFNR3.js → streamAgentChatTurn-Q3FJAHG2.js} +222 -128
- package/{toolApiClient-C7P7YSE4.js → toolApiClient-QI3YBNWB.js} +2 -2
- package/{tools-XSD2BOEN.js → tools-AHNQE4GP.js} +34 -34
- package/{tuiSummaryLlmCaller-32NRHTT2.js → tuiSummaryLlmCaller-TXLF4MEI.js} +8 -6
- package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
- package/{updateDialogTitleAction-7FYNJOGP.js → updateDialogTitleAction-XZDVLKVX.js} +29 -29
- package/chunk-5MMAK75Q.js +0 -15
- 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,34 @@ import {
|
|
|
2
2
|
buildCurrentTimeBlock,
|
|
3
3
|
buildIdentityBlock,
|
|
4
4
|
buildRuntimeGuidanceBlocks
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6OVCRC72.js";
|
|
6
6
|
import {
|
|
7
7
|
canonicalizeToolNames
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-43UD5ZP7.js";
|
|
9
9
|
import {
|
|
10
|
-
clipCompactText
|
|
11
|
-
|
|
10
|
+
clipCompactText,
|
|
11
|
+
estimateTokenCount,
|
|
12
|
+
getModelContextWindow,
|
|
13
|
+
planCompression,
|
|
14
|
+
planContextUsage,
|
|
15
|
+
serializeMessageContent
|
|
16
|
+
} from "./chunk-S73OEOHH.js";
|
|
12
17
|
import {
|
|
13
18
|
findModelConfig,
|
|
14
19
|
getProviderByModelName
|
|
15
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-S3UTETIC.js";
|
|
21
|
+
import {
|
|
22
|
+
buildDialogSummaryLayer
|
|
23
|
+
} from "./chunk-QLUFR47U.js";
|
|
24
|
+
import {
|
|
25
|
+
toErrorMessage
|
|
26
|
+
} from "./chunk-HNMXYYJR.js";
|
|
16
27
|
import {
|
|
17
28
|
asRecordOrEmpty
|
|
18
29
|
} from "./chunk-KX6YBIHG.js";
|
|
19
30
|
import {
|
|
20
31
|
asTrimmedLowercaseString
|
|
21
32
|
} from "./chunk-QSJ3HFEF.js";
|
|
22
|
-
import {
|
|
23
|
-
compactWhitespace
|
|
24
|
-
} from "./chunk-DWLEOOGR.js";
|
|
25
|
-
import {
|
|
26
|
-
toErrorMessage
|
|
27
|
-
} from "./chunk-HNMXYYJR.js";
|
|
28
33
|
import {
|
|
29
34
|
isRecord
|
|
30
35
|
} from "./chunk-KUG52IAP.js";
|
|
@@ -52,14 +57,27 @@ function readActionGate(value) {
|
|
|
52
57
|
if (typeof value.id !== "string" || !value.id.trim()) return null;
|
|
53
58
|
if (value.kind !== "confirm" && value.kind !== "handoff" && value.kind !== "input") return null;
|
|
54
59
|
if (typeof value.title !== "string" || !value.title.trim()) return null;
|
|
60
|
+
const titleParams = readTitleParams(value.titleParams);
|
|
55
61
|
return {
|
|
56
62
|
id: value.id.trim(),
|
|
57
63
|
kind: value.kind,
|
|
58
64
|
title: value.title.trim(),
|
|
65
|
+
...titleParams ? { titleParams } : {},
|
|
59
66
|
...asOptionalTrimmedString(value.body) ? { body: asOptionalTrimmedString(value.body) } : {},
|
|
60
67
|
...Object.prototype.hasOwnProperty.call(value, "payload") ? { payload: value.payload } : {}
|
|
61
68
|
};
|
|
62
69
|
}
|
|
70
|
+
function readTitleParams(value) {
|
|
71
|
+
if (!isRecord(value)) return void 0;
|
|
72
|
+
const entries = Object.entries(value).flatMap(([key, raw]) => {
|
|
73
|
+
if (typeof raw === "string" || typeof raw === "number") {
|
|
74
|
+
return [[key, raw]];
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
});
|
|
78
|
+
if (entries.length === 0) return void 0;
|
|
79
|
+
return Object.fromEntries(entries);
|
|
80
|
+
}
|
|
63
81
|
|
|
64
82
|
// packages/ai/llm/agentCapabilities.ts
|
|
65
83
|
var isCustomAgent = (agent) => {
|
|
@@ -273,6 +291,227 @@ function summarizeToolArguments(toolName, rawArgs) {
|
|
|
273
291
|
);
|
|
274
292
|
}
|
|
275
293
|
|
|
294
|
+
// packages/agent-runtime/contextBlockScope.ts
|
|
295
|
+
var isContextBlockScope = (value) => {
|
|
296
|
+
if (typeof value !== "object" || value === null) return false;
|
|
297
|
+
const candidate = value;
|
|
298
|
+
return typeof candidate.content === "string" && (candidate.cacheScope === "session" || candidate.cacheScope === "turn");
|
|
299
|
+
};
|
|
300
|
+
var normalizeContextBlockScopes = (legacyBlocks = void 0, scopes = void 0) => {
|
|
301
|
+
const trimmedScopes = (Array.isArray(scopes) ? scopes : []).filter(isContextBlockScope).map((scope) => ({ content: scope.content.trim(), cacheScope: scope.cacheScope })).filter((scope) => scope.content.length > 0);
|
|
302
|
+
if (trimmedScopes.length > 0) {
|
|
303
|
+
return trimmedScopes;
|
|
304
|
+
}
|
|
305
|
+
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" }));
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// packages/ai/agent/toolOutputPolicy.ts
|
|
309
|
+
var MAX_HISTORICAL_TOOL_CONTENT_CHARS = 1600;
|
|
310
|
+
var MAX_IN_TURN_TOOL_CONTENT_CHARS = 4e3;
|
|
311
|
+
var FRESH_TOOL_OUTPUT_MAX_CHARS = 32e3;
|
|
312
|
+
var DEFAULT_TOOL_OUTPUT_PROFILE = {
|
|
313
|
+
maxChars: MAX_IN_TURN_TOOL_CONTENT_CHARS,
|
|
314
|
+
headRatio: 0.5
|
|
315
|
+
};
|
|
316
|
+
var TOOL_OUTPUT_PROFILES = {
|
|
317
|
+
readFile: { maxChars: 4800, headRatio: 0.68 },
|
|
318
|
+
read_file: { maxChars: 4800, headRatio: 0.68 },
|
|
319
|
+
readWorkspaceFile: { maxChars: 4800, headRatio: 0.68 },
|
|
320
|
+
searchFiles: { maxChars: 3600, headRatio: 0.78 },
|
|
321
|
+
search_files: { maxChars: 3600, headRatio: 0.78 },
|
|
322
|
+
listFiles: { maxChars: 2800, headRatio: 0.85 },
|
|
323
|
+
globFiles: { maxChars: 2800, headRatio: 0.85 },
|
|
324
|
+
execShell: { maxChars: 4e3, headRatio: 0.35 },
|
|
325
|
+
runCommand: { maxChars: 4e3, headRatio: 0.35 },
|
|
326
|
+
launchProcess: { maxChars: 2800, headRatio: 0.35 },
|
|
327
|
+
editFile: { maxChars: 2800, headRatio: 0.62 },
|
|
328
|
+
writeFile: { maxChars: 2400, headRatio: 0.62 },
|
|
329
|
+
readPastedText: { maxChars: 4800, headRatio: 0.5 }
|
|
330
|
+
};
|
|
331
|
+
function resolveToolOutputProfile(toolName) {
|
|
332
|
+
return (toolName ? TOOL_OUTPUT_PROFILES[toolName] : void 0) ?? DEFAULT_TOOL_OUTPUT_PROFILE;
|
|
333
|
+
}
|
|
334
|
+
function projectToolMessageContent(input) {
|
|
335
|
+
const { content, isFresh, toolName, historicalMaxChars } = input;
|
|
336
|
+
if (isFresh) {
|
|
337
|
+
if (content.length <= FRESH_TOOL_OUTPUT_MAX_CHARS) return content;
|
|
338
|
+
return clipToolText(
|
|
339
|
+
content,
|
|
340
|
+
FRESH_TOOL_OUTPUT_MAX_CHARS,
|
|
341
|
+
resolveToolOutputProfile(toolName).headRatio,
|
|
342
|
+
`
|
|
343
|
+
\u2026[\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${content.length} \u5B57\u7B26]`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (content.length <= historicalMaxChars) return content;
|
|
347
|
+
return content.slice(0, historicalMaxChars) + `
|
|
348
|
+
\u2026[\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${content.length} \u5B57\u7B26]`;
|
|
349
|
+
}
|
|
350
|
+
function clipToolText(content, maxChars, headRatio, marker = "\n\n[... tool output middle omitted; head/tail preserved ...]\n\n") {
|
|
351
|
+
const normalized = content.replace(/\r\n/g, "\n").trim();
|
|
352
|
+
if (normalized.length <= maxChars) return normalized;
|
|
353
|
+
if (maxChars <= marker.length + 2) return normalized.slice(0, maxChars);
|
|
354
|
+
const available = maxChars - marker.length;
|
|
355
|
+
const headChars = Math.max(1, Math.floor(available * headRatio));
|
|
356
|
+
const tailChars = Math.max(1, available - headChars);
|
|
357
|
+
return `${normalized.slice(0, headChars)}${marker}${normalized.slice(-tailChars)}`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// packages/agent-runtime/localAutoCompaction.ts
|
|
361
|
+
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
|
|
362
|
+
|
|
363
|
+
\u4E25\u683C\u53EA\u8F93\u51FA\u4E0B\u9762\u4E24\u90E8\u5206\uFF0C\u6807\u9898\u5FC5\u987B\u5B8C\u5168\u4E00\u81F4\uFF1A
|
|
364
|
+
\u5173\u952E\u4E8B\u5B9E\u6863\u6848
|
|
365
|
+
- ...
|
|
366
|
+
\u5BF9\u8BDD\u8FDB\u5C55\u4E0E\u5F85\u529E
|
|
367
|
+
- ...
|
|
368
|
+
|
|
369
|
+
\u8981\u6C42\uFF1A
|
|
370
|
+
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
|
|
371
|
+
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
|
|
372
|
+
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
|
|
373
|
+
4) \u5FFD\u7565\u5BD2\u6684\u3001\u91CD\u590D\u5C1D\u8BD5\u3001\u5DF2\u653E\u5F03\u65B9\u6848\u548C\u65E0\u4EF7\u503C\u5E9F\u8BDD\u3002
|
|
374
|
+
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`;
|
|
375
|
+
function buildLocalAutoCompactionUserContent(previousSummary, messagesText) {
|
|
376
|
+
return `\u3010\u73B0\u6709\u8BB0\u5FC6\u3011\uFF1A
|
|
377
|
+
${previousSummary || "(\u65E0)"}
|
|
378
|
+
|
|
379
|
+
\u3010\u65B0\u589E\u5BF9\u8BDD\u3011\uFF1A
|
|
380
|
+
${messagesText}`.trim();
|
|
381
|
+
}
|
|
382
|
+
function toPlanCompressionMessages(history) {
|
|
383
|
+
return history.map((message, index) => {
|
|
384
|
+
const usage = message.usage;
|
|
385
|
+
return {
|
|
386
|
+
id: `local-${index}`,
|
|
387
|
+
role: message.role,
|
|
388
|
+
content: message.content,
|
|
389
|
+
...Array.isArray(message.tool_calls) ? { tool_calls: message.tool_calls } : {},
|
|
390
|
+
...usage?.completion_tokens != null ? { usage: { completion_tokens: usage.completion_tokens } } : {}
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
function formatMessagesForLocalSummary(msgs) {
|
|
395
|
+
return msgs.map((msg) => {
|
|
396
|
+
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]");
|
|
397
|
+
return `${msg.role}: ${content}`;
|
|
398
|
+
}).join("\n");
|
|
399
|
+
}
|
|
400
|
+
function buildLocalSummaryHistoryMessage(summary) {
|
|
401
|
+
const layer = buildDialogSummaryLayer({ summary });
|
|
402
|
+
return {
|
|
403
|
+
role: "user",
|
|
404
|
+
content: layer?.content ?? `--- \u5386\u53F2\u5BF9\u8BDD\u6458\u8981 ---
|
|
405
|
+
${summary.trim()}`
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function projectHistoryWithSummary(args) {
|
|
409
|
+
const bridged = toPlanCompressionMessages(args.history);
|
|
410
|
+
let startIndex = 0;
|
|
411
|
+
if (args.summarizedBeforeId) {
|
|
412
|
+
const found = bridged.findIndex((m) => m.id === args.summarizedBeforeId);
|
|
413
|
+
if (found !== -1) startIndex = found + 1;
|
|
414
|
+
}
|
|
415
|
+
return [
|
|
416
|
+
buildLocalSummaryHistoryMessage(args.summary),
|
|
417
|
+
...args.history.slice(startIndex)
|
|
418
|
+
];
|
|
419
|
+
}
|
|
420
|
+
var COLD_RESUME_IDLE_MS = 60 * 60 * 1e3;
|
|
421
|
+
function isColdResume(history, nowMs) {
|
|
422
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
423
|
+
const at = history[i]?.createdAt;
|
|
424
|
+
if (typeof at === "number" && Number.isFinite(at)) {
|
|
425
|
+
return nowMs - at > COLD_RESUME_IDLE_MS;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
async function maybeAutoCompactLocalHistory(args) {
|
|
431
|
+
const { adapter, dialogId, history } = args;
|
|
432
|
+
const unchanged = () => ({
|
|
433
|
+
history,
|
|
434
|
+
compressed: false,
|
|
435
|
+
summaryGenerated: false
|
|
436
|
+
});
|
|
437
|
+
if (!dialogId || history.length === 0 || typeof adapter.loadDialogSummary !== "function" || typeof adapter.saveDialogSummary !== "function") {
|
|
438
|
+
return unchanged();
|
|
439
|
+
}
|
|
440
|
+
let stored = null;
|
|
441
|
+
try {
|
|
442
|
+
stored = await adapter.loadDialogSummary(dialogId);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.warn("[localLoop] loadDialogSummary failed:", error);
|
|
445
|
+
return unchanged();
|
|
446
|
+
}
|
|
447
|
+
const existingSummary = typeof stored?.summary === "string" ? stored.summary : "";
|
|
448
|
+
const summarizedBeforeId = typeof stored?.summarizedBeforeId === "string" ? stored.summarizedBeforeId : void 0;
|
|
449
|
+
const contextWindow = typeof args.contextWindow === "number" && Number.isFinite(args.contextWindow) && args.contextWindow > 0 ? args.contextWindow : getModelContextWindow(args.model ?? "");
|
|
450
|
+
const allMsgs = toPlanCompressionMessages(history);
|
|
451
|
+
const coldResume = isColdResume(history, args.now?.() ?? Date.now());
|
|
452
|
+
const plan = planCompression({
|
|
453
|
+
allMsgs,
|
|
454
|
+
summarizedBeforeId,
|
|
455
|
+
summary: existingSummary,
|
|
456
|
+
contextWindow,
|
|
457
|
+
...coldResume ? { force: true, reason: "context_budget" } : {}
|
|
458
|
+
});
|
|
459
|
+
const projectExisting = () => {
|
|
460
|
+
if (!existingSummary.trim()) return unchanged();
|
|
461
|
+
return {
|
|
462
|
+
history: projectHistoryWithSummary({
|
|
463
|
+
history,
|
|
464
|
+
summary: existingSummary,
|
|
465
|
+
summarizedBeforeId
|
|
466
|
+
}),
|
|
467
|
+
compressed: true,
|
|
468
|
+
summaryGenerated: false
|
|
469
|
+
};
|
|
470
|
+
};
|
|
471
|
+
if (!plan.shouldCompress) {
|
|
472
|
+
return projectExisting();
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
const provider = await args.resolveProvider();
|
|
476
|
+
const messagesText = formatMessagesForLocalSummary(
|
|
477
|
+
plan.msgsToCompress
|
|
478
|
+
);
|
|
479
|
+
const promptContent = buildLocalAutoCompactionUserContent(
|
|
480
|
+
existingSummary,
|
|
481
|
+
messagesText
|
|
482
|
+
);
|
|
483
|
+
const result = await provider.complete([
|
|
484
|
+
{ role: "system", content: LOCAL_AUTO_COMPACTION_SYSTEM_PROMPT },
|
|
485
|
+
{ role: "user", content: promptContent }
|
|
486
|
+
]);
|
|
487
|
+
const newSummary = typeof result.content === "string" ? result.content.trim() : "";
|
|
488
|
+
if (!newSummary) {
|
|
489
|
+
console.warn(
|
|
490
|
+
"[localLoop] auto-compaction produced empty summary; keeping prior projection"
|
|
491
|
+
);
|
|
492
|
+
return projectExisting();
|
|
493
|
+
}
|
|
494
|
+
await adapter.saveDialogSummary({
|
|
495
|
+
dialogId,
|
|
496
|
+
summary: newSummary,
|
|
497
|
+
summarizedBeforeId: plan.newSummarizedBeforeId
|
|
498
|
+
});
|
|
499
|
+
return {
|
|
500
|
+
history: projectHistoryWithSummary({
|
|
501
|
+
history,
|
|
502
|
+
summary: newSummary,
|
|
503
|
+
summarizedBeforeId: plan.newSummarizedBeforeId
|
|
504
|
+
}),
|
|
505
|
+
compressed: true,
|
|
506
|
+
summaryGenerated: true,
|
|
507
|
+
...result.usage ? { usage: result.usage } : {}
|
|
508
|
+
};
|
|
509
|
+
} catch (error) {
|
|
510
|
+
console.warn("[localLoop] auto-compaction failed:", error);
|
|
511
|
+
return projectExisting();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
276
515
|
// packages/agent-runtime/localLoop.ts
|
|
277
516
|
var LOCAL_AGENT_CONFIG_MISSING_CODE = "LOCAL_AGENT_CONFIG_MISSING";
|
|
278
517
|
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";
|
|
@@ -341,14 +580,43 @@ function buildAbortedError() {
|
|
|
341
580
|
function throwIfAborted(input) {
|
|
342
581
|
if (input.abortSignal?.aborted) throw buildAbortedError();
|
|
343
582
|
}
|
|
583
|
+
async function raceWithAbort(input, promise, pendingToolName) {
|
|
584
|
+
const signal = input.abortSignal;
|
|
585
|
+
if (!signal) return promise;
|
|
586
|
+
let abortListener;
|
|
587
|
+
const abortPromise = new Promise((_resolve, reject) => {
|
|
588
|
+
abortListener = () => {
|
|
589
|
+
const error = buildAbortedError();
|
|
590
|
+
if (pendingToolName) error.pendingToolName = pendingToolName;
|
|
591
|
+
promise.catch(() => {
|
|
592
|
+
});
|
|
593
|
+
reject(error);
|
|
594
|
+
};
|
|
595
|
+
if (signal.aborted) {
|
|
596
|
+
abortListener();
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
600
|
+
});
|
|
601
|
+
try {
|
|
602
|
+
return await Promise.race([promise, abortPromise]);
|
|
603
|
+
} finally {
|
|
604
|
+
if (signal && abortListener) signal.removeEventListener("abort", abortListener);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
344
607
|
function resolveLlmRequestTimeoutMs(input) {
|
|
345
608
|
const raw = input.llmRequestTimeoutMs ?? input.timeoutMs;
|
|
346
609
|
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return void 0;
|
|
347
610
|
return raw;
|
|
348
611
|
}
|
|
349
612
|
async function runCompleteWithTimeout(args) {
|
|
350
|
-
const { provider, messages, options, timeoutMs, round, input } = args;
|
|
351
|
-
emitLoopEvent(input, {
|
|
613
|
+
const { provider, messages, options, timeoutMs, round, input, context } = args;
|
|
614
|
+
emitLoopEvent(input, {
|
|
615
|
+
kind: "llm-start",
|
|
616
|
+
round,
|
|
617
|
+
atMs: Date.now(),
|
|
618
|
+
...context ? { context } : {}
|
|
619
|
+
});
|
|
352
620
|
const complete = provider.complete(messages, options);
|
|
353
621
|
let ok = false;
|
|
354
622
|
const signal = input.abortSignal;
|
|
@@ -511,52 +779,218 @@ function buildActionGate(args) {
|
|
|
511
779
|
toolCallId: args.toolCallId
|
|
512
780
|
};
|
|
513
781
|
}
|
|
514
|
-
var
|
|
515
|
-
|
|
516
|
-
|
|
782
|
+
var TOOL_METADATA_KEYS = [
|
|
783
|
+
"path",
|
|
784
|
+
"query",
|
|
785
|
+
"effectivePattern",
|
|
786
|
+
"startLine",
|
|
787
|
+
"endLine",
|
|
788
|
+
"totalLines",
|
|
789
|
+
"totalBytes",
|
|
790
|
+
"bytes",
|
|
791
|
+
"totalChars",
|
|
792
|
+
"count",
|
|
793
|
+
"matchCount",
|
|
794
|
+
"matchedFiles",
|
|
795
|
+
"truncated",
|
|
796
|
+
"limitedByMaxResults",
|
|
797
|
+
"limitedByMaxDepth",
|
|
798
|
+
"visitedEntries",
|
|
799
|
+
"maxResults",
|
|
800
|
+
"exitCode",
|
|
801
|
+
"status",
|
|
802
|
+
"timedOut",
|
|
803
|
+
"aborted",
|
|
804
|
+
"replacements",
|
|
805
|
+
"code",
|
|
806
|
+
"error",
|
|
807
|
+
"message",
|
|
808
|
+
"pasteId",
|
|
809
|
+
"source"
|
|
810
|
+
];
|
|
811
|
+
function trimHistoryToContextBudget(history, model) {
|
|
812
|
+
if (history.length === 0) return { history, droppedCount: 0 };
|
|
813
|
+
const { rawMessageBudget } = planContextUsage({
|
|
814
|
+
contextWindow: getModelContextWindow(model ?? ""),
|
|
815
|
+
summaryTokens: 0,
|
|
816
|
+
// localLoop 没有 web 端的负载分档器;medium 是中性默认值,
|
|
817
|
+
// 不为了省几个 token 在这里复制一份分类逻辑。
|
|
818
|
+
recentLoad: "medium"
|
|
819
|
+
});
|
|
820
|
+
const messageTokens = (message) => {
|
|
821
|
+
const toolCalls = message.tool_calls;
|
|
822
|
+
return estimateTokenCount(contentAsText(message.content)) + (Array.isArray(toolCalls) ? estimateTokenCount(JSON.stringify(toolCalls)) : 0);
|
|
823
|
+
};
|
|
824
|
+
let used = 0;
|
|
825
|
+
let start = history.length;
|
|
826
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
827
|
+
const cost = messageTokens(history[i]);
|
|
828
|
+
if (used + cost > rawMessageBudget && start < history.length) break;
|
|
829
|
+
used += cost;
|
|
830
|
+
start = i;
|
|
831
|
+
}
|
|
832
|
+
if (start === 0) return { history, droppedCount: 0 };
|
|
833
|
+
return {
|
|
834
|
+
history: sanitizeToolCallPairing(history.slice(start)),
|
|
835
|
+
droppedCount: start
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
function contentAsText(content) {
|
|
839
|
+
if (typeof content === "string") return content;
|
|
840
|
+
if (!Array.isArray(content)) return "";
|
|
841
|
+
return content.map((part) => {
|
|
842
|
+
if (part?.type === "text") return part.text;
|
|
843
|
+
if (part?.type === "image_url") return part.image_url.url;
|
|
844
|
+
return "";
|
|
845
|
+
}).join("\n");
|
|
846
|
+
}
|
|
847
|
+
function contentCharCount(content) {
|
|
848
|
+
if (typeof content === "string") return content.length;
|
|
849
|
+
if (!Array.isArray(content)) return 0;
|
|
850
|
+
return content.reduce((total, part) => {
|
|
851
|
+
if (part?.type === "text") return total + part.text.length;
|
|
852
|
+
if (part?.type === "image_url") return total + part.image_url.url.length;
|
|
853
|
+
return total;
|
|
854
|
+
}, 0);
|
|
855
|
+
}
|
|
856
|
+
function compactToolMetadata(metadata) {
|
|
857
|
+
if (!metadata) return "";
|
|
858
|
+
const selected = {};
|
|
859
|
+
for (const key of TOOL_METADATA_KEYS) {
|
|
860
|
+
const value = metadata[key];
|
|
861
|
+
if (value === void 0) continue;
|
|
862
|
+
if (typeof value === "string") {
|
|
863
|
+
selected[key] = clipCompactText(value, 240);
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
if (Array.isArray(value)) {
|
|
867
|
+
selected[key] = value.slice(0, 20).map(
|
|
868
|
+
(item) => typeof item === "string" ? clipCompactText(item, 180) : clipCompactText(JSON.stringify(item), 180)
|
|
869
|
+
);
|
|
870
|
+
continue;
|
|
871
|
+
}
|
|
872
|
+
selected[key] = value;
|
|
873
|
+
}
|
|
874
|
+
return Object.keys(selected).length > 0 ? clipCompactText(JSON.stringify(selected), 1200) : "";
|
|
875
|
+
}
|
|
876
|
+
function projectToolContentForProvider(args) {
|
|
877
|
+
const content = args.content;
|
|
517
878
|
if (typeof content !== "string") return content;
|
|
518
|
-
|
|
519
|
-
const
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
879
|
+
const metadataText = compactToolMetadata(args.metadata);
|
|
880
|
+
const embeddedMetadataIndex = metadataText ? content.indexOf("\n\n[tool metadata]\n") : -1;
|
|
881
|
+
const contentForProjection = embeddedMetadataIndex >= 0 ? content.slice(0, embeddedMetadataIndex) : content;
|
|
882
|
+
const metadataSuffix = metadataText ? `
|
|
883
|
+
|
|
884
|
+
[tool metadata]
|
|
885
|
+
${metadataText}` : "";
|
|
886
|
+
if (embeddedMetadataIndex >= 0 && content.length <= args.maxChars) {
|
|
887
|
+
return content;
|
|
888
|
+
}
|
|
889
|
+
const headRatio = resolveToolOutputProfile(args.toolName).headRatio;
|
|
890
|
+
const diagnostic = (clippedLength) => `[${args.label}; originalChars=${content.length}; omittedChars=${Math.max(
|
|
891
|
+
0,
|
|
892
|
+
content.length - clippedLength
|
|
893
|
+
)}]`;
|
|
894
|
+
const suffix = (clippedLength) => [diagnostic(clippedLength), metadataSuffix.trimStart()].filter(Boolean).join("\n\n");
|
|
895
|
+
const initialBudget = Math.max(
|
|
896
|
+
1,
|
|
897
|
+
args.maxChars - suffix(contentForProjection.length).length - 2
|
|
898
|
+
);
|
|
899
|
+
let clipped = clipToolText(contentForProjection, initialBudget, headRatio);
|
|
900
|
+
const wasClipped = clipped.length < contentForProjection.trim().length;
|
|
901
|
+
const needsProjection = wasClipped || Boolean(metadataSuffix) || embeddedMetadataIndex >= 0;
|
|
902
|
+
if (!needsProjection) return args.content;
|
|
903
|
+
let projected = wasClipped || metadataSuffix ? `${clipped}
|
|
904
|
+
|
|
905
|
+
${suffix(clipped.length)}` : clipped;
|
|
906
|
+
if (projected.length > args.maxChars) {
|
|
907
|
+
const boundedBudget = Math.max(
|
|
908
|
+
1,
|
|
909
|
+
args.maxChars - suffix(clipped.length).length - 2
|
|
910
|
+
);
|
|
911
|
+
clipped = clipToolText(contentForProjection, boundedBudget, headRatio);
|
|
912
|
+
projected = `${clipped}
|
|
913
|
+
|
|
914
|
+
${suffix(clipped.length)}`;
|
|
915
|
+
}
|
|
916
|
+
return projected.length <= args.maxChars ? projected : projected.slice(0, args.maxChars);
|
|
526
917
|
}
|
|
527
|
-
function summarizeHistoricalToolContent(content) {
|
|
528
|
-
|
|
918
|
+
function summarizeHistoricalToolContent(content, toolName, metadata) {
|
|
919
|
+
const profile = resolveToolOutputProfile(toolName);
|
|
920
|
+
return projectToolContentForProvider({
|
|
529
921
|
content,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
922
|
+
toolName,
|
|
923
|
+
metadata,
|
|
924
|
+
maxChars: Math.min(profile.maxChars, MAX_HISTORICAL_TOOL_CONTENT_CHARS),
|
|
925
|
+
label: "historical tool result truncated for the next turn"
|
|
926
|
+
});
|
|
533
927
|
}
|
|
534
928
|
function prepareMessagesForProviderCall(messages) {
|
|
535
929
|
const paired = sanitizeToolCallPairing(messages);
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
930
|
+
let freshRunStart = paired.length;
|
|
931
|
+
while (freshRunStart > 0 && paired[freshRunStart - 1].role === "tool") {
|
|
932
|
+
freshRunStart -= 1;
|
|
933
|
+
}
|
|
934
|
+
let toolMessageCount = 0;
|
|
935
|
+
let rawToolContentChars = 0;
|
|
936
|
+
let projectedToolContentChars = 0;
|
|
937
|
+
let truncatedToolResults = 0;
|
|
938
|
+
const projected = paired.map((message, index) => {
|
|
939
|
+
const { context_reference: _contextReference, ...providerMessage } = message;
|
|
940
|
+
const sanitizedContent = providerMessage.content == null ? "" : typeof providerMessage.content === "string" ? providerMessage.content : providerMessage.content;
|
|
941
|
+
if (providerMessage.role !== "tool") {
|
|
539
942
|
return {
|
|
540
|
-
...
|
|
943
|
+
...providerMessage,
|
|
541
944
|
content: sanitizedContent
|
|
542
945
|
};
|
|
543
946
|
}
|
|
947
|
+
toolMessageCount += 1;
|
|
948
|
+
rawToolContentChars += contentCharCount(sanitizedContent);
|
|
949
|
+
const isFresh = index >= freshRunStart;
|
|
950
|
+
const maxChars = isFresh ? FRESH_TOOL_OUTPUT_MAX_CHARS : resolveToolOutputProfile(providerMessage.toolName).maxChars;
|
|
951
|
+
const projectedContent = projectToolContentForProvider({
|
|
952
|
+
content: sanitizedContent,
|
|
953
|
+
toolName: providerMessage.toolName,
|
|
954
|
+
metadata: providerMessage.tool_result_metadata,
|
|
955
|
+
maxChars,
|
|
956
|
+
label: "in-turn tool result truncated/projected before next provider call"
|
|
957
|
+
});
|
|
958
|
+
projectedToolContentChars += contentCharCount(projectedContent);
|
|
959
|
+
if (contentCharCount(projectedContent) < contentCharCount(sanitizedContent)) {
|
|
960
|
+
truncatedToolResults += 1;
|
|
961
|
+
}
|
|
544
962
|
return {
|
|
545
|
-
...
|
|
546
|
-
content:
|
|
547
|
-
sanitizedContent,
|
|
548
|
-
MAX_IN_TURN_TOOL_CONTENT_CHARS,
|
|
549
|
-
"in-turn tool result truncated before next provider call"
|
|
550
|
-
)
|
|
963
|
+
...providerMessage,
|
|
964
|
+
content: projectedContent
|
|
551
965
|
};
|
|
552
966
|
});
|
|
967
|
+
return {
|
|
968
|
+
messages: projected,
|
|
969
|
+
metrics: {
|
|
970
|
+
messageCount: projected.length,
|
|
971
|
+
contentChars: projected.reduce((total, message) => total + contentCharCount(message.content), 0),
|
|
972
|
+
toolMessageCount,
|
|
973
|
+
rawToolContentChars,
|
|
974
|
+
projectedToolContentChars,
|
|
975
|
+
truncatedToolResults,
|
|
976
|
+
stableContextChars: 0,
|
|
977
|
+
dynamicContextChars: 0
|
|
978
|
+
}
|
|
979
|
+
};
|
|
553
980
|
}
|
|
554
|
-
function prepareHistoryForNextTurn(history) {
|
|
981
|
+
function prepareHistoryForNextTurn(history, contextReferenceResolver) {
|
|
555
982
|
return history.map((message) => {
|
|
983
|
+
if (message.role === "user" && message.context_reference !== void 0 && contextReferenceResolver?.(message.context_reference)) {
|
|
984
|
+
return { ...message, content: message.context_reference };
|
|
985
|
+
}
|
|
556
986
|
if (message.role !== "tool") return message;
|
|
557
987
|
return {
|
|
558
988
|
...message,
|
|
559
|
-
content: summarizeHistoricalToolContent(
|
|
989
|
+
content: summarizeHistoricalToolContent(
|
|
990
|
+
message.content,
|
|
991
|
+
message.toolName,
|
|
992
|
+
message.tool_result_metadata
|
|
993
|
+
)
|
|
560
994
|
};
|
|
561
995
|
});
|
|
562
996
|
}
|
|
@@ -578,25 +1012,33 @@ function buildMessages(args) {
|
|
|
578
1012
|
if (args.contextBlockScopes?.length) {
|
|
579
1013
|
const blocks2 = args.contextBlockScopes.filter((b) => b.content.trim());
|
|
580
1014
|
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);
|
|
1015
|
+
const dynamicParts = blocks2.filter((b) => b.cacheScope === "turn").map((b) => b.content).map((block) => block.trim()).filter(Boolean);
|
|
582
1016
|
const stableContent = stableParts.join("\n\n");
|
|
583
1017
|
const dynamicContent = dynamicParts.join("\n\n");
|
|
584
1018
|
const systemContent2 = dynamicContent ? `${stableContent}
|
|
585
1019
|
|
|
586
1020
|
${dynamicContent}` : stableContent;
|
|
587
|
-
return
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
1021
|
+
return {
|
|
1022
|
+
messages: [
|
|
1023
|
+
...systemContent2 ? [{ role: "system", content: systemContent2 }] : [],
|
|
1024
|
+
...prepareHistoryForNextTurn(args.history, args.contextReferenceResolver),
|
|
1025
|
+
{ role: "user", content: args.input }
|
|
1026
|
+
],
|
|
1027
|
+
stableContextChars: stableContent.length,
|
|
1028
|
+
dynamicContextChars: dynamicContent.length
|
|
1029
|
+
};
|
|
592
1030
|
}
|
|
593
1031
|
const blocks = (args.contextBlocks ?? []).map((block) => block.trim()).filter(Boolean);
|
|
594
1032
|
const systemContent = [args.prompt?.trim(), ...blocks].filter(Boolean).join("\n\n");
|
|
595
|
-
return
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
1033
|
+
return {
|
|
1034
|
+
messages: [
|
|
1035
|
+
...systemContent ? [{ role: "system", content: systemContent }] : [],
|
|
1036
|
+
...prepareHistoryForNextTurn(args.history, args.contextReferenceResolver),
|
|
1037
|
+
{ role: "user", content: args.input }
|
|
1038
|
+
],
|
|
1039
|
+
stableContextChars: (args.prompt?.trim() ?? "").length,
|
|
1040
|
+
dynamicContextChars: blocks.join("\n\n").length
|
|
1041
|
+
};
|
|
600
1042
|
}
|
|
601
1043
|
function mergeTurnUsage(current, next) {
|
|
602
1044
|
if (!next) return current;
|
|
@@ -619,6 +1061,24 @@ function mergeTurnUsage(current, next) {
|
|
|
619
1061
|
cache_creation_input_tokens: left.cacheMiss + right.cacheMiss
|
|
620
1062
|
};
|
|
621
1063
|
}
|
|
1064
|
+
function addOutOfBandUsage(turn, extra) {
|
|
1065
|
+
if (!extra) return turn;
|
|
1066
|
+
const num = (u, ...keys) => {
|
|
1067
|
+
if (!u) return 0;
|
|
1068
|
+
for (const k of keys) {
|
|
1069
|
+
const v = Number(u[k]);
|
|
1070
|
+
if (Number.isFinite(v) && v !== 0) return v;
|
|
1071
|
+
}
|
|
1072
|
+
return 0;
|
|
1073
|
+
};
|
|
1074
|
+
return {
|
|
1075
|
+
...turn ?? {},
|
|
1076
|
+
input_tokens: num(turn, "input_tokens", "prompt_tokens") + num(extra, "input_tokens", "prompt_tokens"),
|
|
1077
|
+
output_tokens: num(turn, "output_tokens", "completion_tokens") + num(extra, "output_tokens", "completion_tokens"),
|
|
1078
|
+
cache_read_input_tokens: num(turn, "cache_read_input_tokens", "prompt_cache_hit_tokens") + num(extra, "cache_read_input_tokens", "prompt_cache_hit_tokens"),
|
|
1079
|
+
cache_creation_input_tokens: num(turn, "cache_creation_input_tokens", "prompt_cache_miss_tokens") + num(extra, "cache_creation_input_tokens", "prompt_cache_miss_tokens")
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
622
1082
|
function extractUserInputText(content) {
|
|
623
1083
|
if (typeof content === "string") return content;
|
|
624
1084
|
if (!Array.isArray(content)) return "";
|
|
@@ -655,6 +1115,21 @@ async function persistFailedLocalTurn(args) {
|
|
|
655
1115
|
return args.input.continueDialogId;
|
|
656
1116
|
}
|
|
657
1117
|
}
|
|
1118
|
+
function applyPersistedTurnInput(messages, persistedInput, persistedInputReference) {
|
|
1119
|
+
if (persistedInput === void 0 && persistedInputReference === void 0) {
|
|
1120
|
+
return messages;
|
|
1121
|
+
}
|
|
1122
|
+
let replaced = false;
|
|
1123
|
+
return messages.map((message) => {
|
|
1124
|
+
if (replaced || message.role !== "user") return message;
|
|
1125
|
+
replaced = true;
|
|
1126
|
+
return {
|
|
1127
|
+
...message,
|
|
1128
|
+
...persistedInput !== void 0 ? { content: persistedInput } : {},
|
|
1129
|
+
...persistedInputReference !== void 0 ? { context_reference: persistedInputReference } : {}
|
|
1130
|
+
};
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
658
1133
|
async function runLocalAgentTurn(input) {
|
|
659
1134
|
const agentConfig = await input.adapter.loadAgentConfig(input.agentRef);
|
|
660
1135
|
if (!agentConfig) {
|
|
@@ -694,26 +1169,52 @@ async function runLocalAgentTurn(input) {
|
|
|
694
1169
|
const currentTimeScope = [
|
|
695
1170
|
{ content: buildCurrentTimeBlock(/* @__PURE__ */ new Date(), void 0), cacheScope: "turn" }
|
|
696
1171
|
];
|
|
1172
|
+
const callerScopes = normalizeContextBlockScopes(
|
|
1173
|
+
input.contextBlocks,
|
|
1174
|
+
input.contextBlockScopes
|
|
1175
|
+
);
|
|
697
1176
|
const mergedContextBlockScopes = [
|
|
698
1177
|
{ content: identityBlock, cacheScope: "session" },
|
|
699
1178
|
...guidanceScopes,
|
|
700
1179
|
...currentTimeScope,
|
|
701
|
-
...
|
|
1180
|
+
...callerScopes
|
|
702
1181
|
];
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1182
|
+
let resolvedProvider;
|
|
1183
|
+
const resolveProviderOnce = async () => {
|
|
1184
|
+
if (!resolvedProvider) {
|
|
1185
|
+
resolvedProvider = await input.adapter.resolveProvider(agentConfig);
|
|
1186
|
+
}
|
|
1187
|
+
return resolvedProvider;
|
|
1188
|
+
};
|
|
1189
|
+
let compactionUsage;
|
|
1190
|
+
try {
|
|
1191
|
+
const compacted = await maybeAutoCompactLocalHistory({
|
|
1192
|
+
adapter: input.adapter,
|
|
1193
|
+
dialogId: input.continueDialogId,
|
|
1194
|
+
history,
|
|
1195
|
+
model: agentConfig.model,
|
|
1196
|
+
resolveProvider: resolveProviderOnce
|
|
1197
|
+
});
|
|
1198
|
+
history = compacted.history;
|
|
1199
|
+
compactionUsage = compacted.usage;
|
|
1200
|
+
} catch (error) {
|
|
1201
|
+
console.warn("[localLoop] auto-compaction unexpected error:", error);
|
|
1202
|
+
}
|
|
1203
|
+
const trimmedHistory = trimHistoryToContextBudget(history, agentConfig.model);
|
|
1204
|
+
if (trimmedHistory.droppedCount > 0) {
|
|
1205
|
+
history = trimmedHistory.history;
|
|
1206
|
+
}
|
|
1207
|
+
const hasContextBlocks = callerScopes.some((block) => block.content.trim()) || mergedContextBlockScopes.some((block) => block.content.trim());
|
|
708
1208
|
const promptMessageCount = agentConfig.prompt?.trim() || hasContextBlocks ? 1 : 0;
|
|
709
1209
|
const turnStartIndex = promptMessageCount + history.length;
|
|
710
|
-
const
|
|
1210
|
+
const builtMessages = buildMessages({
|
|
711
1211
|
prompt: agentConfig.prompt,
|
|
712
|
-
contextBlocks: input.contextBlocks,
|
|
713
1212
|
contextBlockScopes: mergedContextBlockScopes,
|
|
714
1213
|
history,
|
|
715
|
-
input: input.input
|
|
1214
|
+
input: input.input,
|
|
1215
|
+
contextReferenceResolver: input.adapter.host === "cli" ? input.contextReferenceResolver : void 0
|
|
716
1216
|
});
|
|
1217
|
+
const messages = builtMessages.messages;
|
|
717
1218
|
const supportsImages = resolveAgentImageInputSupport({
|
|
718
1219
|
apiSource: agentConfig.apiSource,
|
|
719
1220
|
provider: agentConfig.provider,
|
|
@@ -732,12 +1233,13 @@ async function runLocalAgentTurn(input) {
|
|
|
732
1233
|
let skipFinalAppend = false;
|
|
733
1234
|
let partialContent = "";
|
|
734
1235
|
try {
|
|
735
|
-
const provider = await
|
|
1236
|
+
const provider = await resolveProviderOnce();
|
|
736
1237
|
while (true) {
|
|
737
1238
|
partialContent = "";
|
|
738
1239
|
throwIfAborted(input);
|
|
1240
|
+
const preparedMessages = prepareMessagesForProviderCall(messages);
|
|
739
1241
|
const baseRequestMessages = filterImagePartsFromMessages(
|
|
740
|
-
|
|
1242
|
+
preparedMessages.messages,
|
|
741
1243
|
supportsImages
|
|
742
1244
|
);
|
|
743
1245
|
if (!supportsImages && !imageDowngradeNotified && messages.some((m) => Array.isArray(m.content) && m.content.some((p) => p?.type === "image_url"))) {
|
|
@@ -749,6 +1251,16 @@ async function runLocalAgentTurn(input) {
|
|
|
749
1251
|
});
|
|
750
1252
|
}
|
|
751
1253
|
const requestMessages = emptyAssistantRepairPending ? [...baseRequestMessages, { role: "user", content: EMPTY_ASSISTANT_REPAIR_PROMPT }] : baseRequestMessages;
|
|
1254
|
+
const contextMetrics = {
|
|
1255
|
+
...preparedMessages.metrics,
|
|
1256
|
+
messageCount: requestMessages.length,
|
|
1257
|
+
contentChars: requestMessages.reduce(
|
|
1258
|
+
(total, message) => total + contentCharCount(message.content),
|
|
1259
|
+
0
|
|
1260
|
+
),
|
|
1261
|
+
stableContextChars: builtMessages.stableContextChars,
|
|
1262
|
+
dynamicContextChars: builtMessages.dynamicContextChars
|
|
1263
|
+
};
|
|
752
1264
|
emptyAssistantRepairPending = false;
|
|
753
1265
|
result = await runCompleteWithTimeout({
|
|
754
1266
|
provider,
|
|
@@ -765,7 +1277,8 @@ async function runLocalAgentTurn(input) {
|
|
|
765
1277
|
},
|
|
766
1278
|
timeoutMs: resolveLlmRequestTimeoutMs(input),
|
|
767
1279
|
round,
|
|
768
|
-
input
|
|
1280
|
+
input,
|
|
1281
|
+
context: contextMetrics
|
|
769
1282
|
});
|
|
770
1283
|
turnUsage = mergeTurnUsage(turnUsage, result.usage);
|
|
771
1284
|
const toolCalls = result.tool_calls ?? [];
|
|
@@ -835,13 +1348,14 @@ async function runLocalAgentTurn(input) {
|
|
|
835
1348
|
argumentsPreview: summarizeToolArguments(toolName, toolCall.function.arguments)
|
|
836
1349
|
});
|
|
837
1350
|
try {
|
|
838
|
-
|
|
1351
|
+
const executePromise = input.adapter.executeTool({
|
|
839
1352
|
id: toolCall.id,
|
|
840
1353
|
name: toolName,
|
|
841
1354
|
arguments: toolCall.function.arguments,
|
|
842
1355
|
...userInputText ? { userInput: userInputText } : {},
|
|
843
1356
|
...input.abortSignal ? { abortSignal: input.abortSignal } : {}
|
|
844
1357
|
});
|
|
1358
|
+
toolResult = await raceWithAbort(input, executePromise, toolName);
|
|
845
1359
|
const actionGate = buildActionGate({
|
|
846
1360
|
toolName,
|
|
847
1361
|
toolCallId: toolCall.id,
|
|
@@ -866,6 +1380,9 @@ async function runLocalAgentTurn(input) {
|
|
|
866
1380
|
});
|
|
867
1381
|
} catch (error) {
|
|
868
1382
|
emitLoopEvent(input, { kind: "tool-end", name: toolName, atMs: Date.now(), ok: false });
|
|
1383
|
+
if (error && typeof error === "object" && error.code === LOCAL_TURN_ABORTED_CODE) {
|
|
1384
|
+
throw error;
|
|
1385
|
+
}
|
|
869
1386
|
if (!shouldReturnToolExecutionErrors(input.adapter)) throw error;
|
|
870
1387
|
emitToolEvent(input, {
|
|
871
1388
|
type: "tool-error",
|
|
@@ -903,7 +1420,11 @@ async function runLocalAgentTurn(input) {
|
|
|
903
1420
|
loopError = error;
|
|
904
1421
|
}
|
|
905
1422
|
if (loopError) {
|
|
906
|
-
const turnMessages2 =
|
|
1423
|
+
const turnMessages2 = applyPersistedTurnInput(
|
|
1424
|
+
messages.slice(turnStartIndex),
|
|
1425
|
+
input.persistedInput,
|
|
1426
|
+
input.persistedInputReference
|
|
1427
|
+
);
|
|
907
1428
|
const dialogId = await persistFailedLocalTurn({
|
|
908
1429
|
adapter: input.adapter,
|
|
909
1430
|
agentKey: agentConfig.key,
|
|
@@ -927,7 +1448,11 @@ async function runLocalAgentTurn(input) {
|
|
|
927
1448
|
...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {}
|
|
928
1449
|
});
|
|
929
1450
|
}
|
|
930
|
-
const turnMessages =
|
|
1451
|
+
const turnMessages = applyPersistedTurnInput(
|
|
1452
|
+
messages.slice(turnStartIndex),
|
|
1453
|
+
input.persistedInput,
|
|
1454
|
+
input.persistedInputReference
|
|
1455
|
+
);
|
|
931
1456
|
const saved = await input.adapter.saveTurn({
|
|
932
1457
|
agentKey: agentConfig.key,
|
|
933
1458
|
messages: turnMessages,
|
|
@@ -945,7 +1470,10 @@ async function runLocalAgentTurn(input) {
|
|
|
945
1470
|
});
|
|
946
1471
|
return {
|
|
947
1472
|
...result,
|
|
948
|
-
...
|
|
1473
|
+
...(() => {
|
|
1474
|
+
const merged = addOutOfBandUsage(turnUsage, compactionUsage);
|
|
1475
|
+
return merged ? { usage: merged } : {};
|
|
1476
|
+
})(),
|
|
949
1477
|
...toolCallCount > 0 ? { toolCallCount } : {},
|
|
950
1478
|
...agentConfig.toolSurface ? { runtimeToolSurface: agentConfig.toolSurface } : {},
|
|
951
1479
|
// 透出最后一轮 provider 调用的 finish_reason;多轮工具循环里只有最后一轮收尾状态有意义。
|
|
@@ -961,6 +1489,7 @@ export {
|
|
|
961
1489
|
parseToolArgumentsJson,
|
|
962
1490
|
summarizeToolArguments,
|
|
963
1491
|
resolveAgentImageInputSupport,
|
|
1492
|
+
projectToolMessageContent,
|
|
964
1493
|
LOCAL_AGENT_CONFIG_MISSING_CODE,
|
|
965
1494
|
EMPTY_ASSISTANT_REPAIR_PROMPT,
|
|
966
1495
|
LOCAL_TURN_ABORTED_CODE,
|