@librechat/agents 3.7.15 → 3.7.17

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/dist/cjs/agents/AgentContext.cjs +91 -0
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/agents/projection.cjs +2 -1
  4. package/dist/cjs/agents/projection.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +47 -6
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +20 -7
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +1 -1
  10. package/dist/cjs/llm/openai/utils/index.cjs +1 -1
  11. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  12. package/dist/cjs/llm/preempt.cjs +1 -10
  13. package/dist/cjs/llm/preempt.cjs.map +1 -1
  14. package/dist/cjs/main.cjs +23 -3
  15. package/dist/cjs/messages/contextPruning.cjs +1 -1
  16. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  17. package/dist/cjs/messages/core.cjs +29 -1
  18. package/dist/cjs/messages/core.cjs.map +1 -1
  19. package/dist/cjs/messages/fading.cjs +122 -0
  20. package/dist/cjs/messages/fading.cjs.map +1 -0
  21. package/dist/cjs/messages/format.cjs +1 -1
  22. package/dist/cjs/messages/index.cjs +1 -0
  23. package/dist/cjs/messages/prune.cjs +364 -236
  24. package/dist/cjs/messages/prune.cjs.map +1 -1
  25. package/dist/cjs/run.cjs +27 -0
  26. package/dist/cjs/run.cjs.map +1 -1
  27. package/dist/cjs/session/AgentSession.cjs +213 -8
  28. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  29. package/dist/cjs/session/JsonlSessionStore.cjs +41 -0
  30. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
  31. package/dist/cjs/session/index.cjs +1 -1
  32. package/dist/cjs/stream.cjs +1 -1
  33. package/dist/cjs/summarization/node.cjs +1 -1
  34. package/dist/cjs/tools/ToolNode.cjs +1 -1
  35. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +2 -0
  36. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  37. package/dist/cjs/tools/subagent/SubagentReplay.cjs +2 -1
  38. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  39. package/dist/cjs/utils/errors.cjs +3 -1
  40. package/dist/cjs/utils/errors.cjs.map +1 -1
  41. package/dist/cjs/utils/index.cjs +1 -1
  42. package/dist/cjs/utils/truncation.cjs +9 -0
  43. package/dist/cjs/utils/truncation.cjs.map +1 -1
  44. package/dist/esm/agents/AgentContext.mjs +92 -1
  45. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  46. package/dist/esm/agents/projection.mjs +2 -1
  47. package/dist/esm/agents/projection.mjs.map +1 -1
  48. package/dist/esm/graphs/Graph.mjs +47 -6
  49. package/dist/esm/graphs/Graph.mjs.map +1 -1
  50. package/dist/esm/graphs/MultiAgentGraph.mjs +20 -7
  51. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  52. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +1 -1
  53. package/dist/esm/llm/openai/utils/index.mjs +2 -2
  54. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  55. package/dist/esm/llm/preempt.mjs +1 -10
  56. package/dist/esm/llm/preempt.mjs.map +1 -1
  57. package/dist/esm/main.mjs +6 -5
  58. package/dist/esm/messages/contextPruning.mjs +1 -1
  59. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  60. package/dist/esm/messages/core.mjs +29 -2
  61. package/dist/esm/messages/core.mjs.map +1 -1
  62. package/dist/esm/messages/fading.mjs +108 -0
  63. package/dist/esm/messages/fading.mjs.map +1 -0
  64. package/dist/esm/messages/format.mjs +1 -1
  65. package/dist/esm/messages/index.mjs +1 -0
  66. package/dist/esm/messages/prune.mjs +363 -235
  67. package/dist/esm/messages/prune.mjs.map +1 -1
  68. package/dist/esm/run.mjs +27 -0
  69. package/dist/esm/run.mjs.map +1 -1
  70. package/dist/esm/session/AgentSession.mjs +213 -8
  71. package/dist/esm/session/AgentSession.mjs.map +1 -1
  72. package/dist/esm/session/JsonlSessionStore.mjs +41 -0
  73. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
  74. package/dist/esm/session/index.mjs +1 -1
  75. package/dist/esm/stream.mjs +1 -1
  76. package/dist/esm/summarization/node.mjs +1 -1
  77. package/dist/esm/tools/ToolNode.mjs +1 -1
  78. package/dist/esm/tools/subagent/SubagentExecutor.mjs +2 -0
  79. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  80. package/dist/esm/tools/subagent/SubagentReplay.mjs +2 -1
  81. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  82. package/dist/esm/utils/errors.mjs +3 -1
  83. package/dist/esm/utils/errors.mjs.map +1 -1
  84. package/dist/esm/utils/index.mjs +1 -1
  85. package/dist/esm/utils/truncation.mjs +7 -1
  86. package/dist/esm/utils/truncation.mjs.map +1 -1
  87. package/dist/types/agents/AgentContext.d.ts +32 -1
  88. package/dist/types/agents/projection.d.ts +3 -1
  89. package/dist/types/graphs/Graph.d.ts +14 -1
  90. package/dist/types/graphs/MultiAgentGraph.d.ts +7 -0
  91. package/dist/types/messages/contextPruning.d.ts +2 -0
  92. package/dist/types/messages/core.d.ts +2 -0
  93. package/dist/types/messages/fading.d.ts +81 -0
  94. package/dist/types/messages/index.d.ts +1 -0
  95. package/dist/types/messages/prune.d.ts +71 -24
  96. package/dist/types/run.d.ts +16 -0
  97. package/dist/types/session/AgentSession.d.ts +15 -0
  98. package/dist/types/session/JsonlSessionStore.d.ts +4 -1
  99. package/dist/types/session/types.d.ts +9 -0
  100. package/dist/types/tools/subagent/SubagentReplay.d.ts +3 -1
  101. package/dist/types/types/graph.d.ts +23 -0
  102. package/dist/types/types/run.d.ts +10 -0
  103. package/dist/types/utils/truncation.d.ts +9 -0
  104. package/package.json +1 -1
  105. package/src/agents/AgentContext.ts +175 -1
  106. package/src/agents/projection.ts +4 -0
  107. package/src/graphs/Graph.ts +97 -11
  108. package/src/graphs/MultiAgentGraph.ts +54 -6
  109. package/src/llm/openai/utils/index.ts +3 -1
  110. package/src/llm/preempt.ts +4 -17
  111. package/src/messages/contextPruning.ts +7 -1
  112. package/src/messages/core.ts +52 -0
  113. package/src/messages/fading.ts +301 -0
  114. package/src/messages/index.ts +1 -0
  115. package/src/messages/prune.ts +719 -507
  116. package/src/run.ts +52 -0
  117. package/src/session/AgentSession.ts +397 -8
  118. package/src/session/JsonlSessionStore.ts +71 -0
  119. package/src/session/types.ts +10 -0
  120. package/src/tools/subagent/SubagentExecutor.ts +6 -0
  121. package/src/tools/subagent/SubagentReplay.ts +15 -2
  122. package/src/types/graph.ts +25 -0
  123. package/src/types/run.ts +10 -0
  124. package/src/utils/errors.ts +17 -1
  125. package/src/utils/truncation.ts +25 -0
@@ -1,8 +1,10 @@
1
+ const require_truncation = require("../utils/truncation.cjs");
2
+ const require_fading = require("./fading.cjs");
1
3
  require("../common/enum.cjs");
2
4
  require("../common/index.cjs");
3
- const require_truncation = require("../utils/truncation.cjs");
4
5
  const require_toolContent = require("../utils/toolContent.cjs");
5
6
  const require_langchain = require("./langchain.cjs");
7
+ const require_cache = require("./cache.cjs");
6
8
  const require_core = require("./core.cjs");
7
9
  const require_tokens = require("../utils/tokens.cjs");
8
10
  const require_providerRegistry = require("../llm/providerRegistry.cjs");
