nolo-cli 0.10.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.
Files changed (125) hide show
  1. package/{_executeModel-YAXYAUOK.js → _executeModel-MWAY74HA.js} +47 -44
  2. package/{action-TLI3V62L.js → action-RAXNQQKC.js} +9 -10
  3. package/{addContentAction-PLWJSCOZ.js → addContentAction-ZHI7TVV3.js} +8 -10
  4. package/{addDialogAgentAction-BJ6SB76X.js → addDialogAgentAction-JWXPKXNC.js} +7 -8
  5. package/{agentDeleteCommand-2VYX73U5.js → agentDeleteCommand-LXR5A5FV.js} +36 -26
  6. package/{agentEmailCommands-3GDZIX64.js → agentEmailCommands-DXRAP7BB.js} +63 -58
  7. package/{agentListCommands-UWVBT3LH.js → agentListCommands-ZFMS5ZLY.js} +132 -35
  8. package/{agentMachineCommands-WOKMUG2Y.js → agentMachineCommands-ZJLYVV52.js} +40 -29
  9. package/{agentPullCommand-O2MTUAFR.js → agentPullCommand-KRBRIFIY.js} +6 -6
  10. package/{agentRecordCommands-MVNZRH3U.js → agentRecordCommands-DFS626TB.js} +55 -50
  11. package/{agentRunCommand-K7THIJLA.js → agentRunCommand-GWBIE2TS.js} +60 -56
  12. package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
  13. package/{agentRuntimeLocal-WJIGU24U.js → agentRuntimeLocal-YNZSHMZC.js} +29 -14
  14. package/{agentSlice-LL5TKZEO.js → agentSlice-T2PXOEPQ.js} +9 -10
  15. package/{authCommand-TC2SW2CQ.js → authCommand-DMW336GT.js} +13 -13
  16. package/{authCommands-XPEHTKSM.js → authCommands-GH5T6RYV.js} +6 -6
  17. package/{chunk-5JGGZCZL.js → chunk-3HHTVM6G.js} +64 -5
  18. package/chunk-3QKO4EHV.js +112 -0
  19. package/{chunk-AUQ747NZ.js → chunk-43UD5ZP7.js} +76 -15
  20. package/{chunk-6SLCD4RB.js → chunk-4BTHUYMY.js} +596 -67
  21. package/{chunk-T7R3NGUU.js → chunk-4EH7AR52.js} +13 -13
  22. package/{chunk-C3RUNRHT.js → chunk-4GBNEA5B.js} +1 -1
  23. package/{chunk-4IJYA27C.js → chunk-4LG74DRG.js} +1 -1
  24. package/{chunk-2FTTDCD4.js → chunk-4ON5AV72.js} +2 -1
  25. package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
  26. package/{chunk-EXXFGKF7.js → chunk-5ITRDSHY.js} +1400 -119
  27. package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
  28. package/{chunk-7KNKFIRO.js → chunk-6LMFFG3K.js} +3 -3
  29. package/{chunk-PAKN26FP.js → chunk-6OVCRC72.js} +1 -1
  30. package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
  31. package/{chunk-KBKYAONQ.js → chunk-7MYHLZ3F.js} +2 -2
  32. package/{chunk-PIVFPOW6.js → chunk-AM53Q2SD.js} +175 -2541
  33. package/chunk-BYXBJQAS.js +0 -0
  34. package/{chunk-CL3IOKGK.js → chunk-DE5BA4O6.js} +20 -2
  35. package/{chunk-6KUOUWMP.js → chunk-DRMGJFGQ.js} +300 -89
  36. package/{chunk-QAALPJS7.js → chunk-EBH7G34M.js} +20 -7
  37. package/{chunk-HJGEVL7C.js → chunk-G3B356GF.js} +15 -17
  38. package/{chunk-OZMMKDBI.js → chunk-GDBCUWQ3.js} +26 -20
  39. package/{chunk-I5URP43I.js → chunk-GPZM7QH6.js} +9 -9
  40. package/chunk-IKXL2HZ3.js +70 -0
  41. package/{chunk-AS4PVWA4.js → chunk-IVAOVLM7.js} +1 -1
  42. package/{chunk-TNPIMII7.js → chunk-JUN3HFMN.js} +221 -81
  43. package/{chunk-HFASY3WO.js → chunk-JZDIW766.js} +1 -1
  44. package/{chunk-6JJWFVZ6.js → chunk-LHSJMY5V.js} +17 -17
  45. package/chunk-LTRXGFMS.js +83 -0
  46. package/{chunk-YIHFXH5Q.js → chunk-MEVCDATU.js} +3 -3
  47. package/{chunk-TILFE6LV.js → chunk-NCGFB7NN.js} +5 -3
  48. package/{chunk-BE2OOJ5P.js → chunk-NITP2JDG.js} +42 -11
  49. package/{chunk-YPRZSPF3.js → chunk-OCDEWRE2.js} +1 -1
  50. package/{chunk-VVX233PH.js → chunk-OHEKYYJF.js} +9 -9
  51. package/chunk-OOIN7HSO.js +146 -0
  52. package/{chunk-WPE4F4Z6.js → chunk-OPKKB25T.js} +1 -1
  53. package/{chunk-OR2XR25C.js → chunk-QCA3EPYK.js} +8 -8
  54. package/{chunk-SL35K7AH.js → chunk-QLUFR47U.js} +13 -83
  55. package/{chunk-CDTLMEB3.js → chunk-RNQ5BLR2.js} +1 -1
  56. package/{chunk-AVMKZHBC.js → chunk-RSEUHW6T.js} +3 -2
  57. package/{chunk-TYSCFY6S.js → chunk-RZ735TLX.js} +762 -167
  58. package/chunk-S3UTETIC.js +4885 -0
  59. package/chunk-S73OEOHH.js +2286 -0
  60. package/{chunk-JRKKFRAV.js → chunk-TSKWE74T.js} +58 -8
  61. package/{chunk-A67QFHAA.js → chunk-UG3WOFU6.js} +1 -1
  62. package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
  63. package/chunk-VC6OZXO5.js +298 -0
  64. package/{chunk-7U44LYTR.js → chunk-WPHL6ALY.js} +1 -1
  65. package/{chunk-4SIMLBEU.js → chunk-WTYKCU4W.js} +9 -9
  66. package/{chunk-NPVBXJY2.js → chunk-WWU275LU.js} +9 -9
  67. package/{chunk-ZQ2UM4KG.js → chunk-WZSRQNQG.js} +1 -292
  68. package/{chunk-DSZUWUYB.js → chunk-XHQWJUW4.js} +2 -2
  69. package/{chunk-GAKEBQD6.js → chunk-YDCZVUFP.js} +4 -0
  70. package/{chunk-2O4IZNTG.js → chunk-YTQLJYLB.js} +2 -2
  71. package/{chunk-3Y3HW5PX.js → chunk-ZPFRLGIZ.js} +1512 -935
  72. package/{chunk-5KLGBAAR.js → chunk-ZR4VHQ5D.js} +14 -13
  73. package/cliEnvHelpers-VYC2OPTB.js +30 -0
  74. package/{cliExecutor-DNE7TCC4.js → cliExecutor-K3F7DMRO.js} +2 -2
  75. package/{createAgentAutomationAction-CCE7CWGQ.js → createAgentAutomationAction-G32GKDGV.js} +22 -23
  76. package/{createDialogAction-QTRR4AT7.js → createDialogAction-TISANRZK.js} +25 -25
  77. package/{createPageAction-Y4KQOAJE.js → createPageAction-O7KOYNV4.js} +27 -27
  78. package/{dbSlice-WWNV2SGX.js → dbSlice-WDP2ZZHK.js} +7 -8
  79. package/{deleteFile-OW53A6BX.js → deleteFile-M7UNIUOO.js} +5 -5
  80. package/{dialogCommands-SMKIAC3Y.js → dialogCommands-6JPB3YQY.js} +33 -23
  81. package/{dialogSlice-PT7YBWNE.js → dialogSlice-PRHHYSP5.js} +21 -22
  82. package/{docCreateCommands-RFNA4HNM.js → docCreateCommands-75E6TZNN.js} +36 -25
  83. package/{docDeleteCommands-4YZ325M6.js → docDeleteCommands-EJHLN7LW.js} +37 -27
  84. package/{docReadCommands-RT6VAFFG.js → docReadCommands-RJYBC4FS.js} +36 -25
  85. package/{docStore-QUQZAFZF.js → docStore-H5VWKT5D.js} +22 -23
  86. package/{docUpdateCommands-EKKKNYPW.js → docUpdateCommands-MG4CNISV.js} +36 -25
  87. package/fetchMessages-KTNM7QAR.js +1 -1
  88. package/{getPricing-3E6UNPKL.js → getPricing-6TOZXPAU.js} +7 -3
  89. package/{handleSendMessageAction-5NSYAS53.js → handleSendMessageAction-QTUGICSZ.js} +22 -23
  90. package/index.js +77 -76
  91. package/{machineCommands-DOOSIUKG.js → machineCommands-MAV7M3TK.js} +67 -60
  92. package/{machineWsRunDispatch-PIOIQRBW.js → machineWsRunDispatch-ZOEGTKHC.js} +54 -49
  93. package/{memoryCommands-O4MIPIWQ.js → memoryCommands-AFMES3LN.js} +5 -5
  94. package/{models-4OPOOLFS.js → models-LSCRJJ6P.js} +2 -2
  95. package/package.json +1 -1
  96. package/{read-GW7ZEI23.js → read-2MIMRBZ2.js} +5 -5
  97. package/{readDocTool-HUFU6OCT.js → readDocTool-QHVGQHUJ.js} +1 -1
  98. package/{readlineWorkspace-QEAOH5EP.js → readlineWorkspace-SWQKC7ZS.js} +1126 -678
  99. package/{removeDialogAgentAction-OHMCNSSD.js → removeDialogAgentAction-GTX5AN54.js} +11 -11
  100. package/{runAgentClientLoop-AIGLXC3R.js → runAgentClientLoop-P25HACXX.js} +42 -40
  101. package/{runtimeDoctorCommands-LRHLPDRC.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
  102. package/{setDialogExtraReferencesAction-YJUM5BWQ.js → setDialogExtraReferencesAction-WFQ63KYO.js} +7 -8
  103. package/{setPrimaryDialogAgentAction-EBAREZDM.js → setPrimaryDialogAgentAction-P57FXTSU.js} +11 -11
  104. package/{settingSlice-UW4IUI5L.js → settingSlice-CK5YZGNI.js} +8 -9
  105. package/skillDiscovery-ZYHSX7VD.js +16 -0
  106. package/{spaceCommands-BKHSPR73.js → spaceCommands-6O5EFEM5.js} +41 -33
  107. package/{spaceSlice-TYNWE3OC.js → spaceSlice-4EXTYXS4.js} +25 -24
  108. package/{streamAgentChatTurn-HRMFFA7E.js → streamAgentChatTurn-Q3FJAHG2.js} +205 -107
  109. package/{tableCommands-O7O2LBEG.js → tableCommands-E4SQWDOX.js} +10 -10
  110. package/{tools-3JNDS3WH.js → tools-AHNQE4GP.js} +33 -31
  111. package/{tuiSummaryLlmCaller-RV3THLZF.js → tuiSummaryLlmCaller-TXLF4MEI.js} +8 -6
  112. package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
  113. package/{updateDialogTitleAction-OCETSKCP.js → updateDialogTitleAction-XZDVLKVX.js} +25 -25
  114. package/chunk-5MMAK75Q.js +0 -15
  115. package/chunk-AZPCASRE.js +0 -36
  116. package/chunk-BBYVOSU2.js +0 -792
  117. package/chunk-DCOA4QSN.js +0 -105
  118. package/chunk-UAD2WT54.js +0 -64
  119. package/{chunk-DZ3XJVET.js → chunk-3QAF2LTL.js} +3 -3
  120. package/{chunk-X7W4TNB4.js → chunk-CMKUBIGW.js} +3 -3
  121. package/{chunk-4CQLVGRV.js → chunk-ID4G2MAR.js} +3 -3
  122. package/{chunk-5CS33IBL.js → chunk-PDL2AV3E.js} +3 -3
  123. package/{chunk-EHLHSX7R.js → chunk-TOILAPLH.js} +3 -3
  124. package/{chunk-UTLZINAP.js → chunk-TSUJOM6N.js} +10 -10
  125. package/{offlineMarxistsAgentCommand-BCPJITJL.js → offlineMarxistsAgentCommand-5OOWZLJI.js} +3 -3
@@ -2,20 +2,25 @@ import {
2
2
  buildCurrentTimeBlock,
3
3
  buildIdentityBlock,
4
4
  buildRuntimeGuidanceBlocks
5
- } from "./chunk-PAKN26FP.js";
5
+ } from "./chunk-6OVCRC72.js";
6
6
  import {
7
7
  canonicalizeToolNames
8
- } from "./chunk-AUQ747NZ.js";
8
+ } from "./chunk-43UD5ZP7.js";
9
9
  import {
10
- clipCompactText
11
- } from "./chunk-5MMAK75Q.js";
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-BBYVOSU2.js";
20
+ } from "./chunk-S3UTETIC.js";
16
21
  import {
17
- compactWhitespace
18
- } from "./chunk-DWLEOOGR.js";
22
+ buildDialogSummaryLayer
23
+ } from "./chunk-QLUFR47U.js";
19
24
  import {
20
25
  toErrorMessage
21
26
  } from "./chunk-HNMXYYJR.js";
