@linnlabs/linnkit 0.9.0 → 0.10.0

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 (81) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/linnkit.cjs +7 -0
  3. package/dist/{agentSpec-EkmviZjy.d.cts → agentSpec-Du4Iye0q.d.cts} +16 -1
  4. package/dist/{agentSpec-EkmviZjy.d.ts → agentSpec-Du4Iye0q.d.ts} +16 -1
  5. package/dist/cli.cjs +118 -65
  6. package/dist/cli.cjs.map +1 -1
  7. package/dist/cli.js +118 -65
  8. package/dist/cli.js.map +1 -1
  9. package/dist/context-manager.cjs +234 -32
  10. package/dist/context-manager.cjs.map +1 -1
  11. package/dist/context-manager.d.cts +52 -15
  12. package/dist/context-manager.d.ts +52 -15
  13. package/dist/context-manager.js +234 -33
  14. package/dist/context-manager.js.map +1 -1
  15. package/dist/{context-trace-HE2qY5Q-.d.cts → context-trace-BHKDS-eq.d.cts} +2 -2
  16. package/dist/{context-trace-DRi5M4lX.d.ts → context-trace-CHbqHmyE.d.ts} +2 -2
  17. package/dist/contracts.cjs +3 -1
  18. package/dist/contracts.cjs.map +1 -1
  19. package/dist/contracts.d.cts +3 -3
  20. package/dist/contracts.d.ts +3 -3
  21. package/dist/contracts.js +3 -1
  22. package/dist/contracts.js.map +1 -1
  23. package/dist/{defaultGraphExecutor-BBswR8wn.d.ts → defaultGraphExecutor-B29_qTHy.d.ts} +16 -15
  24. package/dist/{defaultGraphExecutor-BIjJj7WF.d.cts → defaultGraphExecutor-C2E59v_R.d.cts} +16 -15
  25. package/dist/{index-BanRABEt.d.cts → index-BAaUP9yU.d.cts} +26 -14
  26. package/dist/{index-Z8NXKNwI.d.ts → index-BaVpVNi2.d.ts} +26 -14
  27. package/dist/{index-DO4dQgf2.d.cts → index-BnYCS8Zg.d.cts} +2 -2
  28. package/dist/{index-CJeWHopy.d.ts → index-C0DAjsdX.d.ts} +2 -2
  29. package/dist/{index-Dl5PLgAv.d.cts → index-CKQzzZ5Y.d.cts} +2 -2
  30. package/dist/{index-CHqwkvGp.d.ts → index-D0mKxTR5.d.ts} +2 -2
  31. package/dist/index.cjs +186 -85
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +10 -10
  34. package/dist/index.d.ts +10 -10
  35. package/dist/index.js +186 -85
  36. package/dist/index.js.map +1 -1
  37. package/dist/{ports-DnLuKfpE.d.ts → ports-DpPTFhSd.d.ts} +2 -2
  38. package/dist/{ports-DaatKJXp.d.cts → ports-s-tSp3sB.d.cts} +2 -2
  39. package/dist/quickstart.cjs +119 -65
  40. package/dist/quickstart.cjs.map +1 -1
  41. package/dist/quickstart.d.cts +7 -7
  42. package/dist/quickstart.d.ts +7 -7
  43. package/dist/quickstart.js +119 -65
  44. package/dist/quickstart.js.map +1 -1
  45. package/dist/{runAgent-CPj_9e58.d.ts → runAgent-C6F-399C.d.ts} +5 -5
  46. package/dist/{runAgent-HYKlXbVr.d.cts → runAgent-ilEj66Ik.d.cts} +5 -5
  47. package/dist/{runHandle-D3gPsD7B.d.cts → runHandle-BNOqS-Bl.d.cts} +3 -3
  48. package/dist/{runHandle-CyXvzgzk.d.ts → runHandle-BdLXOFqF.d.ts} +3 -3
  49. package/dist/runtime-kernel/events.cjs +1 -0
  50. package/dist/runtime-kernel/events.cjs.map +1 -1
  51. package/dist/runtime-kernel/events.d.cts +4 -4
  52. package/dist/runtime-kernel/events.d.ts +4 -4
  53. package/dist/runtime-kernel/events.js +1 -0
  54. package/dist/runtime-kernel/events.js.map +1 -1
  55. package/dist/runtime-kernel.cjs +181 -82
  56. package/dist/runtime-kernel.cjs.map +1 -1
  57. package/dist/runtime-kernel.d.cts +8 -8
  58. package/dist/runtime-kernel.d.ts +8 -8
  59. package/dist/runtime-kernel.js +181 -83
  60. package/dist/runtime-kernel.js.map +1 -1
  61. package/dist/testkit.cjs +181 -82
  62. package/dist/testkit.cjs.map +1 -1
  63. package/dist/testkit.d.cts +8 -8
  64. package/dist/testkit.d.ts +8 -8
  65. package/dist/testkit.js +181 -82
  66. package/dist/testkit.js.map +1 -1
  67. package/dist/{todo-B1PmDlp3.d.cts → todo-Ca8llpRQ.d.cts} +1 -1
  68. package/dist/{todo-B1PmDlp3.d.ts → todo-Ca8llpRQ.d.ts} +1 -1
  69. package/dist/{toolContracts-CLkQmhTG.d.cts → toolContracts-Bm3EZ1UM.d.cts} +13 -2
  70. package/dist/{toolContracts-Blll0241.d.ts → toolContracts-f8lzZBNa.d.ts} +13 -2
  71. package/docs/integration/README.md +1 -1
  72. package/docs/integration/agent-registration-guide.md +1 -1
  73. package/docs/integration/child-runs.md +4 -1
  74. package/docs/integration/context-engineering.md +30 -15
  75. package/docs/integration/context-fences.md +32 -3
  76. package/docs/integration/llm-provider.md +1 -1
  77. package/docs/integration/persistence.md +1 -0
  78. package/docs/integration/run-supervisor.md +3 -0
  79. package/docs/integration/tool-development-guide.md +7 -5
  80. package/docs/integration/tool-history.md +43 -17
  81. package/package.json +4 -3
