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.
Files changed (123) hide show
  1. package/{_executeModel-ZEDPX6OF.js → _executeModel-4F7VZR5U.js} +52 -50
  2. package/{action-AA4IJ5K2.js → action-OTOWUMPW.js} +8 -9
  3. package/{addContentAction-QYVO4SYP.js → addContentAction-JGBUVR7K.js} +7 -9
  4. package/{addDialogAgentAction-WVVKT5WQ.js → addDialogAgentAction-V6CL2UCF.js} +9 -10
  5. package/{agentDeleteCommand-RDPGYIWF.js → agentDeleteCommand-MMRSG6AB.js} +39 -28
  6. package/{agentEmailCommands-2HJJKS5F.js → agentEmailCommands-YFVKOGM7.js} +65 -62
  7. package/{agentListCommands-HFEVV3ZW.js → agentListCommands-2PZHIQ3I.js} +128 -31
  8. package/{agentMachineCommands-25ZOENJU.js → agentMachineCommands-55UUZNHF.js} +38 -26
  9. package/{agentPullCommand-GOL2S4W2.js → agentPullCommand-KRBRIFIY.js} +7 -7
  10. package/{agentRecordCommands-GTCOAM3L.js → agentRecordCommands-BTP3OAPL.js} +57 -54
  11. package/{agentRunCommand-YBS3JDLD.js → agentRunCommand-AA4QLRQZ.js} +60 -59
  12. package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
  13. package/{agentRuntimeLocal-UQBMQI2M.js → agentRuntimeLocal-QDJCBXM2.js} +31 -15
  14. package/{agentSlice-6EKOBCCS.js → agentSlice-6JAJOU3X.js} +8 -9
  15. package/{authCommand-O2OWACN4.js → authCommand-DMW336GT.js} +15 -15
  16. package/{authCommands-H7365TZP.js → authCommands-GH5T6RYV.js} +6 -6
  17. package/{chunk-E4HNJSHC.js → chunk-2BVGERJ2.js} +81 -19
  18. package/{chunk-LP65VRCI.js → chunk-3MI5LDDU.js} +6 -6
  19. package/{chunk-OPZYGHUC.js → chunk-3WGLLTK4.js} +59 -1
  20. package/{chunk-KJR5KAUQ.js → chunk-3XF6LCCN.js} +20 -7
  21. package/{chunk-PXZAAB4L.js → chunk-4LG74DRG.js} +4 -4
  22. package/{chunk-2FTTDCD4.js → chunk-4ON5AV72.js} +2 -1
  23. package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
  24. package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
  25. package/{chunk-ZPRK73XY.js → chunk-5S4SWV5G.js} +51 -38
  26. package/{chunk-EVZKZF32.js → chunk-5V524MHX.js} +195 -78
  27. package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
  28. package/{chunk-NEM67FX7.js → chunk-ARSC56DC.js} +1 -1
  29. package/{chunk-AFON4QUU.js → chunk-AS3ZQ3O2.js} +19 -28
  30. package/{chunk-4SOKXIW4.js → chunk-B4KT7RN2.js} +10 -13
  31. package/chunk-BYXBJQAS.js +0 -0
  32. package/{chunk-PXQN7IBQ.js → chunk-C3BFP57W.js} +95 -2
  33. package/{chunk-AEZAORLG.js → chunk-CGDQDXKZ.js} +5 -5
  34. package/{chunk-MQI77IRB.js → chunk-DPFWKRHG.js} +2 -2
  35. package/{chunk-TACHTH5D.js → chunk-EIMX5ER5.js} +18 -18
  36. package/{chunk-5MOUAAMK.js → chunk-FSUZIGIG.js} +197 -2578
  37. package/{chunk-FXXSGWAJ.js → chunk-H5Y2H2N6.js} +1 -1
  38. package/chunk-IKXL2HZ3.js +70 -0
  39. package/{chunk-QNMFBBZK.js → chunk-IVAOVLM7.js} +1 -1
  40. package/chunk-JFANOO7I.js +2274 -0
  41. package/{chunk-YPRZSPF3.js → chunk-JKWWVF2I.js} +1 -1
  42. package/{chunk-3NSIQZZ6.js → chunk-JU7J7EAV.js} +45 -45
  43. package/{chunk-X2S7JTIG.js → chunk-KWBH4RAL.js} +1280 -137
  44. package/{chunk-OUI3ZHYD.js → chunk-LUKQQJFL.js} +39 -2
  45. package/{chunk-BX6U3ETE.js → chunk-MG6BVV3T.js} +320 -102
  46. package/{chunk-L53FGHA4.js → chunk-MQRZ5QUH.js} +1 -1
  47. package/{chunk-2W2VYVI5.js → chunk-MZHSAPZP.js} +675 -177
  48. package/{chunk-YOCHZD7D.js → chunk-NCGFB7NN.js} +5 -3
  49. package/{chunk-DWGAEFJL.js → chunk-O4FOPRCP.js} +1 -1
  50. package/chunk-OGGHETUE.js +4900 -0
  51. package/{chunk-PNEW2Z3T.js → chunk-OHEKYYJF.js} +7 -7
  52. package/chunk-OOIN7HSO.js +146 -0
  53. package/{chunk-SV5MWASH.js → chunk-OPM2DE2J.js} +12 -12
  54. package/{chunk-5OIFWVX7.js → chunk-QJSTNKJY.js} +1 -1
  55. package/{chunk-C4FTUXVF.js → chunk-QLUFR47U.js} +11 -1
  56. package/{chunk-LAX2JSTX.js → chunk-QUHUVG6Y.js} +20 -2
  57. package/{chunk-D6OQHXVG.js → chunk-RQSOAMPJ.js} +22 -6
  58. package/{chunk-ZEYDOXHF.js → chunk-RUGEW4P7.js} +1515 -884
  59. package/{chunk-GBDSZATH.js → chunk-RWO5DGXI.js} +3 -3
  60. package/{chunk-S3GS2PRL.js → chunk-TA7VKOHU.js} +613 -70
  61. package/{chunk-XYZA7S7V.js → chunk-TQYYGE6G.js} +6 -6
  62. package/{chunk-YTBMA7NO.js → chunk-TYPHUVK5.js} +70 -8
  63. package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
  64. package/{chunk-C2MDQQ4O.js → chunk-VBSWHE5W.js} +1 -1
  65. package/{chunk-O26GI2JP.js → chunk-VKG4HXYV.js} +39 -8
  66. package/{chunk-IPYUUY4E.js → chunk-WPHL6ALY.js} +1 -1
  67. package/{chunk-7D3JY6C2.js → chunk-WVTBAALN.js} +64 -5
  68. package/{chunk-XFOLQLDN.js → chunk-XHQWJUW4.js} +2 -2
  69. package/{chunk-Q3UKJVU4.js → chunk-Y5P3ZRGC.js} +10 -10
  70. package/{chunk-KQQVGRA3.js → chunk-YUMYYXPI.js} +53 -183
  71. package/cliEnvHelpers-VYC2OPTB.js +30 -0
  72. package/{createAgentAutomationAction-LTI5QU35.js → createAgentAutomationAction-4NY4JLOK.js} +26 -26
  73. package/{createDialogAction-OJJ7L3L3.js → createDialogAction-I5WUWFJ6.js} +26 -26
  74. package/{createPageAction-5MXWVRGY.js → createPageAction-HFTIQ7TW.js} +26 -26
  75. package/{dbSlice-RQFQQVHT.js → dbSlice-J4PUZ6Y5.js} +6 -7
  76. package/{deleteFile-7XP5BGC7.js → deleteFile-M7UNIUOO.js} +4 -4
  77. package/{dialogCommands-JTJ3RPPW.js → dialogCommands-SVXRJFZS.js} +34 -23
  78. package/{dialogSlice-OWROKURJ.js → dialogSlice-35LA3ZRG.js} +26 -26
  79. package/{docCreateCommands-WJBEW2V7.js → docCreateCommands-Z7RIAYKQ.js} +34 -23
  80. package/{docDeleteCommands-BVEHJOY5.js → docDeleteCommands-NEYHUVZF.js} +35 -25
  81. package/{docReadCommands-NEWOZIJV.js → docReadCommands-I4US2SDE.js} +34 -23
  82. package/{docStore-3DXRYBMD.js → docStore-3CP2IO6M.js} +25 -25
  83. package/{docUpdateCommands-C7WHT7UU.js → docUpdateCommands-UU5HQBRP.js} +34 -23
  84. package/fetchMessages-KTNM7QAR.js +1 -1
  85. package/{getPricing-3E6UNPKL.js → getPricing-UGDJHI7M.js} +7 -3
  86. package/{handleSendMessageAction-B5PRR7BE.js → handleSendMessageAction-FWTLQGOZ.js} +30 -30
  87. package/index.js +77 -76
  88. package/{localDialogRead-YSKFFRTT.js → localDialogRead-2VRI24F4.js} +1 -1
  89. package/{machineCommands-IIIREAEK.js → machineCommands-STHZKBDJ.js} +64 -61
  90. package/{machineWsRunDispatch-3SIU6ILW.js → machineWsRunDispatch-7YHT4TPT.js} +56 -53
  91. package/{memoryCommands-O4MIPIWQ.js → memoryCommands-AFMES3LN.js} +5 -5
  92. package/{models-4OPOOLFS.js → models-LSCRJJ6P.js} +2 -2
  93. package/package.json +1 -1
  94. package/{read-QJNKRROM.js → read-2MIMRBZ2.js} +4 -4
  95. package/{readDocTool-H6X6KO6X.js → readDocTool-QHVGQHUJ.js} +1 -1
  96. package/{readlineWorkspace-HGP3I3OC.js → readlineWorkspace-OMRWYMB6.js} +1279 -716
  97. package/{removeDialogAgentAction-TCT35SOF.js → removeDialogAgentAction-XUZYQBV4.js} +13 -13
  98. package/{runAgentClientLoop-EFM4PTVI.js → runAgentClientLoop-FP2K33ZO.js} +47 -46
  99. package/{runtimeDoctorCommands-ID43Z7VX.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
  100. package/{setDialogExtraReferencesAction-CGYLZMFH.js → setDialogExtraReferencesAction-PTAIFKGM.js} +9 -10
  101. package/{setPrimaryDialogAgentAction-F6BFQDJU.js → setPrimaryDialogAgentAction-HRFTDB44.js} +13 -13
  102. package/{settingSlice-2I5HMG47.js → settingSlice-VGRJUSE5.js} +7 -8
  103. package/{skillDiscovery-UGSQGX2U.js → skillDiscovery-6M2BZUFN.js} +3 -3
  104. package/{spaceCommands-MT7BWCIL.js → spaceCommands-GN73BCOB.js} +38 -29
  105. package/{spaceSlice-BS5TSW3W.js → spaceSlice-WTFNKOVA.js} +30 -26
  106. package/{streamAgentChatTurn-I3VBFNR3.js → streamAgentChatTurn-HBAQQVNK.js} +442 -130
  107. package/{toolApiClient-C7P7YSE4.js → toolApiClient-QI3YBNWB.js} +2 -2
  108. package/{tools-XSD2BOEN.js → tools-TPQYTLQZ.js} +34 -33
  109. package/{tuiSummaryLlmCaller-32NRHTT2.js → tuiSummaryLlmCaller-XDC7WG4K.js} +11 -7
  110. package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
  111. package/{updateDialogTitleAction-7FYNJOGP.js → updateDialogTitleAction-AWXNPUQJ.js} +30 -29
  112. package/chunk-AZPCASRE.js +0 -36
  113. package/chunk-BBYVOSU2.js +0 -792
  114. package/chunk-DCOA4QSN.js +0 -105
  115. package/chunk-UAD2WT54.js +0 -64
  116. package/{chunk-JK7VW2FN.js → chunk-5MIDPSSK.js} +3 -3
  117. package/{chunk-5CS33IBL.js → chunk-PDL2AV3E.js} +3 -3
  118. package/{chunk-5MMZUWSG.js → chunk-TOILAPLH.js} +5 -5
  119. package/{chunk-OOM6SJNR.js → chunk-TSUJOM6N.js} +6 -6
  120. package/{db-6QED3ZVO.js → db-HCJ5PRB5.js} +3 -3
  121. package/{offlineMarxistsAgentCommand-BCPJITJL.js → offlineMarxistsAgentCommand-5OOWZLJI.js} +3 -3
  122. package/{tableCommands-M3DJUETS.js → tableCommands-E4SQWDOX.js} +6 -6
  123. 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-OUI3ZHYD.js";
5
+ } from "./chunk-LUKQQJFL.js";
6
6
  import {
7
7
  canonicalizeToolNames
8
- } from "./chunk-4SOKXIW4.js";
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-BBYVOSU2.js";
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 stripped = stripEffortSuffix(model);
88
- if (stripped !== model) candidates.push(stripped);
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) return { kind: "fallback", reason: "stream_truncated" };
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, { kind: "llm-start", round, atMs: Date.now() });
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 MAX_HISTORICAL_TOOL_CONTENT_CHARS = 1600;
515
- var MAX_IN_TURN_TOOL_CONTENT_CHARS = 4e3;
516
- function summarizeToolContentForProvider(content, maxChars, label) {
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
- if (content.length <= maxChars) return content;
519
- const compact = compactWhitespace(content);
520
- const clipped = compact.length > maxChars ? compact.slice(0, maxChars - 160) : compact;
521
- return [
522
- `[${label}]`,
523
- `originalChars=${content.length}`,
524
- clipped
525
- ].join("\n");
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
- return summarizeToolContentForProvider(
931
+ function summarizeHistoricalToolContent(content, toolName, metadata) {
932
+ const profile = resolveToolOutputProfile(toolName);
933
+ return projectToolContentForProvider({
529
934
  content,
530
- MAX_HISTORICAL_TOOL_CONTENT_CHARS,
531
- "historical tool result truncated for the next turn"
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
- return paired.map((message) => {
537
- const sanitizedContent = message.content == null ? "" : typeof message.content === "string" ? message.content : message.content;
538
- if (message.role !== "tool") {
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
- ...message,
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
- ...message,
546
- content: summarizeToolContentForProvider(
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(message.content)
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
- ...systemContent2 ? [{ role: "system", content: systemContent2 }] : [],
589
- ...prepareHistoryForNextTurn(args.history),
590
- { role: "user", content: args.input }
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
- ...systemContent ? [{ role: "system", content: systemContent }] : [],
597
- ...prepareHistoryForNextTurn(args.history),
598
- { role: "user", content: args.input }
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
- ...input.contextBlockScopes ?? []
1193
+ ...callerScopes
702
1194
  ];
703
- const hasContextBlocks = (input.contextBlocks ?? []).some(
704
- (block) => block.trim()
705
- ) || mergedContextBlockScopes.some(
706
- (block) => block.content.trim()
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 messages = buildMessages({
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 input.adapter.resolveProvider(agentConfig);
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
- prepareMessagesForProviderCall(messages),
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
- toolResult = await input.adapter.executeTool({
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 = messages.slice(turnStartIndex);
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 = messages.slice(turnStartIndex);
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
- ...turnUsage ? { usage: turnUsage } : {},
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,