@@ -25,12 +30,12 @@ import {
25
30
  import {
26
31
  asTrimmedLowercaseString
27
32
  } from "./chunk-QSJ3HFEF.js";
28
- import {
29
- asOptionalTrimmedString
30
- } from "./chunk-G3SUCT4W.js";
31
33
  import {
32
34
  isRecord
33
35
  } from "./chunk-KUG52IAP.js";
36
+ import {
37
+ asOptionalTrimmedString
38
+ } from "./chunk-G3SUCT4W.js";
34
39
 
35
40
  // packages/agent-runtime/actionGate.ts
36
41
  function readCommandActionGatePayload(payload) {
@@ -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, { kind: "llm-start", round, atMs: Date.now() });
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 MAX_HISTORICAL_TOOL_CONTENT_CHARS = 1600;
515
- var MAX_IN_TURN_TOOL_CONTENT_CHARS = 4e3;
516
- function summarizeToolContentForProvider(content, maxChars, label) {
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
- 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");
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
- return summarizeToolContentForProvider(
918
+ function summarizeHistoricalToolContent(content, toolName, metadata) {
919
+ const profile = resolveToolOutputProfile(toolName);
920
+ return projectToolContentForProvider({
529
921
  content,
530
- MAX_HISTORICAL_TOOL_CONTENT_CHARS,
531
- "historical tool result truncated for the next turn"
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
- return paired.map((message) => {
537
- const sanitizedContent = message.content == null ? "" : typeof message.content === "string" ? message.content : message.content;
538
- if (message.role !== "tool") {
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
- ...message,
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
- ...message,
546
- content: summarizeToolContentForProvider(
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(message.content)
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
- ...systemContent2 ? [{ role: "system", content: systemContent2 }] : [],
589
- ...prepareHistoryForNextTurn(args.history),
590
- { role: "user", content: args.input }
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
- ...systemContent ? [{ role: "system", content: systemContent }] : [],
597
- ...prepareHistoryForNextTurn(args.history),
598
- { role: "user", content: args.input }
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
- ...input.contextBlockScopes ?? []
1180
+ ...callerScopes
702
1181
  ];
703
- const hasContextBlocks = (input.contextBlocks ?? []).some(
704
- (block) => block.trim()
705
- ) || mergedContextBlockScopes.some(
706
- (block) => block.content.trim()
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 messages = buildMessages({
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 input.adapter.resolveProvider(agentConfig);
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
- prepareMessagesForProviderCall(messages),
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
- toolResult = await input.adapter.executeTool({
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 = messages.slice(turnStartIndex);
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 = messages.slice(turnStartIndex);
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
- ...turnUsage ? { usage: turnUsage } : {},
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,