@@ -29,14 +31,6 @@ function prependItems(target, prefix) {
29
31
  }
30
32
  const DEFAULT_RESERVE_RATIO = .05;
31
33
  const REPLY_PRIMER_TOKENS = 3;
32
- const PRESSURE_THRESHOLD_MASKING = .8;
33
- const PRESSURE_BANDS = [
34
- [.99, .05],
35
- [.9, .2],
36
- [.85, .5],
37
- [.8, 1]
38
- ];
39
- const MASKED_RESULT_MAX_CHARS = 300;
40
34
  const ORIGINAL_CONTENT_MAX_CHARS = 2e6;
41
35
  function enforceOriginalContentCap(map) {
42
36
  let total = 0;
@@ -59,17 +53,30 @@ function getToolCallIds(message) {
59
53
  if (message.getType() !== "ai" && messageRole !== "assistant") return /* @__PURE__ */ new Set();
60
54
  const ids = /* @__PURE__ */ new Set();
61
55
  const aiMessage = message;
62
- for (const toolCall of aiMessage.tool_calls ?? []) if (typeof toolCall.id === "string" && toolCall.id.length > 0) ids.add(toolCall.id);
63
- if (Array.isArray(aiMessage.content)) for (const part of aiMessage.content) {
56
+ const toolCalls = aiMessage.tool_calls;
57
+ if (Array.isArray(toolCalls) && !(0, node_util_types.isProxy)(toolCalls)) for (const toolCall of toolCalls) {
58
+ if (typeof toolCall !== "object") continue;
59
+ const id = getStringProperty(toolCall, "id");
60
+ if (id != null && id.length > 0) ids.add(id);
61
+ }
62
+ const rawToolCalls = readPropertyWithoutAccessors(aiMessage.additional_kwargs, "tool_calls");
63
+ if (!rawToolCalls.accessor && Array.isArray(rawToolCalls.value) && !(0, node_util_types.isProxy)(rawToolCalls.value)) for (const toolCall of rawToolCalls.value) {
64
+ if (toolCall == null || typeof toolCall !== "object") continue;
65
+ const id = getStringProperty(toolCall, "id");
66
+ if (id != null && id.length > 0) ids.add(id);
67
+ }
68
+ if (Array.isArray(aiMessage.content) && !(0, node_util_types.isProxy)(aiMessage.content)) for (const part of aiMessage.content) {
64
69
  if (typeof part !== "object") continue;
65
- const record = part;
66
- if ((record.type === "tool_use" || record.type === "tool_call") && typeof record.id === "string" && record.id.length > 0) ids.add(record.id);
70
+ const type = getStringProperty(part, "type");
71
+ const id = getStringProperty(part, "id");
72
+ if ((type === "tool_use" || type === "tool_call") && id != null && id.length > 0) ids.add(id);
67
73
  }
68
74
  const toolOutputSource = getResponsesToolOutputSource(message);
69
75
  if (toolOutputSource != null) for (const item of toolOutputSource.items) {
70
76
  if (item == null || typeof item !== "object") continue;
71
- const record = item;
72
- if ((record.type === "function_call" || record.type === "custom_tool_call" || record.type === "computer_call") && typeof record.call_id === "string" && record.call_id !== "") ids.add(record.call_id);
77
+ const type = getStringProperty(item, "type");
78
+ const callId = getStringProperty(item, "call_id");
79
+ if ((type === "function_call" || type === "custom_tool_call" || type === "computer_call") && callId != null && callId !== "") ids.add(callId);
73
80
  }
74
81
  return ids;
75
82
  }
@@ -445,72 +452,117 @@ function getMessagesWithinTokenLimit({ messages: _messages, maxContextTokens, in
445
452
  function checkValidNumber(value) {
446
453
  return typeof value === "number" && !isNaN(value) && value > 0;
447
454
  }
448
- function maskConsumedToolResults(params) {
449
- const { messages, indexTokenCountMap, tokenCounter } = params;
450
- let maskedCount = 0;
451
- let seenNonToolCallAI = false;
452
- const consumedIndices = [];
455
+ function findConsumedBoundary(messages) {
453
456
  for (let i = messages.length - 1; i >= 0; i--) {
454
- const msg = messages[i];
455
- const type = msg.getType();
456
- if (type === "ai") {
457
- if (typeof msg.content === "string" ? msg.content.trim().length > 0 : Array.isArray(msg.content) && msg.content.some((b) => typeof b === "object" && b.type === "text" && typeof b.text === "string" && b.text.trim().length > 0)) seenNonToolCallAI = true;
458
- } else if (type === "tool" && seenNonToolCallAI) consumedIndices.push(i);
457
+ const message = messages[i];
458
+ if (message.getType() === "ai" && require_core.hasNonEmptyTextContent(message.content)) return i;
459
459
  }
460
- if (consumedIndices.length === 0) return 0;
461
- consumedIndices.reverse();
462
- const totalBudgetChars = params.availableRawBudget != null && params.availableRawBudget > 0 ? params.availableRawBudget * 4 : 0;
463
- const count = consumedIndices.length;
464
- for (let c = 0; c < count; c++) {
465
- const i = consumedIndices[c];
460
+ return 0;
461
+ }
462
+ function applyFadingCaps(params) {
463
+ const { messages, indexTokenCountMap, tokenCounter, caps, masked } = params;
464
+ const fromIndex = params.fromIndex ?? 0;
465
+ const maskedFromIndex = params.maskedFromIndex ?? 0;
466
+ const consumedBoundary = masked ? findConsumedBoundary(messages) : 0;
467
+ const start = masked ? Math.min(fromIndex, maskedFromIndex) : fromIndex;
468
+ let truncated = 0;
469
+ let maskedCount = 0;
470
+ for (let i = start; i < messages.length; i++) {
466
471
  const message = messages[i];
467
- if (require_toolContent.isComputerCallOutputMessage(message)) continue;
468
- const content = message.content;
469
- let maxChars;
470
- if (totalBudgetChars > 0) {
471
- const share = (.2 + .8 * (count > 1 ? c / (count - 1) : 1)) / (count > 1 ? .6 * count : 1) * totalBudgetChars;
472
- maxChars = Math.max(MASKED_RESULT_MAX_CHARS, Math.floor(share));
473
- } else maxChars = MASKED_RESULT_MAX_CHARS;
474
- const compacted = require_toolContent.compactToolContent(content, maxChars);
472
+ if (message.getType() !== "tool" || require_toolContent.isComputerCallOutputMessage(message)) continue;
473
+ const consumed = masked && i < consumedBoundary;
474
+ if (consumed ? i < maskedFromIndex : i < fromIndex) continue;
475
+ const maxChars = consumed ? caps.consumedChars : caps.resultChars;
476
+ if (!Number.isFinite(maxChars)) continue;
477
+ const canonicalContent = params.canonicalMessages?.[i]?.content ?? message.content;
478
+ const compacted = require_toolContent.compactToolContent(canonicalContent, maxChars);
475
479
  if (!compacted.changed) continue;
476
- if (params.originalContentStore && !params.originalContentStore.has(i)) {
477
- const original = require_toolContent.serializeToolContentBounded(content, ORIGINAL_CONTENT_MAX_CHARS);
480
+ if (consumed && params.originalContentStore && !params.originalContentStore.has(i)) {
481
+ const original = require_toolContent.serializeToolContentBounded(canonicalContent, ORIGINAL_CONTENT_MAX_CHARS);
478
482
  params.originalContentStore.set(i, original);
479
- if (params.onContentStored) params.onContentStored(i, original);
483
+ params.onContentStored?.(i, original);
480
484
  }
481
485
  const cloned = require_toolContent.cloneToolMessageWithContent(message, compacted.content);
482
486
  messages[i] = cloned;
483
487
  indexTokenCountMap[i] = tokenCounter(cloned);
484
- maskedCount++;
488
+ if (consumed) maskedCount++;
489
+ else truncated++;
485
490
  }
486
- return maskedCount;
491
+ const inputs = Number.isFinite(caps.inputChars) ? applyToolCallInputCaps({
492
+ messages,
493
+ canonicalMessages: params.canonicalMessages,
494
+ maxInputChars: caps.inputChars,
495
+ indexTokenCountMap,
496
+ tokenCounter,
497
+ fromIndex
498
+ }) : 0;
499
+ return {
500
+ truncated,
501
+ inputs,
502
+ masked: maskedCount,
503
+ consumedBoundary
504
+ };
487
505
  }
488
- function preFlightTruncateToolResults(params) {
489
- const { messages, maxContextTokens, indexTokenCountMap, tokenCounter } = params;
490
- const baseMaxChars = require_truncation.calculateMaxToolResultChars(maxContextTokens);
491
- let truncatedCount = 0;
492
- const toolIndices = [];
493
- for (let i = 0; i < messages.length; i++) if (messages[i].getType() === "tool" && !require_toolContent.isComputerCallOutputMessage(messages[i])) toolIndices.push(i);
494
- for (let t = 0; t < toolIndices.length; t++) {
495
- const i = toolIndices[t];
496
- const message = messages[i];
497
- const content = message.content;
498
- const recencyFactor = .2 + .8 * (toolIndices.length > 1 ? t / (toolIndices.length - 1) : 1);
499
- const compacted = require_toolContent.compactToolContent(content, Math.max(200, Math.floor(baseMaxChars * recencyFactor)));
500
- if (!compacted.changed) continue;
501
- const cloned = require_toolContent.cloneToolMessageWithContent(message, compacted.content);
502
- messages[i] = cloned;
503
- indexTokenCountMap[i] = tokenCounter(cloned);
504
- truncatedCount++;
506
+ function maskConsumedToolResults(params) {
507
+ if (params.maxChars == null && params.availableRawBudget != null && params.availableRawBudget > 0) {
508
+ const consumedBoundary = findConsumedBoundary(params.messages);
509
+ const consumedIndices = [];
510
+ for (let i = 0; i < consumedBoundary; i++) if (params.messages[i].getType() === "tool") consumedIndices.push(i);
511
+ const count = consumedIndices.length;
512
+ const totalBudgetChars = params.availableRawBudget * 4;
513
+ let masked = 0;
514
+ for (let c = 0; c < count; c++) {
515
+ const i = consumedIndices[c];
516
+ const message = params.messages[i];
517
+ if (require_toolContent.isComputerCallOutputMessage(message)) continue;
518
+ const weight = .2 + .8 * (count > 1 ? c / (count - 1) : 1);
519
+ const totalWeight = count > 1 ? .6 * count : 1;
520
+ const maxChars = Math.max(300, Math.floor(weight / totalWeight * totalBudgetChars));
521
+ const compacted = require_toolContent.compactToolContent(message.content, maxChars);
522
+ if (!compacted.changed) continue;
523
+ if (params.originalContentStore != null && !params.originalContentStore.has(i)) {
524
+ const original = require_toolContent.serializeToolContentBounded(message.content, ORIGINAL_CONTENT_MAX_CHARS);
525
+ params.originalContentStore.set(i, original);
526
+ params.onContentStored?.(i, original);
527
+ }
528
+ const cloned = require_toolContent.cloneToolMessageWithContent(message, compacted.content);
529
+ params.messages[i] = cloned;
530
+ params.indexTokenCountMap[i] = params.tokenCounter(cloned);
531
+ masked++;
532
+ }
533
+ return masked;
505
534
  }
506
- return truncatedCount;
535
+ return applyFadingCaps({
536
+ messages: params.messages,
537
+ indexTokenCountMap: params.indexTokenCountMap,
538
+ tokenCounter: params.tokenCounter,
539
+ caps: {
540
+ resultChars: Number.POSITIVE_INFINITY,
541
+ consumedChars: Math.max(300, Math.floor(params.maxChars ?? 300)),
542
+ inputChars: Number.POSITIVE_INFINITY
543
+ },
544
+ masked: true,
545
+ originalContentStore: params.originalContentStore,
546
+ onContentStored: params.onContentStored
547
+ }).masked;
548
+ }
549
+ function preFlightTruncateToolResults(params) {
550
+ return applyFadingCaps({
551
+ messages: params.messages,
552
+ indexTokenCountMap: params.indexTokenCountMap,
553
+ tokenCounter: params.tokenCounter,
554
+ caps: {
555
+ resultChars: require_truncation.calculateMaxToolResultChars(params.maxContextTokens),
556
+ consumedChars: Number.POSITIVE_INFINITY,
557
+ inputChars: Number.POSITIVE_INFINITY
558
+ },
559
+ masked: false
560
+ }).truncated;
507
561
  }
508
- const HARD_MAX_TOOL_CALL_INPUT_CHARS = 2e5;
509
- const MIN_JSON_VALUE_CHARS = 4;
510
562
  const ACCESSOR_INPUT_PLACEHOLDER = "[Property accessor omitted]";
511
563
  function normalizeToolInputLimit(maxChars) {
512
- if (!Number.isFinite(maxChars)) return HARD_MAX_TOOL_CALL_INPUT_CHARS;
513
- return Math.max(MIN_JSON_VALUE_CHARS, Math.floor(maxChars));
564
+ if (!Number.isFinite(maxChars)) return require_truncation.HARD_MAX_TOOL_CALL_INPUT_CHARS;
565
+ return Math.max(4, Math.floor(maxChars));
514
566
  }
515
567
  function readPropertyWithoutAccessors(value, key) {
516
568
  let current = value;
@@ -600,33 +652,62 @@ function cloneAIMessageWithProjectedStreamContent(message, changes, streamConten
600
652
  };
601
653
  return Object.create(Object.getPrototypeOf(message), descriptors);
602
654
  }
655
+ const TOOL_INPUT_TRUNCATION_MARKER = "… [truncated]\n";
603
656
  function createBoundedTruncationValue(preview, originalChars, maxChars) {
604
657
  const normalizedMaxChars = normalizeToolInputLimit(maxChars);
658
+ const canonicalPrefix = preview.startsWith(TOOL_INPUT_TRUNCATION_MARKER) ? preview.slice(14) : preview;
605
659
  if (JSON.stringify({
606
- _truncated: "",
660
+ _truncated: TOOL_INPUT_TRUNCATION_MARKER,
607
661
  _originalChars: originalChars
608
662
  }).length > normalizedMaxChars) return {};
609
663
  let low = 0;
610
- let high = Math.min(preview.length, normalizedMaxChars);
664
+ let high = Math.min(canonicalPrefix.length, normalizedMaxChars);
611
665
  while (low < high) {
612
666
  const next = Math.ceil((low + high) / 2);
613
667
  const candidate = {
614
- _truncated: preview.slice(0, next),
668
+ _truncated: TOOL_INPUT_TRUNCATION_MARKER + canonicalPrefix.slice(0, next),
615
669
  _originalChars: originalChars
616
670
  };
617
671
  if (JSON.stringify(candidate).length <= normalizedMaxChars) low = next;
618
672
  else high = next - 1;
619
673
  }
620
674
  return {
621
- _truncated: low < preview.length && low >= 13 ? preview.slice(0, low - 13) + "… [truncated]" : preview.slice(0, low),
675
+ _truncated: TOOL_INPUT_TRUNCATION_MARKER + canonicalPrefix.slice(0, low),
622
676
  _originalChars: originalChars
623
677
  };
624
678
  }
679
+ function readBoundedTruncationValue(input) {
680
+ if (input == null || typeof input !== "object" || (0, node_util_types.isProxy)(input)) return;
681
+ try {
682
+ const prototype = Object.getPrototypeOf(input);
683
+ const keys = Object.keys(input);
684
+ if (prototype !== Object.prototype && prototype !== null || keys.length !== 2 || !keys.includes("_truncated") || !keys.includes("_originalChars")) return;
685
+ } catch {
686
+ return;
687
+ }
688
+ const preview = readPropertyWithoutAccessors(input, "_truncated");
689
+ const originalChars = readPropertyWithoutAccessors(input, "_originalChars");
690
+ return preview.own && !preview.accessor && typeof preview.value === "string" && originalChars.own && !originalChars.accessor && typeof originalChars.value === "number" && Number.isFinite(originalChars.value) && originalChars.value >= 0 ? {
691
+ preview: preview.value,
692
+ originalChars: originalChars.value
693
+ } : void 0;
694
+ }
625
695
  function projectToolInputWithinLimit(input, maxChars) {
626
696
  const normalizedMaxChars = normalizeToolInputLimit(maxChars);
627
- const serialized = require_toolContent.serializeStructuredValueBounded(input, normalizedMaxChars);
697
+ const priorTruncation = readBoundedTruncationValue(input);
698
+ if (priorTruncation != null) {
699
+ if (!require_toolContent.serializeStructuredValueBounded(input, normalizedMaxChars).truncated) return {
700
+ value: input,
701
+ changed: false
702
+ };
703
+ return {
704
+ value: createBoundedTruncationValue(priorTruncation.preview, priorTruncation.originalChars, normalizedMaxChars),
705
+ changed: true
706
+ };
707
+ }
708
+ const serialized = require_toolContent.serializeStructuredValueBounded(input, normalizedMaxChars, normalizedMaxChars);
628
709
  if (serialized.truncated) return {
629
- value: createBoundedTruncationValue(serialized.content, serialized.originalChars, normalizedMaxChars),
710
+ value: createBoundedTruncationValue(serialized.prefix, serialized.originalChars, normalizedMaxChars),
630
711
  changed: true
631
712
  };
632
713
  if (!require_tokens.hasUnsafeStructuredSerialization(input)) return {
@@ -645,11 +726,11 @@ function projectToolInputWithinLimit(input, maxChars) {
645
726
  };
646
727
  }
647
728
  }
648
- function serializeToolCallInput(input, maxChars = HARD_MAX_TOOL_CALL_INPUT_CHARS) {
729
+ function serializeToolCallInput(input, maxChars = require_truncation.HARD_MAX_TOOL_CALL_INPUT_CHARS) {
649
730
  const normalizedMaxChars = normalizeToolInputLimit(maxChars);
650
- const serialized = require_toolContent.serializeStructuredValueBounded(projectToolInputWithinLimit(input, normalizedMaxChars).value, normalizedMaxChars);
731
+ const serialized = require_toolContent.serializeStructuredValueBounded(projectToolInputWithinLimit(input, normalizedMaxChars).value, normalizedMaxChars, normalizedMaxChars);
651
732
  if (!serialized.truncated) return serialized.content === "undefined" ? "null" : serialized.content;
652
- const fallback = createBoundedTruncationValue(serialized.content, serialized.originalChars, normalizedMaxChars);
733
+ const fallback = createBoundedTruncationValue(serialized.prefix, serialized.originalChars, normalizedMaxChars);
653
734
  return JSON.stringify(fallback);
654
735
  }
655
736
  function projectKnownInputProperty(value, key, maxChars, changes) {
@@ -675,7 +756,7 @@ function projectInlineToolInput(block, maxChars) {
675
756
  if (nestedProperty.found && !nestedProperty.accessor && nestedProperty.value != null && typeof nestedProperty.value === "object") {
676
757
  const nestedChanges = {};
677
758
  projectKnownInputProperty(nestedProperty.value, "args", maxChars, nestedChanges);
678
- if (Object.keys(nestedChanges).length > 0) changes.tool_call = cloneWithProjectedProperties(nestedProperty.value, nestedChanges);
759
+ if (Object.keys(nestedChanges).length > 0) changes.tool_call = (0, node_util_types.isProxy)(nestedProperty.value) ? { args: ACCESSOR_INPUT_PLACEHOLDER } : cloneWithProjectedProperties(nestedProperty.value, nestedChanges);
679
760
  else if (!nestedProperty.own) changes.tool_call = nestedProperty.value;
680
761
  } else if (nestedProperty.accessor) changes.tool_call = { args: ACCESSOR_INPUT_PLACEHOLDER };
681
762
  return Object.keys(changes).length > 0 ? cloneWithProjectedProperties(block, changes) : block;
@@ -686,11 +767,34 @@ function projectSerializedArguments(value, maxChars) {
686
767
  value,
687
768
  changed: false
688
769
  };
770
+ if (typeof value === "string" && value.includes("\"_truncated\"") && value.includes("\"_originalChars\"")) try {
771
+ const priorTruncation = readBoundedTruncationValue(JSON.parse(value));
772
+ if (priorTruncation != null) return {
773
+ value: JSON.stringify(createBoundedTruncationValue(priorTruncation.preview, priorTruncation.originalChars, normalizedMaxChars)),
774
+ changed: true
775
+ };
776
+ } catch {}
689
777
  return {
690
778
  value: serializeToolCallInput(value, normalizedMaxChars),
691
779
  changed: true
692
780
  };
693
781
  }
782
+ const TRUNCATED_STRING_INPUT_PATTERN = /\n… \[truncated: (\d+) chars\]$/u;
783
+ function projectStringInputWithinLimit(value, maxChars) {
784
+ const normalizedMaxChars = normalizeToolInputLimit(maxChars);
785
+ if (value.length <= normalizedMaxChars) return {
786
+ value,
787
+ changed: false
788
+ };
789
+ const match = TRUNCATED_STRING_INPUT_PATTERN.exec(value);
790
+ const originalChars = match == null ? value.length : Number(match[1]);
791
+ const prefix = match == null ? value : value.slice(0, match.index);
792
+ const marker = `\n… [truncated: ${originalChars} chars]`;
793
+ return {
794
+ value: marker.length >= normalizedMaxChars ? prefix.slice(0, normalizedMaxChars) : prefix.slice(0, normalizedMaxChars - marker.length) + marker,
795
+ changed: true
796
+ };
797
+ }
694
798
  function selectProjectedSerializedArguments(property, canonical, maxChars) {
695
799
  if (canonical != null) return {
696
800
  value: canonical,
@@ -794,7 +898,7 @@ function projectResponsesOutput(output, maxChars, canonicalArguments, canonicalI
794
898
  changed: !inputProperty.own || inputProperty.value !== source
795
899
  };
796
900
  else if (type === "custom_tool_call") {
797
- const value = typeof source === "string" ? require_truncation.truncateToolResultContent(source, normalizeToolInputLimit(maxChars)) : serializeToolCallInput(source, maxChars);
901
+ const value = typeof source === "string" ? projectStringInputWithinLimit(source, maxChars).value : serializeToolCallInput(source, maxChars);
798
902
  projectedInput = {
799
903
  value,
800
904
  changed: !inputProperty.own || inputProperty.value !== value
@@ -816,14 +920,10 @@ function projectResponsesOutput(output, maxChars, canonicalArguments, canonicalI
816
920
  changed: state.changed
817
921
  };
818
922
  }
819
- function calculateMaxToolCallInputChars(maxContextTokens) {
820
- if (maxContextTokens == null || maxContextTokens <= 0) return HARD_MAX_TOOL_CALL_INPUT_CHARS;
821
- return Math.max(MIN_JSON_VALUE_CHARS, Math.min(Math.floor(maxContextTokens * .15) * 4, HARD_MAX_TOOL_CALL_INPUT_CHARS));
822
- }
823
- function projectToolCallInputsInternal(messages, maxInputChars, dropIncompleteStreamContent) {
923
+ function projectToolCallInputsInternal(messages, maxInputChars, dropIncompleteStreamContent, fromIndex = 0) {
824
924
  const normalizedMaxInputChars = normalizeToolInputLimit(maxInputChars);
825
925
  let projectedMessages;
826
- for (let i = 0; i < messages.length; i++) {
926
+ for (let i = fromIndex; i < messages.length; i++) {
827
927
  const message = messages[i];
828
928
  const messageRole = message.role;
829
929
  if (message.getType() !== "ai" && messageRole !== "assistant") continue;
@@ -891,25 +991,47 @@ function projectToolCallInputsInternal(messages, maxInputChars, dropIncompleteSt
891
991
  }
892
992
  return projectedMessages ?? messages;
893
993
  }
894
- function projectToolCallInputs(messages, maxInputChars) {
895
- return projectToolCallInputsInternal(messages, maxInputChars, false);
994
+ function projectToolCallInputs(messages, maxInputChars, fromIndex = 0) {
995
+ return projectToolCallInputsInternal(messages, maxInputChars, false, fromIndex);
896
996
  }
897
997
  function projectToolMessagesForProvider(messages, maxInputChars) {
898
998
  return projectToolCallInputsInternal(messages, maxInputChars, true);
899
999
  }
900
- function preFlightTruncateToolCallInputs(params) {
901
- const { messages, maxContextTokens, indexTokenCountMap, tokenCounter } = params;
902
- const projected = projectToolCallInputs(messages, calculateMaxToolCallInputChars(maxContextTokens));
903
- if (projected === messages) return 0;
1000
+ function applyToolCallInputCaps(params) {
1001
+ const { messages, maxInputChars, indexTokenCountMap, tokenCounter } = params;
1002
+ const fromIndex = params.fromIndex ?? 0;
1003
+ const sourceMessages = params.canonicalMessages ?? messages;
1004
+ const projected = projectToolCallInputs(sourceMessages, maxInputChars, fromIndex);
1005
+ if (projected === sourceMessages) return 0;
904
1006
  let truncatedCount = 0;
905
- for (let i = 0; i < messages.length; i++) {
1007
+ for (let i = fromIndex; i < messages.length; i++) {
1008
+ if (projected[i] === sourceMessages[i]) continue;
906
1009
  if (projected[i] === messages[i]) continue;
907
- messages[i] = projected[i];
908
- indexTokenCountMap[i] = tokenCounter(projected[i]);
1010
+ const current = messages[i];
1011
+ const canonical = sourceMessages[i];
1012
+ const capped = projected[i];
1013
+ const changes = {};
1014
+ if (capped.content !== canonical.content) changes.content = capped.content;
1015
+ if (capped.tool_calls !== canonical.tool_calls) changes.tool_calls = capped.tool_calls;
1016
+ const additionalKwargsChanges = {};
1017
+ for (const key of ["tool_calls", "function_call"]) if (capped.additional_kwargs[key] !== canonical.additional_kwargs[key]) additionalKwargsChanges[key] = capped.additional_kwargs[key];
1018
+ if (Object.keys(additionalKwargsChanges).length > 0) changes.additional_kwargs = cloneWithProjectedProperties(current.additional_kwargs, additionalKwargsChanges);
1019
+ if (capped.response_metadata.output !== canonical.response_metadata.output) changes.response_metadata = cloneWithProjectedProperties(current.response_metadata, { output: capped.response_metadata.output });
1020
+ const merged = cloneWithProjectedProperties(current, changes);
1021
+ messages[i] = merged;
1022
+ indexTokenCountMap[i] = tokenCounter(merged);
909
1023
  truncatedCount++;
910
1024
  }
911
1025
  return truncatedCount;
912
1026
  }
1027
+ function preFlightTruncateToolCallInputs(params) {
1028
+ return applyToolCallInputCaps({
1029
+ messages: params.messages,
1030
+ maxInputChars: require_truncation.calculateMaxToolCallInputChars(params.maxContextTokens),
1031
+ indexTokenCountMap: params.indexTokenCountMap,
1032
+ tokenCounter: params.tokenCounter
1033
+ });
1034
+ }
913
1035
  function createPruneMessages(factoryParams) {
914
1036
  const providerFamily = factoryParams.provider == null ? void 0 : require_providerRegistry.getProviderFamily(factoryParams.provider);
915
1037
  const usesBedrockThinking = factoryParams.provider === "bedrock" || providerFamily === "bedrock";
@@ -926,12 +1048,43 @@ function createPruneMessages(factoryParams) {
926
1048
  let calibrationRatio = factoryParams.calibrationRatio != null && factoryParams.calibrationRatio > 0 ? factoryParams.calibrationRatio : 1;
927
1049
  let bestInstructionOverhead;
928
1050
  const reconciledLegacyAiMessages = /* @__PURE__ */ new WeakSet();
1051
+ const canonicalizedToolCallMessages = /* @__PURE__ */ new WeakSet();
929
1052
  let bestVarianceAbs = Infinity;
930
1053
  let bestInstructionEstimate;
931
1054
  const originalToolContent = /* @__PURE__ */ new Map();
932
1055
  let originalToolContentSize = 0;
1056
+ const canonicalByProjection = /* @__PURE__ */ new WeakMap();
1057
+ let fadingTier = require_fading.seedFadingTier(factoryParams.maxTokens, factoryParams.fadingTier);
1058
+ let restoredTierPending = require_fading.isFadingTier(factoryParams.fadingTier);
1059
+ let maxToolExchangeWidth = 1;
1060
+ let toolExchangeWidthThrough = 0;
1061
+ let toolExchangeWidthSources = [];
1062
+ let fadedThrough = 0;
1063
+ let maskedThrough = 0;
933
1064
  const contextPruningSettings = require_contextPruningSettings.resolveContextPruningSettings(factoryParams.contextPruningConfig);
934
1065
  return function pruneMessages(params) {
1066
+ const suppliedCanonicalMessages = params.canonicalMessages;
1067
+ const derivesCanonicalHistory = suppliedCanonicalMessages == null;
1068
+ const canonicalMessages = suppliedCanonicalMessages ?? params.messages.map((message) => {
1069
+ const recovered = canonicalByProjection.get(message);
1070
+ if (recovered != null) return recovered;
1071
+ return Array.isArray(message.content) ? require_cache.cloneMessage(message, [...message.content]) : message;
1072
+ });
1073
+ const priorWidthLength = toolExchangeWidthSources.length;
1074
+ if (derivesCanonicalHistory ? canonicalMessages.length < priorWidthLength || toolExchangeWidthSources.some((source, index) => canonicalMessages[index] !== source) : canonicalMessages.length < priorWidthLength || (params.canonicalPrefixStable === true ? priorWidthLength > 0 && canonicalMessages[priorWidthLength - 1] !== toolExchangeWidthSources[priorWidthLength - 1] : toolExchangeWidthSources.some((source, index) => canonicalMessages[index] !== source))) {
1075
+ maxToolExchangeWidth = 1;
1076
+ toolExchangeWidthThrough = 0;
1077
+ toolExchangeWidthSources = [];
1078
+ fadedThrough = 0;
1079
+ maskedThrough = 0;
1080
+ originalToolContent.clear();
1081
+ originalToolContentSize = 0;
1082
+ }
1083
+ for (let i = toolExchangeWidthThrough; i < canonicalMessages.length; i++) {
1084
+ maxToolExchangeWidth = Math.max(maxToolExchangeWidth, getToolCallIds(canonicalMessages[i]).size);
1085
+ toolExchangeWidthSources[i] = canonicalMessages[i];
1086
+ }
1087
+ toolExchangeWidthThrough = canonicalMessages.length;
935
1088
  let newOriginalToolContent;
936
1089
  if (params.messages.length === 0) {
937
1090
  const emptyInstructionTokens = factoryParams.getInstructionTokens?.() ?? 0;
@@ -945,6 +1098,7 @@ function createPruneMessages(factoryParams) {
945
1098
  prePruneContextTokens: 0,
946
1099
  remainingContextTokens: Math.max(0, emptyBudget - emptyInstructionTokens - emptyReplyPrimerTokens),
947
1100
  calibrationRatio,
1101
+ fadingTier,
948
1102
  resolvedInstructionOverhead: bestInstructionOverhead,
949
1103
  contextBudget: emptyBudget,
950
1104
  effectiveInstructionTokens: emptyInstructionTokens
@@ -987,14 +1141,33 @@ function createPruneMessages(factoryParams) {
987
1141
  };
988
1142
  for (let i = 0; i < params.messages.length; i++) {
989
1143
  let message = params.messages[i];
990
- const cachedCount = indexTokenCountMap[i];
1144
+ let cachedCount = indexTokenCountMap[i];
991
1145
  const messageType = message.getType();
992
1146
  const messageRole = message.role;
993
- if ((messageType === "ai" || messageRole === "assistant" ? readPropertyWithoutAccessors(message.additional_kwargs, "function_call") : void 0)?.found === true && !reconciledLegacyAiMessages.has(message)) {
994
- const [projected] = projectToolCallInputs([message], calculateMaxToolCallInputChars(factoryParams.maxTokens));
1147
+ const isAssistant = messageType === "ai" || messageRole === "assistant";
1148
+ if (isAssistant && !canonicalizedToolCallMessages.has(message)) {
1149
+ const [canonicalized] = projectToolCallInputs([message], require_truncation.HARD_MAX_TOOL_CALL_INPUT_CHARS);
1150
+ if (canonicalized !== message) {
1151
+ message = canonicalized;
1152
+ params.messages[i] = message;
1153
+ const canonicalizedCount = factoryParams.tokenCounter(message);
1154
+ indexTokenCountMap[i] = canonicalizedCount;
1155
+ totalTokens += canonicalizedCount - (cachedCount ?? 0);
1156
+ cachedCount = canonicalizedCount;
1157
+ if (i >= lastTurnStartIndex) newOutputs.add(i);
1158
+ }
1159
+ canonicalizedToolCallMessages.add(message);
1160
+ }
1161
+ if ((isAssistant ? readPropertyWithoutAccessors(message.additional_kwargs, "function_call") : void 0)?.found === true && !reconciledLegacyAiMessages.has(message)) {
1162
+ const [projected] = projectToolCallInputs([message], require_truncation.calculateMaxToolCallInputChars(factoryParams.maxTokens));
995
1163
  if (projected !== message) {
996
1164
  message = projected;
997
1165
  params.messages[i] = message;
1166
+ const projectedCount = factoryParams.tokenCounter(message);
1167
+ indexTokenCountMap[i] = projectedCount;
1168
+ totalTokens += projectedCount - (cachedCount ?? 0);
1169
+ cachedCount = projectedCount;
1170
+ if (i >= lastTurnStartIndex) newOutputs.add(i);
998
1171
  }
999
1172
  reconciledLegacyAiMessages.add(message);
1000
1173
  if (cachedCount !== void 0 || i < lastTurnStartIndex) applyReconciledCount(i, cachedCount, factoryParams.tokenCounter(message));
@@ -1081,15 +1254,6 @@ function createPruneMessages(factoryParams) {
1081
1254
  const pruningBudget = factoryParams.maxTokens - reserveTokens;
1082
1255
  const effectiveMaxTokens = Math.max(0, pruningBudget - currentInstructionTokens);
1083
1256
  let calibratedTotalTokens = Math.round(totalTokens * calibrationRatio);
1084
- factoryParams.log?.("debug", "Budget", {
1085
- maxTokens: factoryParams.maxTokens,
1086
- pruningBudget,
1087
- effectiveMax: effectiveMaxTokens,
1088
- instructionTokens: currentInstructionTokens,
1089
- messageCount: params.messages.length,
1090
- calibratedTotalTokens,
1091
- calibrationRatio: Math.round(calibrationRatio * 100) / 100
1092
- });
1093
1257
  if (effectiveMaxTokens === 0 && factoryParams.summarizationEnabled === true && params.messages.length > 0) {
1094
1258
  factoryParams.log?.("warn", "Instructions consume entire budget — yielding all messages for summarization", {
1095
1259
  instructionTokens: currentInstructionTokens,
@@ -1105,81 +1269,102 @@ function createPruneMessages(factoryParams) {
1105
1269
  remainingContextTokens: 0,
1106
1270
  contextPressure: pruningBudget > 0 ? calibratedTotalTokens / pruningBudget : 0,
1107
1271
  calibrationRatio,
1272
+ fadingTier,
1108
1273
  resolvedInstructionOverhead: bestInstructionOverhead,
1109
1274
  contextBudget: pruningBudget,
1110
1275
  effectiveInstructionTokens: currentInstructionTokens
1111
1276
  };
1112
1277
  }
1113
- totalTokens = sumTokenCounts(indexTokenCountMap, params.messages.length);
1114
- calibratedTotalTokens = Math.round(totalTokens * calibrationRatio);
1115
- const contextPressure = pruningBudget > 0 ? calibratedTotalTokens / pruningBudget : 0;
1116
- let preFlightResultCount = 0;
1117
- let preFlightInputCount = 0;
1118
- let observationsMasked = 0;
1119
- if (contextPressure >= PRESSURE_THRESHOLD_MASKING) {
1120
- const rawMessageBudget = calibrationRatio > 0 ? Math.floor(effectiveMaxTokens / calibrationRatio) : effectiveMaxTokens;
1121
- const reserveHeadroom = factoryParams.summarizationEnabled === true ? Math.floor(rawMessageBudget * (factoryParams.reserveRatio ?? .05) * .5) : 0;
1122
- observationsMasked = maskConsumedToolResults({
1278
+ const storeOriginalToolContent = (index, content) => {
1279
+ originalToolContentSize += content.length;
1280
+ if (newOriginalToolContent == null) newOriginalToolContent = /* @__PURE__ */ new Map();
1281
+ newOriginalToolContent.set(index, content);
1282
+ while (originalToolContentSize > 2e6 && originalToolContent.size > 0) {
1283
+ const oldest = originalToolContent.keys().next();
1284
+ if (oldest.done === true) break;
1285
+ const removed = originalToolContent.get(oldest.value);
1286
+ if (removed != null) originalToolContentSize -= removed.length;
1287
+ originalToolContent.delete(oldest.value);
1288
+ }
1289
+ };
1290
+ let restoredFading = {
1291
+ truncated: 0,
1292
+ inputs: 0,
1293
+ masked: 0,
1294
+ consumedBoundary: 0
1295
+ };
1296
+ if (restoredTierPending) {
1297
+ restoredFading = applyFadingCaps({
1123
1298
  messages: params.messages,
1299
+ canonicalMessages,
1124
1300
  indexTokenCountMap,
1125
1301
  tokenCounter: factoryParams.tokenCounter,
1126
- availableRawBudget: rawMessageBudget + reserveHeadroom,
1302
+ caps: require_fading.resolveFadingCaps(fadingTier, factoryParams.maxToolResultChars),
1303
+ masked: fadingTier.masked,
1127
1304
  originalContentStore: factoryParams.summarizationEnabled === true ? originalToolContent : void 0,
1128
- onContentStored: factoryParams.summarizationEnabled === true ? (index, content) => {
1129
- originalToolContentSize += content.length;
1130
- if (newOriginalToolContent == null) newOriginalToolContent = /* @__PURE__ */ new Map();
1131
- newOriginalToolContent.set(index, content);
1132
- while (originalToolContentSize > 2e6 && originalToolContent.size > 0) {
1133
- const oldest = originalToolContent.keys().next();
1134
- if (oldest.done === true) break;
1135
- const removed = originalToolContent.get(oldest.value);
1136
- if (removed != null) originalToolContentSize -= removed.length;
1137
- originalToolContent.delete(oldest.value);
1138
- }
1139
- } : void 0
1305
+ onContentStored: factoryParams.summarizationEnabled === true ? storeOriginalToolContent : void 0
1140
1306
  });
1141
- if (observationsMasked > 0) {
1142
- cumulativeRawSent = 0;
1143
- cumulativeProviderReported = 0;
1144
- }
1307
+ fadedThrough = params.messages.length;
1308
+ maskedThrough = restoredFading.consumedBoundary;
1309
+ restoredTierPending = false;
1145
1310
  }
1146
- if (contextPressure >= PRESSURE_THRESHOLD_MASKING && factoryParams.summarizationEnabled !== true) {
1147
- const budgetFactor = PRESSURE_BANDS.find(([threshold]) => contextPressure >= threshold)?.[1] ?? 1;
1148
- const baseBudget = Math.max(1024, Math.floor(effectiveMaxTokens * budgetFactor));
1149
- preFlightResultCount = preFlightTruncateToolResults({
1150
- messages: params.messages,
1151
- maxContextTokens: baseBudget,
1152
- indexTokenCountMap,
1153
- tokenCounter: factoryParams.tokenCounter
1154
- });
1155
- preFlightInputCount = preFlightTruncateToolCallInputs({
1311
+ totalTokens = sumTokenCounts(indexTokenCountMap, params.messages.length);
1312
+ calibratedTotalTokens = Math.round(totalTokens * calibrationRatio);
1313
+ const contextPressure = pruningBudget > 0 ? calibratedTotalTokens / pruningBudget : 0;
1314
+ factoryParams.log?.("debug", "Budget", {
1315
+ maxTokens: factoryParams.maxTokens,
1316
+ pruningBudget,
1317
+ effectiveMax: effectiveMaxTokens,
1318
+ instructionTokens: currentInstructionTokens,
1319
+ messageCount: params.messages.length,
1320
+ calibratedTotalTokens,
1321
+ calibrationRatio: Math.round(calibrationRatio * 100) / 100
1322
+ });
1323
+ const fade = (signals) => {
1324
+ const nextTier = require_fading.resolveFadingTier(fadingTier, factoryParams.maxTokens, signals, factoryParams.maxToolResultChars);
1325
+ if (nextTier !== fadingTier) {
1326
+ fadingTier = nextTier;
1327
+ fadedThrough = 0;
1328
+ maskedThrough = 0;
1329
+ }
1330
+ const result = applyFadingCaps({
1156
1331
  messages: params.messages,
1157
- maxContextTokens: baseBudget,
1332
+ canonicalMessages,
1158
1333
  indexTokenCountMap,
1159
- tokenCounter: factoryParams.tokenCounter
1334
+ tokenCounter: factoryParams.tokenCounter,
1335
+ caps: require_fading.resolveFadingCaps(fadingTier, factoryParams.maxToolResultChars),
1336
+ masked: fadingTier.masked,
1337
+ fromIndex: fadedThrough,
1338
+ maskedFromIndex: maskedThrough,
1339
+ originalContentStore: factoryParams.summarizationEnabled === true ? originalToolContent : void 0,
1340
+ onContentStored: factoryParams.summarizationEnabled === true ? storeOriginalToolContent : void 0
1160
1341
  });
1342
+ fadedThrough = params.messages.length;
1343
+ maskedThrough = result.consumedBoundary;
1344
+ return result;
1345
+ };
1346
+ const fadingSignals = {
1347
+ contextPressure,
1348
+ effectiveRawTokens: calibrationRatio > 0 ? Math.floor(effectiveMaxTokens / calibrationRatio) : effectiveMaxTokens,
1349
+ summarizationEnabled: factoryParams.summarizationEnabled === true,
1350
+ toolExchangeWidth: maxToolExchangeWidth
1351
+ };
1352
+ const faded = fade(fadingSignals);
1353
+ const observationsMasked = restoredFading.masked + faded.masked;
1354
+ const preFlightResultCount = restoredFading.truncated + faded.truncated;
1355
+ const preFlightInputCount = restoredFading.inputs + faded.inputs;
1356
+ if (observationsMasked > 0) {
1357
+ cumulativeRawSent = 0;
1358
+ cumulativeProviderReported = 0;
1161
1359
  }
1162
1360
  if (factoryParams.contextPruningConfig?.enabled === true && factoryParams.summarizationEnabled !== true) require_contextPruning.applyContextPruning({
1163
1361
  messages: params.messages,
1362
+ canonicalMessages,
1164
1363
  indexTokenCountMap,
1165
1364
  tokenCounter: factoryParams.tokenCounter,
1166
1365
  resolvedSettings: contextPruningSettings
1167
1366
  });
1168
- const rawSpaceEffectiveMax = calibrationRatio > 0 ? Math.round(effectiveMaxTokens / calibrationRatio) : effectiveMaxTokens;
1169
- if (factoryParams.summarizationEnabled === true && rawSpaceEffectiveMax > 0) {
1170
- preFlightResultCount = preFlightTruncateToolResults({
1171
- messages: params.messages,
1172
- maxContextTokens: rawSpaceEffectiveMax,
1173
- indexTokenCountMap,
1174
- tokenCounter: factoryParams.tokenCounter
1175
- });
1176
- preFlightInputCount = preFlightTruncateToolCallInputs({
1177
- messages: params.messages,
1178
- maxContextTokens: rawSpaceEffectiveMax,
1179
- indexTokenCountMap,
1180
- tokenCounter: factoryParams.tokenCounter
1181
- });
1182
- }
1367
+ if (derivesCanonicalHistory) for (let i = 0; i < params.messages.length; i++) canonicalByProjection.set(params.messages[i], canonicalMessages[i]);
1183
1368
  const preTruncationTotalTokens = totalTokens;
1184
1369
  totalTokens = sumTokenCounts(indexTokenCountMap, params.messages.length);
1185
1370
  calibratedTotalTokens = Math.round(totalTokens * calibrationRatio);
@@ -1204,6 +1389,7 @@ function createPruneMessages(factoryParams) {
1204
1389
  originalToolContent: originalToolContent.size > 0 ? originalToolContent : void 0,
1205
1390
  newOriginalToolContent,
1206
1391
  calibrationRatio,
1392
+ fadingTier,
1207
1393
  resolvedInstructionOverhead: bestInstructionOverhead,
1208
1394
  contextBudget: pruningBudget,
1209
1395
  effectiveInstructionTokens: currentInstructionTokens
@@ -1242,96 +1428,36 @@ function createPruneMessages(factoryParams) {
1242
1428
  let context = repairedContext;
1243
1429
  let reclaimedTokens = initialReclaimedTokens;
1244
1430
  if (droppedMessages.length > 0) appendItems(messagesToRefine, droppedMessages);
1245
- if (context.length === 0 && params.messages.length > 0 && effectiveMaxTokens > 0 && factoryParams.summarizationEnabled === true) {
1246
- const fadingBudget = Math.max(1024, effectiveMaxTokens);
1247
- factoryParams.log?.("debug", "Fallback fading — empty context with summarization", {
1248
- messageCount: params.messages.length,
1249
- effectiveMaxTokens,
1250
- fadingBudget
1251
- });
1252
- const fadedMessages = [...params.messages];
1253
- const preFadingTokenCounts = {};
1254
- for (let i = 0; i < params.messages.length; i++) preFadingTokenCounts[i] = indexTokenCountMap[i];
1255
- preFlightTruncateToolResults({
1256
- messages: fadedMessages,
1257
- maxContextTokens: fadingBudget,
1258
- indexTokenCountMap,
1259
- tokenCounter: factoryParams.tokenCounter
1260
- });
1261
- preFlightTruncateToolCallInputs({
1262
- messages: fadedMessages,
1263
- maxContextTokens: fadingBudget,
1264
- indexTokenCountMap,
1265
- tokenCounter: factoryParams.tokenCounter
1266
- });
1267
- const fadingRetry = getMessagesWithinTokenLimit({
1268
- maxContextTokens: pruningBudget,
1269
- messages: fadedMessages,
1270
- indexTokenCountMap,
1271
- startType: params.startType,
1272
- thinkingEnabled: factoryParams.thinkingEnabled,
1273
- tokenCounter: factoryParams.tokenCounter,
1274
- instructionTokens: currentInstructionTokens,
1275
- reasoningType: usesBedrockThinking ? "reasoning_content" : "thinking",
1276
- thinkingStartIndex: factoryParams.thinkingEnabled === true ? runThinkingStartIndex : void 0
1277
- });
1278
- const fadingRepaired = repairOrphanedToolMessages({
1279
- context: fadingRetry.context,
1280
- allMessages: fadedMessages,
1281
- tokenCounter: factoryParams.tokenCounter,
1282
- indexTokenCountMap
1283
- });
1284
- if (fadingRepaired.context.length > 0) {
1285
- context = fadingRepaired.context;
1286
- reclaimedTokens = fadingRepaired.reclaimedTokens;
1287
- appendItems(messagesToRefine, fadingRetry.messagesToRefine);
1288
- if (fadingRepaired.droppedMessages.length > 0) appendItems(messagesToRefine, fadingRepaired.droppedMessages);
1289
- factoryParams.log?.("debug", "Fallback fading recovered context", {
1290
- contextLength: context.length,
1291
- messagesToRefineCount: messagesToRefine.length,
1292
- remainingTokens: fadingRetry.remainingContextTokens
1293
- });
1294
- for (const [key, value] of Object.entries(preFadingTokenCounts)) indexTokenCountMap[key] = value;
1295
- } else for (const [key, value] of Object.entries(preFadingTokenCounts)) indexTokenCountMap[key] = value;
1296
- }
1297
1431
  if (context.length === 0 && params.messages.length > 0 && effectiveMaxTokens > 0) {
1298
1432
  const perMessageTokenBudget = Math.floor(effectiveMaxTokens / Math.max(1, params.messages.length));
1299
1433
  const emergencyMaxChars = Math.max(200, perMessageTokenBudget * 4);
1434
+ const emergencyTier = require_fading.resolveFadingTier(fadingTier, factoryParams.maxTokens, {
1435
+ ...fadingSignals,
1436
+ minRung: require_fading.fadingRungForExchangeChars(factoryParams.maxTokens, emergencyMaxChars, factoryParams.maxToolResultChars)
1437
+ }, factoryParams.maxToolResultChars);
1438
+ const emergencyCaps = require_fading.resolveFadingCaps(emergencyTier, factoryParams.maxToolResultChars);
1300
1439
  factoryParams.log?.("warn", "Empty context, entering emergency truncation", {
1301
1440
  messageCount: params.messages.length,
1302
1441
  effectiveMax: effectiveMaxTokens,
1303
- emergencyMaxChars
1442
+ emergencyMaxChars,
1443
+ budgetTokens: emergencyCaps.budgetTokens
1304
1444
  });
1305
1445
  const emergencyMessages = [...params.messages];
1306
1446
  const preEmergencyTokenCounts = {};
1307
1447
  for (let i = 0; i < params.messages.length; i++) preEmergencyTokenCounts[i] = indexTokenCountMap[i];
1308
1448
  try {
1309
- let emergencyTruncatedCount = 0;
1310
- for (let i = 0; i < emergencyMessages.length; i++) {
1311
- const message = emergencyMessages[i];
1312
- if (message.getType() === "tool" && !require_toolContent.isComputerCallOutputMessage(message)) {
1313
- const content = message.content;
1314
- if (require_toolContent.getToolContentCharLength(content) > emergencyMaxChars) {
1315
- const compacted = require_toolContent.compactToolContent(content, emergencyMaxChars);
1316
- if (!compacted.changed) continue;
1317
- const cloned = require_toolContent.cloneToolMessageWithContent(message, compacted.content);
1318
- emergencyMessages[i] = cloned;
1319
- indexTokenCountMap[i] = factoryParams.tokenCounter(cloned);
1320
- emergencyTruncatedCount++;
1321
- }
1322
- }
1323
- }
1324
- const projectedToolInputs = projectToolCallInputs(emergencyMessages, emergencyMaxChars);
1325
- if (projectedToolInputs !== emergencyMessages) for (let i = 0; i < emergencyMessages.length; i++) {
1326
- if (projectedToolInputs[i] === emergencyMessages[i]) continue;
1327
- emergencyMessages[i] = projectedToolInputs[i];
1328
- indexTokenCountMap[i] = factoryParams.tokenCounter(projectedToolInputs[i]);
1329
- emergencyTruncatedCount++;
1330
- }
1449
+ const emergency = applyFadingCaps({
1450
+ messages: emergencyMessages,
1451
+ canonicalMessages,
1452
+ indexTokenCountMap,
1453
+ tokenCounter: factoryParams.tokenCounter,
1454
+ caps: emergencyCaps,
1455
+ masked: emergencyTier.masked
1456
+ });
1331
1457
  factoryParams.log?.("info", "Emergency truncation complete");
1332
1458
  factoryParams.log?.("debug", "Emergency truncation details", {
1333
- truncatedCount: emergencyTruncatedCount,
1334
- emergencyMaxChars
1459
+ truncatedCount: emergency.truncated + emergency.inputs + emergency.masked,
1460
+ budgetTokens: emergencyCaps.budgetTokens
1335
1461
  });
1336
1462
  const retryResult = getMessagesWithinTokenLimit({
1337
1463
  maxContextTokens: pruningBudget,
@@ -1352,6 +1478,7 @@ function createPruneMessages(factoryParams) {
1352
1478
  });
1353
1479
  context = repaired.context;
1354
1480
  reclaimedTokens = repaired.reclaimedTokens;
1481
+ messagesToRefine.length = 0;
1355
1482
  appendItems(messagesToRefine, retryResult.messagesToRefine);
1356
1483
  if (repaired.droppedMessages.length > 0) appendItems(messagesToRefine, repaired.droppedMessages);
1357
1484
  factoryParams.log?.("debug", "Emergency truncation retry result", {
@@ -1377,6 +1504,7 @@ function createPruneMessages(factoryParams) {
1377
1504
  originalToolContent: originalToolContent.size > 0 ? originalToolContent : void 0,
1378
1505
  newOriginalToolContent,
1379
1506
  calibrationRatio,
1507
+ fadingTier,
1380
1508
  resolvedInstructionOverhead: bestInstructionOverhead,
1381
1509
  contextBudget: pruningBudget,
1382
1510
  effectiveInstructionTokens: currentInstructionTokens
@@ -1389,7 +1517,7 @@ exports.CALIBRATION_RATIO_MIN = CALIBRATION_RATIO_MIN;
1389
1517
  exports.DEFAULT_RESERVE_RATIO = DEFAULT_RESERVE_RATIO;
1390
1518
  exports.ORIGINAL_CONTENT_MAX_CHARS = ORIGINAL_CONTENT_MAX_CHARS;
1391
1519
  exports.REPLY_PRIMER_TOKENS = REPLY_PRIMER_TOKENS;
1392
- exports.calculateMaxToolCallInputChars = calculateMaxToolCallInputChars;
1520
+ exports.applyFadingCaps = applyFadingCaps;
1393
1521
  exports.calculateTotalTokens = calculateTotalTokens;
1394
1522
  exports.checkValidNumber = checkValidNumber;
1395
1523
  exports.clampCalibrationRatio = clampCalibrationRatio;