@@ -499,6 +499,7 @@ var AgentSpecBudgetPolicy = zod.z.object({
499
499
  });
500
500
  var AgentSpecToolHistoryPolicy = zod.z.object({
501
501
  strategy: zod.z.enum(["per-pair", "per-run", "none"]).optional(),
502
+ retentionMode: zod.z.enum(["drop", "compress"]).optional(),
502
503
  keepLatestToolPairs: zod.z.number().int().nonnegative().optional(),
503
504
  keepLatestRuns: zod.z.number().int().nonnegative().optional(),
504
505
  maxInteractionGroups: zod.z.number().int().nonnegative().optional(),
@@ -613,12 +614,13 @@ var AgentSpecContextPolicy = zod.z.object({
613
614
  var DEFAULT_CONTEXT_POLICY = {
614
615
  profileId: "agent",
615
616
  budget: {
616
- maxTokens: 12e4,
617
+ maxTokens: 232e3,
617
618
  reservedForResponse: 2400,
618
619
  workingMemoryBudgetPercentage: 0.7
619
620
  },
620
621
  toolHistory: {
621
622
  strategy: "per-run",
623
+ retentionMode: "drop",
622
624
  keepLatestToolPairs: 2,
623
625
  keepLatestRuns: 1,
624
626
  maxInteractionGroups: 12,
@@ -1774,6 +1776,157 @@ function findLastIndex(items, predicate) {
1774
1776
  return -1;
1775
1777
  }
1776
1778
 
1779
+ // src/context-manager/shared/preprocessors/currentTurnMessageAssembler.ts
1780
+ var CurrentTurnMessageAssembler = class extends BasePreprocessor {
1781
+ name = "CurrentTurnMessageAssembler";
1782
+ description = "Assembles current-turn fences into system_prompt and user_input messages";
1783
+ priority = 10;
1784
+ fenceRegistry;
1785
+ constructor(options) {
1786
+ super();
1787
+ this.fenceRegistry = options.fenceRegistry;
1788
+ }
1789
+ async process(messages, _context) {
1790
+ const systemResult = assembleSystemPrompt({ messages, fenceRegistry: this.fenceRegistry });
1791
+ const userResult = assembleCurrentUserInput({
1792
+ messages: systemResult.messages,
1793
+ fenceRegistry: this.fenceRegistry
1794
+ });
1795
+ const modifiedCount = systemResult.modifiedCount + userResult.modifiedCount;
1796
+ return this.createResult(
1797
+ messages,
1798
+ userResult.messages,
1799
+ modifiedCount > 0 ? ["current_turn_message_assembly"] : [],
1800
+ modifiedCount
1801
+ );
1802
+ }
1803
+ };
1804
+ function assembleSystemPrompt(params) {
1805
+ const systemIndex = params.messages.findIndex(
1806
+ (message) => message.role === "system" && message.type === "system_prompt"
1807
+ );
1808
+ if (systemIndex === -1) {
1809
+ return { messages: [...params.messages], modifiedCount: 0 };
1810
+ }
1811
+ const fences = collectAdjacentFences({
1812
+ messages: params.messages,
1813
+ startIndex: systemIndex + 1,
1814
+ direction: 1,
1815
+ fenceRegistry: params.fenceRegistry,
1816
+ matches: (descriptor) => descriptor.llmRole === "system" && descriptor.placement === "after-system"
1817
+ });
1818
+ if (fences.length === 0) {
1819
+ return { messages: [...params.messages], modifiedCount: 0 };
1820
+ }
1821
+ const indexesToRemove = new Set(fences.map((fence) => fence.index));
1822
+ const systemPrompt = params.messages[systemIndex];
1823
+ const assembledSystemPrompt = {
1824
+ ...systemPrompt,
1825
+ content: [
1826
+ systemPrompt.content,
1827
+ ...fences.map(formatFence)
1828
+ ].join("\n\n"),
1829
+ metadata: {
1830
+ ...systemPrompt.metadata,
1831
+ assembledFenceKinds: fences.map((fence) => fence.message.metadata?.fenceKind).filter((kind) => typeof kind === "string")
1832
+ }
1833
+ };
1834
+ return {
1835
+ messages: params.messages.map(
1836
+ (message, index) => index === systemIndex ? assembledSystemPrompt : message
1837
+ ).filter((_, index) => !indexesToRemove.has(index)),
1838
+ modifiedCount: fences.length + 1
1839
+ };
1840
+ }
1841
+ function assembleCurrentUserInput(params) {
1842
+ const currentUserIndex = findLastUserInputIndex(params.messages);
1843
+ if (currentUserIndex === -1) {
1844
+ return { messages: [...params.messages], modifiedCount: 0 };
1845
+ }
1846
+ const beforeFences = collectAdjacentFences({
1847
+ messages: params.messages,
1848
+ startIndex: currentUserIndex - 1,
1849
+ direction: -1,
1850
+ fenceRegistry: params.fenceRegistry,
1851
+ matches: (descriptor) => isUserCurrentTurnPlacement(descriptor, "before-current-user")
1852
+ }).reverse();
1853
+ const afterFences = collectAdjacentFences({
1854
+ messages: params.messages,
1855
+ startIndex: currentUserIndex + 1,
1856
+ direction: 1,
1857
+ fenceRegistry: params.fenceRegistry,
1858
+ matches: (descriptor) => isUserCurrentTurnPlacement(descriptor, "after-current-user")
1859
+ });
1860
+ if (beforeFences.length === 0 && afterFences.length === 0) {
1861
+ return { messages: [...params.messages], modifiedCount: 0 };
1862
+ }
1863
+ const indexesToRemove = /* @__PURE__ */ new Set([
1864
+ ...beforeFences.map((fence) => fence.index),
1865
+ ...afterFences.map((fence) => fence.index)
1866
+ ]);
1867
+ const currentUser = params.messages[currentUserIndex];
1868
+ const assembledUser = {
1869
+ ...currentUser,
1870
+ content: [
1871
+ ...beforeFences.map(formatFence),
1872
+ wrapUserRequest(currentUser.content),
1873
+ ...afterFences.map(formatFence)
1874
+ ].join("\n\n"),
1875
+ metadata: {
1876
+ ...currentUser.metadata,
1877
+ assembledFenceKinds: [
1878
+ ...beforeFences.map((fence) => fence.message.metadata?.fenceKind),
1879
+ ...afterFences.map((fence) => fence.message.metadata?.fenceKind)
1880
+ ].filter((kind) => typeof kind === "string")
1881
+ }
1882
+ };
1883
+ return {
1884
+ messages: params.messages.map(
1885
+ (message, index) => index === currentUserIndex ? assembledUser : message
1886
+ ).filter((_, index) => !indexesToRemove.has(index)),
1887
+ modifiedCount: indexesToRemove.size + 1
1888
+ };
1889
+ }
1890
+ function findLastUserInputIndex(messages) {
1891
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1892
+ const message = messages[index];
1893
+ if (message.role === "user" && message.type === "user_input") {
1894
+ return index;
1895
+ }
1896
+ }
1897
+ return -1;
1898
+ }
1899
+ function collectAdjacentFences(params) {
1900
+ const result = [];
1901
+ for (let index = params.startIndex; index >= 0 && index < params.messages.length; index += params.direction) {
1902
+ const message = params.messages[index];
1903
+ if (message.type !== "context_injection") {
1904
+ break;
1905
+ }
1906
+ const fenceKind = message.metadata?.fenceKind;
1907
+ if (!fenceKind) {
1908
+ break;
1909
+ }
1910
+ const descriptor = params.fenceRegistry.get(fenceKind);
1911
+ if (!descriptor || !params.matches(descriptor)) {
1912
+ break;
1913
+ }
1914
+ result.push({ index, message, descriptor });
1915
+ }
1916
+ return result;
1917
+ }
1918
+ function isUserCurrentTurnPlacement(descriptor, placement) {
1919
+ return descriptor.llmRole === "user" && descriptor.placement === placement;
1920
+ }
1921
+ function formatFence(fence) {
1922
+ return fence.descriptor.formatter(fence.message.content, fence.message.metadata?.fenceAttrs ?? {});
1923
+ }
1924
+ function wrapUserRequest(content) {
1925
+ return `<user_request>
1926
+ ${content.trim()}
1927
+ </user_request>`;
1928
+ }
1929
+
1777
1930
  // src/context-manager/shared/providers/base.ts
1778
1931
  var TOOL_HISTORY_OVERFLOW_ERROR_CODE = "TOOL_HISTORY_OVERFLOW";
1779
1932
  var SUMMARIZATION_FAILED_ERROR_CODE = "SUMMARIZATION_FAILED";
@@ -2576,7 +2729,7 @@ __export(config_exports, {
2576
2729
  var AGENT_CONTEXT_BUILDER_CONFIG = {
2577
2730
  // === 绝对Token限制设置 ===
2578
2731
  /** 默认最大Token预算上限 */
2579
- DEFAULT_MAX_TOKENS: 12e4,
2732
+ DEFAULT_MAX_TOKENS: 232e3,
2580
2733
  /** 响应预留Token数 */
2581
2734
  RESERVED_FOR_RESPONSE: 2400,
2582
2735
  // === Agent专用工作记忆填充策略 ===
@@ -3189,6 +3342,7 @@ var ToolPairMatcher = class {
3189
3342
  constructor(config) {
3190
3343
  this.config = config;
3191
3344
  }
3345
+ config;
3192
3346
  /**
3193
3347
  * 判断是否为"预处理阶段压缩的工具历史摘要消息"
3194
3348
  * - 该类消息不再是 tool_calls/tool_output 的原始结构,而是 assistant 的自然语言记录
@@ -3369,6 +3523,7 @@ var ToolPairTruncator = class {
3369
3523
  constructor(config) {
3370
3524
  this.config = config;
3371
3525
  }
3526
+ config;
3372
3527
  /**
3373
3528
  * 智能截断超大的工具对
3374
3529
  *
@@ -3965,7 +4120,7 @@ var AgentWorkingMemoryProvider = class extends BaseContextProvider2 {
3965
4120
  let workingMemoryTokens = coreTokens;
3966
4121
  let processedCount = 0;
3967
4122
  const strategiesApplied = [];
3968
- const workingMemoryBudget = Math.floor(availableBudget * config.WORKING_MEMORY_BUDGET_PERCENTAGE);
4123
+ const workingMemoryBudget = Math.floor(context.totalBudget * config.WORKING_MEMORY_BUDGET_PERCENTAGE);
3969
4124
  const remainingBudget = workingMemoryBudget - coreTokens;
3970
4125
  if (remainingBudget <= 0 && config.MIN_TOOL_INTERACTIONS_TO_KEEP <= 0) {
3971
4126
  this.debug("\u26A0\uFE0F \u6838\u5FC3\u4E0A\u4E0B\u6587\u5DF2\u7528\u5C3D\u9884\u7B97\uFF0C\u8DF3\u8FC7\u5DE5\u4F5C\u8BB0\u5FC6\u586B\u5145", {
@@ -4100,6 +4255,7 @@ var Logger = class {
4100
4255
  constructor(moduleName) {
4101
4256
  this.moduleName = moduleName;
4102
4257
  }
4258
+ moduleName;
4103
4259
  debug(message, data) {
4104
4260
  this.log(0 /* DEBUG */, "debug", message, data);
4105
4261
  }
@@ -4347,6 +4503,7 @@ var DefaultTokenizerPort = class {
4347
4503
  constructor(config = {}) {
4348
4504
  this.config = config;
4349
4505
  }
4506
+ config;
4350
4507
  estimateText(text, _modelId) {
4351
4508
  return TokenCalculator.estimateTokens(text, {
4352
4509
  encoding: this.config.encoding,
@@ -5106,6 +5263,7 @@ __export(orchestration_exports, {
5106
5263
  var preprocessors_exports = {};
5107
5264
  __export(preprocessors_exports, {
5108
5265
  BasePreprocessor: () => BasePreprocessor2,
5266
+ CurrentTurnMessageAssembler: () => CurrentTurnMessageAssembler,
5109
5267
  FenceLifetimePreprocessor: () => FenceLifetimePreprocessor,
5110
5268
  HistoryPurificationPreprocessor: () => HistoryPurificationPreprocessor,
5111
5269
  PreprocessorPipeline: () => PreprocessorPipeline,
@@ -5157,7 +5315,7 @@ var DEFAULT_KEEP_LATEST_RUNS = 1;
5157
5315
  var DEFAULT_MAX_INTERACTION_GROUPS = 12;
5158
5316
  var ToolHistoryCompressorPreprocessor = class extends BasePreprocessor2 {
5159
5317
  name = "ToolHistoryCompressorPreprocessor";
5160
- description = "\u5DE5\u5177\u5386\u53F2\u538B\u7F29\u5904\u7406\u5668 - \u5C06\u8F83\u65E9\u7684\u5386\u53F2\u5DE5\u5177\u8C03\u7528\u5BF9\u538B\u7F29\u4E3A\u81EA\u7136\u8BED\u8A00\u8BB0\u5F55\u6D88\u606F";
5318
+ description = "\u5DE5\u5177\u5386\u53F2\u4FDD\u7559\u5904\u7406\u5668 - \u6309\u7B56\u7565\u4FDD\u7559\u3001\u5220\u9664\u6216\u538B\u7F29\u8F83\u65E9\u7684\u5386\u53F2\u5DE5\u5177\u8C03\u7528\u5BF9";
5161
5319
  priority = 0;
5162
5320
  summarizer = createDefaultToolOutputSummarizer();
5163
5321
  options;
@@ -5165,6 +5323,7 @@ var ToolHistoryCompressorPreprocessor = class extends BasePreprocessor2 {
5165
5323
  super();
5166
5324
  this.options = {
5167
5325
  strategy: options.strategy ?? "per-run",
5326
+ retentionMode: options.retentionMode ?? "drop",
5168
5327
  keepLatestToolPairs: normalizeNonNegativeInteger4(
5169
5328
  options.keepLatestToolPairs,
5170
5329
  DEFAULT_KEEP_LATEST_TOOL_PAIRS
@@ -5181,7 +5340,7 @@ var ToolHistoryCompressorPreprocessor = class extends BasePreprocessor2 {
5181
5340
  };
5182
5341
  }
5183
5342
  async process(messages, context) {
5184
- this.debug("\u{1F527} \u5F00\u59CB\u5DE5\u5177\u5386\u53F2\u538B\u7F29\u5904\u7406", {
5343
+ this.debug("\u{1F527} \u5F00\u59CB\u5DE5\u5177\u5386\u53F2\u4FDD\u7559\u5904\u7406", {
5185
5344
  \u539F\u59CB\u6D88\u606F\u6570: messages.length
5186
5345
  }, context);
5187
5346
  const currentRunStartIndex = findCurrentRunStartIndex(messages);
@@ -5191,17 +5350,18 @@ var ToolHistoryCompressorPreprocessor = class extends BasePreprocessor2 {
5191
5350
  \u5386\u53F2\u6D88\u606F\u6570: historyMessages.length,
5192
5351
  \u5F53\u524D\u8F6E\u6B21\u6D88\u606F\u6570: currentRunMessages.length,
5193
5352
  \u5F53\u524D\u8F6E\u6B21\u8D77\u70B9\u7D22\u5F15: currentRunStartIndex,
5194
- \u5DE5\u5177\u538B\u7F29\u7B56\u7565: this.options.strategy
5353
+ \u5DE5\u5177\u4FDD\u7559\u7B56\u7565: this.options.strategy,
5354
+ \u5386\u53F2\u5DE5\u5177\u4FDD\u7559\u6A21\u5F0F: this.options.retentionMode
5195
5355
  }, context);
5196
- const compressedHistory = this.compressToolCallPairsInHistory(historyMessages, context);
5197
- const finalMessages = [...compressedHistory, ...currentRunMessages];
5356
+ const processedHistory = this.processToolCallPairsInHistory(historyMessages, context);
5357
+ const finalMessages = [...processedHistory, ...currentRunMessages];
5198
5358
  const originalCount = messages.length;
5199
- const compressedCount = finalMessages.length;
5200
- const removedCount = originalCount - compressedCount;
5201
- const appliedStrategies = removedCount > 0 ? ["tool_history_compression"] : [];
5202
- this.debug("\u2705 \u5DE5\u5177\u5386\u53F2\u538B\u7F29\u5B8C\u6210", {
5359
+ const processedCount = finalMessages.length;
5360
+ const removedCount = originalCount - processedCount;
5361
+ const appliedStrategies = removedCount > 0 ? [this.options.retentionMode === "compress" ? "tool_history_compression" : "tool_history_drop"] : [];
5362
+ this.debug("\u2705 \u5DE5\u5177\u5386\u53F2\u4FDD\u7559\u5904\u7406\u5B8C\u6210", {
5203
5363
  \u539F\u59CB\u6D88\u606F: originalCount,
5204
- \u538B\u7F29\u540E\u6D88\u606F: compressedCount,
5364
+ \u5904\u7406\u540E\u6D88\u606F: processedCount,
5205
5365
  \u51CF\u5C11\u6D88\u606F: removedCount,
5206
5366
  Token\u8282\u7701\u4F30\u8BA1: `\u7EA6${removedCount * 50}\u4E2AToken`
5207
5367
  }, context);
@@ -5222,7 +5382,7 @@ var ToolHistoryCompressorPreprocessor = class extends BasePreprocessor2 {
5222
5382
  }
5223
5383
  return false;
5224
5384
  }
5225
- compressToolCallPairsInHistory(historyMessages, context) {
5385
+ processToolCallPairsInHistory(historyMessages, context) {
5226
5386
  if (historyMessages.length < 2) {
5227
5387
  return historyMessages;
5228
5388
  }
@@ -5243,8 +5403,10 @@ var ToolHistoryCompressorPreprocessor = class extends BasePreprocessor2 {
5243
5403
  if (!group.isComplete || keepAnchorIds.has(group.anchorId)) {
5244
5404
  continue;
5245
5405
  }
5246
- const compressedMessage = this.compressToolInteractionGroup(group, context);
5247
- replacementMap.set(group.assistantIndex, compressedMessage);
5406
+ if (this.options.retentionMode === "compress") {
5407
+ const compressedMessage = this.compressToolInteractionGroup(group, context);
5408
+ replacementMap.set(group.assistantIndex, compressedMessage);
5409
+ }
5248
5410
  for (const messageIndex of group.messageIndexes) {
5249
5411
  messagesToRemove.add(messageIndex);
5250
5412
  }
@@ -5663,6 +5825,7 @@ function createDefaultAgentPreprocessorRegistry(options = {}) {
5663
5825
  registry.register(new ToolReplayProtocolGuardPreprocessor({ policy: options.providerReplay }));
5664
5826
  registry.register(new HistoryPurificationPreprocessor({ logPrefix: "Agent-HistoryPurification" }));
5665
5827
  if (options.fenceRegistry) {
5828
+ registry.register(new CurrentTurnMessageAssembler({ fenceRegistry: options.fenceRegistry }));
5666
5829
  registry.register(new FenceLifetimePreprocessor({ fenceRegistry: options.fenceRegistry }));
5667
5830
  }
5668
5831
  return registry;
@@ -6778,6 +6941,18 @@ var AgentMessageOrchestrator = class {
6778
6941
  tokenizer: this.options.tokenizer,
6779
6942
  tokenizerModelId: this.resolvePreprocessorModel(request)
6780
6943
  });
6944
+ return contextBuilderConfig;
6945
+ }
6946
+ resolveEffectiveContextBudget(contextBuilderConfig) {
6947
+ const maxTokens = contextBuilderConfig.DEFAULT_MAX_TOKENS ?? this.options.tokenBudget.maxTokens;
6948
+ const reservedForResponse = contextBuilderConfig.RESERVED_FOR_RESPONSE ?? this.options.tokenBudget.reservedForResponse;
6949
+ return {
6950
+ maxTokens,
6951
+ reservedForResponse,
6952
+ // 中文备注:ContextManager 接收的是“可放入上下文的输入预算”,不是模型完整窗口。
6953
+ // 因此单个 agent 通过 contextPolicy.budget 覆盖预算时,这里必须同步使用覆盖后的值。
6954
+ totalBudget: maxTokens - reservedForResponse
6955
+ };
6781
6956
  }
6782
6957
  async processAgentConversation(request, history, toolManager, callbacks, extraOptions) {
6783
6958
  const historyCount = history.length;
@@ -6796,7 +6971,8 @@ var AgentMessageOrchestrator = class {
6796
6971
  const allMessages = this.buildCompleteMessageList(request, historyMessages);
6797
6972
  this.debug("Built complete message list", { totalCount: allMessages.length });
6798
6973
  const contextPolicy = this.resolveContextPolicy(request);
6799
- this.applyContextPolicy(request, contextPolicy);
6974
+ const contextBuilderConfig = this.applyContextPolicy(request, contextPolicy);
6975
+ const effectiveContextBudget = this.resolveEffectiveContextBudget(contextBuilderConfig);
6800
6976
  const preprocessorPipeline = this.buildPreprocessorPipelineForRequest(toolManager, request, contextPolicy);
6801
6977
  const modelId = this.resolvePreprocessorModel(request);
6802
6978
  preprocessorPipeline.updateContext({
@@ -6824,7 +7000,8 @@ var AgentMessageOrchestrator = class {
6824
7000
  callbacks,
6825
7001
  void 0,
6826
7002
  extraOptions?.generate,
6827
- contextPolicy
7003
+ contextPolicy,
7004
+ effectiveContextBudget.totalBudget
6828
7005
  );
6829
7006
  this.debug("Context built", { afterContextCount: contextResult.messages.length });
6830
7007
  if (this.options.processing.debugMode) {
@@ -6857,7 +7034,7 @@ var AgentMessageOrchestrator = class {
6857
7034
  processedCount: contextResult.messages.length,
6858
7035
  tokenUsage: {
6859
7036
  estimated: contextResult.tokenUsage.used,
6860
- budget: this.options.tokenBudget.maxTokens,
7037
+ budget: effectiveContextBudget.totalBudget,
6861
7038
  remaining: contextResult.tokenUsage.remaining
6862
7039
  },
6863
7040
  processingStats: contextResult.processingStats,
@@ -6877,13 +7054,13 @@ var AgentMessageOrchestrator = class {
6877
7054
  async runPreprocessorPipeline(preprocessorPipeline, messages) {
6878
7055
  return preprocessorPipeline.process(messages);
6879
7056
  }
6880
- async buildContextFromPreprocessedMessages(request, conversationSession, messages, callbacks, phaseOverride, generate, contextPolicy) {
6881
- const totalBudget = this.options.tokenBudget.maxTokens - this.options.tokenBudget.reservedForResponse;
7057
+ async buildContextFromPreprocessedMessages(request, conversationSession, messages, callbacks, phaseOverride, generate, contextPolicy, totalBudget) {
7058
+ const resolvedTotalBudget = totalBudget ?? this.options.tokenBudget.maxTokens - this.options.tokenBudget.reservedForResponse;
6882
7059
  const contextResult = await this.agentContextManager.buildContextFromPreprocessedMessages(
6883
7060
  request,
6884
7061
  conversationSession,
6885
7062
  messages,
6886
- totalBudget,
7063
+ resolvedTotalBudget,
6887
7064
  callbacks,
6888
7065
  phaseOverride,
6889
7066
  generate,
@@ -6964,22 +7141,33 @@ var BaseAgentTask = class {
6964
7141
  });
6965
7142
  }
6966
7143
  messages.push(...fenceMessages["after-system"]);
7144
+ const currentUserIndex = findCurrentUserInputIndex(history, request.query);
7145
+ const historyBeforeCurrentUser = currentUserIndex === -1 ? history : history.slice(0, currentUserIndex);
7146
+ const currentUserMessage = currentUserIndex === -1 ? this.createCurrentUserMessage(request.query) : history[currentUserIndex];
7147
+ const historyAfterCurrentUser = currentUserIndex === -1 ? [] : history.slice(currentUserIndex + 1);
7148
+ messages.push(...historyBeforeCurrentUser);
6967
7149
  messages.push(...fenceMessages["before-current-user"]);
6968
- const hasUserMessageInHistory = history.some((m) => m.role === "user");
6969
- if (!hasUserMessageInHistory && request.query && request.query.trim()) {
6970
- messages.push({
6971
- id: generateMessageId(),
6972
- role: "user",
6973
- type: "user_input",
6974
- content: request.query.trim(),
6975
- timestamp: Date.now()
6976
- });
7150
+ if (currentUserMessage) {
7151
+ messages.push(currentUserMessage);
6977
7152
  }
6978
7153
  messages.push(...fenceMessages["after-current-user"]);
6979
- messages.push(...history);
7154
+ messages.push(...historyAfterCurrentUser);
6980
7155
  this.insertAfterLastToolResult(messages, fenceMessages["after-last-tool-result"]);
6981
7156
  return messages;
6982
7157
  }
7158
+ createCurrentUserMessage(query) {
7159
+ const content = query.trim();
7160
+ if (!content) {
7161
+ return null;
7162
+ }
7163
+ return {
7164
+ id: generateMessageId(),
7165
+ role: "user",
7166
+ type: "user_input",
7167
+ content,
7168
+ timestamp: Date.now()
7169
+ };
7170
+ }
6983
7171
  createFenceMessages(fences) {
6984
7172
  const grouped = {
6985
7173
  "after-system": [],
@@ -7044,6 +7232,19 @@ function findLastIndex2(items, predicate) {
7044
7232
  }
7045
7233
  return -1;
7046
7234
  }
7235
+ function findCurrentUserInputIndex(history, query) {
7236
+ const normalizedQuery = query.trim();
7237
+ if (!normalizedQuery) {
7238
+ return -1;
7239
+ }
7240
+ for (let index = history.length - 1; index >= 0; index -= 1) {
7241
+ const message = history[index];
7242
+ if (message.role === "user" && message.type === "user_input" && message.content.trim() === normalizedQuery) {
7243
+ return index;
7244
+ }
7245
+ }
7246
+ return -1;
7247
+ }
7047
7248
 
7048
7249
  // src/context-manager/profiles/agent/tools/index.ts
7049
7250
  var tools_exports2 = {};
@@ -8657,6 +8858,7 @@ exports.ChatWorkingMemoryProvider = WorkingMemoryProvider;
8657
8858
  exports.ContextProviderError = ContextProviderError;
8658
8859
  exports.ContextProviderRegistry = ContextProviderRegistry;
8659
8860
  exports.ContextTraceCollector = ContextTraceCollector;
8861
+ exports.CurrentTurnMessageAssembler = CurrentTurnMessageAssembler;
8660
8862
  exports.DEFAULT_MUST_KEEP_POLICY = DEFAULT_MUST_KEEP_POLICY;
8661
8863
  exports.FenceLifetimePreprocessor = FenceLifetimePreprocessor;
8662
8864
  exports.HistoryPurificationPreprocessor = HistoryPurificationPreprocessor;