@myagentroam/agent 0.9.62 → 0.9.64

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 (119) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/dist/cli/codex-auth-file.js +268 -3
  4. package/dist/cli/main.js +427 -2
  5. package/dist/error.js +66 -2
  6. package/dist/host/contracts.js +20 -2
  7. package/dist/host/local-agent-host.js +146 -2
  8. package/dist/host/local-process-executor.js +39 -2
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +12 -2
  11. package/dist/model/anthropic-messages.js +474 -2
  12. package/dist/model/anthropic-thinking.js +33 -2
  13. package/dist/model/catalog-file.js +88 -2
  14. package/dist/model/configuration.js +137 -2
  15. package/dist/model/contracts.d.ts +2 -0
  16. package/dist/model/contracts.js +1 -2
  17. package/dist/model/http.js +529 -2
  18. package/dist/model/openai-responses.d.ts +2 -0
  19. package/dist/model/openai-responses.js +1602 -2
  20. package/dist/model/runtime-credential.js +35 -2
  21. package/dist/model/sse.js +199 -2
  22. package/dist/prompts/compact.js +25 -2
  23. package/dist/prompts/core.js +37 -2
  24. package/dist/prompts/execution-budget.js +10 -2
  25. package/dist/prompts/index.d.ts +3 -2
  26. package/dist/prompts/index.js +47 -2
  27. package/dist/prompts/modes.js +25 -2
  28. package/dist/prompts/output.js +1 -2
  29. package/dist/prompts/resources.d.ts +1 -1
  30. package/dist/prompts/resources.js +31 -2
  31. package/dist/prompts/subagent.d.ts +6 -0
  32. package/dist/prompts/subagent.js +29 -2
  33. package/dist/prompts/title.js +9 -2
  34. package/dist/prompts/workflow.js +32 -2
  35. package/dist/runtime/async-queue.js +62 -2
  36. package/dist/runtime/builtin-skills.js +19 -2
  37. package/dist/runtime/byted-ark-imagegen-skill.js +18 -2
  38. package/dist/runtime/codex-imagegen-skill.js +35 -2
  39. package/dist/runtime/compact.js +225 -2
  40. package/dist/runtime/context-gc-runtime.d.ts +35 -0
  41. package/dist/runtime/context-gc-runtime.js +163 -0
  42. package/dist/runtime/context-gc-tokens.d.ts +24 -0
  43. package/dist/runtime/context-gc-tokens.js +83 -0
  44. package/dist/runtime/context-gc.d.ts +66 -0
  45. package/dist/runtime/context-gc.js +909 -0
  46. package/dist/runtime/context-projection.d.ts +23 -0
  47. package/dist/runtime/context-projection.js +203 -0
  48. package/dist/runtime/environment-context.js +41 -2
  49. package/dist/runtime/execution-policy.js +13 -2
  50. package/dist/runtime/instructions.js +298 -2
  51. package/dist/runtime/normalize.js +40 -2
  52. package/dist/runtime/plan-output.js +95 -2
  53. package/dist/runtime/rollout-budget.d.ts +2 -0
  54. package/dist/runtime/rollout-budget.js +92 -2
  55. package/dist/runtime/token-budget.d.ts +2 -0
  56. package/dist/runtime/token-budget.js +44 -2
  57. package/dist/sdk/agent.js +1804 -2
  58. package/dist/sdk/types.d.ts +15 -5
  59. package/dist/sdk/types.js +1 -2
  60. package/dist/session/catalog.d.ts +5 -4
  61. package/dist/session/catalog.js +133 -2
  62. package/dist/session/context-gc-checkpoint.d.ts +148 -0
  63. package/dist/session/context-gc-checkpoint.js +665 -0
  64. package/dist/session/context-gc-replacements.d.ts +52 -0
  65. package/dist/session/context-gc-replacements.js +226 -0
  66. package/dist/session/context-gc-subagent.d.ts +43 -0
  67. package/dist/session/context-gc-subagent.js +267 -0
  68. package/dist/session/jsonl-store.d.ts +42 -4
  69. package/dist/session/jsonl-store.js +1657 -2
  70. package/dist/subagent/scheduler.d.ts +35 -2
  71. package/dist/subagent/scheduler.js +249 -2
  72. package/dist/subagent/session-controller.d.ts +94 -0
  73. package/dist/subagent/session-controller.js +675 -0
  74. package/dist/tools/agent-cancel.js +28 -2
  75. package/dist/tools/{task.d.ts → agent-message.d.ts} +2 -2
  76. package/dist/tools/agent-message.js +51 -0
  77. package/dist/tools/agent-start.js +84 -2
  78. package/dist/tools/agent-wait.js +53 -2
  79. package/dist/tools/apply-patch.js +478 -2
  80. package/dist/tools/code-mode.js +370 -2
  81. package/dist/tools/codex-image-gen.js +312 -2
  82. package/dist/tools/exec.js +455 -2
  83. package/dist/tools/execution-limits.d.ts +1 -0
  84. package/dist/tools/execution-limits.js +44 -2
  85. package/dist/tools/local-registry.js +233 -2
  86. package/dist/tools/local-workspace-tools.js +28 -2
  87. package/dist/tools/question.js +124 -2
  88. package/dist/tools/read-context.d.ts +16 -0
  89. package/dist/tools/read-context.js +85 -0
  90. package/dist/tools/read.d.ts +13 -0
  91. package/dist/tools/read.js +294 -2
  92. package/dist/tools/registry.js +76 -2
  93. package/dist/tools/runtime-context.d.ts +9 -2
  94. package/dist/tools/runtime-context.js +1 -2
  95. package/dist/tools/runtime.js +21 -2
  96. package/dist/tools/scheduler.js +41 -2
  97. package/dist/tools/shared/path-resolver.js +133 -2
  98. package/dist/tools/skill.js +37 -2
  99. package/dist/tools/todo-read.js +10 -2
  100. package/dist/tools/todo-write.js +65 -2
  101. package/dist/tools/todo.js +67 -2
  102. package/dist/tools/view-image.js +81 -2
  103. package/dist/tools/web-fetch.js +11 -2
  104. package/dist/tools/web-search.js +14 -2
  105. package/package.json +9 -7
  106. package/README.md +0 -3
  107. package/dist/runtime/prompt.d.ts +0 -9
  108. package/dist/runtime/prompt.js +0 -2
  109. package/dist/tools/shared/secret-redaction.d.ts +0 -4
  110. package/dist/tools/shared/secret-redaction.js +0 -2
  111. package/dist/tools/task-cancel.d.ts +0 -7
  112. package/dist/tools/task-cancel.js +0 -2
  113. package/dist/tools/task-output.d.ts +0 -7
  114. package/dist/tools/task-output.js +0 -2
  115. package/dist/tools/task-tools.d.ts +0 -36
  116. package/dist/tools/task-tools.js +0 -2
  117. package/dist/tools/task.js +0 -2
  118. package/dist/tools/todo-tools.d.ts +0 -25
  119. package/dist/tools/todo-tools.js +0 -2
@@ -0,0 +1,909 @@
1
+ import { ContextGcCheckpointCache, contextGcBaseFromRecords, contextGcLogicalPathKey, contextGcMinimumUsageGrowth, contextGcRecordSourceHash, contextGcTargetKey, normalizeContextGcReplacements } from '../session/context-gc-checkpoint.js';
2
+ import { contextGcApplyPatchInputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcStaleReadReplacement, normalizeContextGcExecResult } from '../session/context-gc-replacements.js';
3
+ import { contextGcSubagentNeedsRetainedPriorFinal, contextGcSubagentOutputReplacement, contextGcSubagentPriorFinalIsRetained, contextGcSubagentSnapshotIsSuperseded, contextGcSubagentSnapshotTurnId, normalizeContextGcSubagentSnapshot } from '../session/context-gc-subagent.js';
4
+ import { parseReadContextOutput } from '../tools/read-context.js';
5
+ import { CONTEXT_GC_TOKEN_ESTIMATOR, contextGcCacheImpact, contextGcMessageTokens, contextGcPrefixTokens, contextGcReclaimedTokens } from './context-gc-tokens.js';
6
+ import { applyContextGcReplacementsToMessages, providerToolCallId, providerToolName } from './context-projection.js';
7
+ export const CONTEXT_GC_POLICY = {
8
+ version: 1,
9
+ immediatePaybackRollouts: 3,
10
+ maximumPaybackRollouts: 20,
11
+ contextGrowthSampleWindow: 16,
12
+ minimumContextGrowthSamples: 6,
13
+ minimumObservationRollouts: 1,
14
+ stageAProtectionRollouts: 2,
15
+ largeExecProtectionRollouts: 6,
16
+ normalInputCostRate: 1,
17
+ cacheReadCostRate: 0.1,
18
+ cacheWriteCostRate: 1.25,
19
+ outputCostRate: 1,
20
+ minimumGrossReductionRate: 0.2,
21
+ minimumCacheInvestmentRoi: 0.1
22
+ };
23
+ const CONTEXT_GC_START_RATIO = 0.5;
24
+ const HIGH_WATER_GROSS_REDUCTION_START_RATIO = 0.7;
25
+ const HIGH_WATER_MINIMUM_GROSS_REDUCTION_RATE = 0.1;
26
+ const MINIMUM_CONTEXT_WINDOW_REDUCTION_RATIO = 0.05;
27
+ const COMPACT_CREDIT_ACTIVATION_RATIO = 0.8;
28
+ const COMPACT_THRESHOLD_RATIO = 0.9;
29
+ const IMMINENT_COMPACT_CREDIT_DISCOUNT = 0.8;
30
+ const LARGE_EXEC_TOKENS = 4_096;
31
+ export function evaluateContextGcEconomics(input) {
32
+ const reclaimedTokens = Math.max(0, input.reclaimedTokens);
33
+ const cacheReadTokens = Math.max(0, input.cacheReadTokens);
34
+ const preservedCachedPrefixTokens = clamp(input.preservedCachedPrefixTokens, 0, cacheReadTokens);
35
+ const invalidatedCachedSuffixTokens = cacheReadTokens - preservedCachedPrefixTokens;
36
+ const reclaimedCachedTokens = clamp(input.reclaimedCachedTokens, 0, invalidatedCachedSuffixTokens);
37
+ const invalidatedRetainedCachedTokens = Math.max(0, invalidatedCachedSuffixTokens - reclaimedCachedTokens);
38
+ const cacheRebuildPenaltyCostUnits = invalidatedRetainedCachedTokens *
39
+ (CONTEXT_GC_POLICY.cacheWriteCostRate - CONTEXT_GC_POLICY.cacheReadCostRate);
40
+ const reclaimedUncachedTokens = Math.max(0, reclaimedTokens - reclaimedCachedTokens);
41
+ const firstRolloutSavingsCostUnits = reclaimedCachedTokens * CONTEXT_GC_POLICY.cacheReadCostRate +
42
+ reclaimedUncachedTokens * CONTEXT_GC_POLICY.normalInputCostRate;
43
+ const steadyRolloutSavingsCostUnits = reclaimedTokens * CONTEXT_GC_POLICY.cacheReadCostRate;
44
+ const netAt = (rollouts) => input.compactDeferralCreditCostUnits -
45
+ cacheRebuildPenaltyCostUnits +
46
+ (rollouts <= 0
47
+ ? 0
48
+ : firstRolloutSavingsCostUnits + Math.max(0, rollouts - 1) * steadyRolloutSavingsCostUnits);
49
+ const maximumSearch = Math.max(CONTEXT_GC_POLICY.maximumPaybackRollouts + 1, 10_000);
50
+ const requiredPositiveNetRollouts = firstMatchingRollout(maximumSearch, (rollouts) => netAt(rollouts) > 0);
51
+ const requiredTargetRoiRollouts = cacheRebuildPenaltyCostUnits === 0
52
+ ? requiredPositiveNetRollouts
53
+ : firstMatchingRollout(maximumSearch, (rollouts) => netAt(rollouts) > 0 &&
54
+ netAt(rollouts) / cacheRebuildPenaltyCostUnits >=
55
+ CONTEXT_GC_POLICY.minimumCacheInvestmentRoi);
56
+ const targetRollouts = Number.isFinite(requiredTargetRoiRollouts)
57
+ ? requiredTargetRoiRollouts
58
+ : CONTEXT_GC_POLICY.maximumPaybackRollouts;
59
+ const expectedNetSavedCostUnits = netAt(targetRollouts);
60
+ const cacheInvestmentRoi = cacheRebuildPenaltyCostUnits === 0
61
+ ? 0
62
+ : expectedNetSavedCostUnits / cacheRebuildPenaltyCostUnits;
63
+ const grossReductionRate = reclaimedTokens / Math.max(1, Math.max(0, input.contextUsageTokens));
64
+ const minimumGrossReductionRate = Math.max(0, input.minimumGrossReductionRate ?? CONTEXT_GC_POLICY.minimumGrossReductionRate);
65
+ const minimumReclaimedTokens = Math.max(0, input.minimumReclaimedTokens ??
66
+ Math.ceil(Math.max(0, input.contextUsageTokens) * minimumGrossReductionRate));
67
+ const immediate = requiredTargetRoiRollouts <= CONTEXT_GC_POLICY.immediatePaybackRollouts;
68
+ const passes = reclaimedTokens > 0 &&
69
+ reclaimedTokens >= minimumReclaimedTokens &&
70
+ Number.isFinite(requiredTargetRoiRollouts) &&
71
+ (immediate ||
72
+ (requiredTargetRoiRollouts <= CONTEXT_GC_POLICY.maximumPaybackRollouts &&
73
+ input.contextGrowthSampleCount >= CONTEXT_GC_POLICY.minimumContextGrowthSamples &&
74
+ requiredTargetRoiRollouts <= input.projectedRolloutsUntilCompactAfterGc));
75
+ return {
76
+ cacheRebuildPenaltyCostUnits,
77
+ requiredPositiveNetRollouts,
78
+ requiredTargetRoiRollouts,
79
+ expectedNetSavedCostUnits,
80
+ cacheInvestmentRoi,
81
+ grossReductionRate,
82
+ passes
83
+ };
84
+ }
85
+ export function contextGcMinimumGrossReductionRate(usageRatio) {
86
+ if (!Number.isFinite(usageRatio) || usageRatio <= HIGH_WATER_GROSS_REDUCTION_START_RATIO)
87
+ return CONTEXT_GC_POLICY.minimumGrossReductionRate;
88
+ if (usageRatio >= COMPACT_THRESHOLD_RATIO)
89
+ return HIGH_WATER_MINIMUM_GROSS_REDUCTION_RATE;
90
+ const progress = (usageRatio - HIGH_WATER_GROSS_REDUCTION_START_RATIO) /
91
+ (COMPACT_THRESHOLD_RATIO - HIGH_WATER_GROSS_REDUCTION_START_RATIO);
92
+ return (CONTEXT_GC_POLICY.minimumGrossReductionRate -
93
+ progress *
94
+ (CONTEXT_GC_POLICY.minimumGrossReductionRate - HIGH_WATER_MINIMUM_GROSS_REDUCTION_RATE));
95
+ }
96
+ export function contextGcMinimumReclaimedTokens(contextUsageTokens, contextWindowTokens) {
97
+ if (!Number.isFinite(contextUsageTokens) ||
98
+ contextUsageTokens < 0 ||
99
+ !Number.isFinite(contextWindowTokens) ||
100
+ contextWindowTokens <= 0)
101
+ return 0;
102
+ const usageRatio = contextUsageTokens / contextWindowTokens;
103
+ const required = Math.max(contextUsageTokens * contextGcMinimumGrossReductionRate(usageRatio), contextWindowTokens * MINIMUM_CONTEXT_WINDOW_REDUCTION_RATIO);
104
+ return Math.ceil(required - 1e-9);
105
+ }
106
+ export function contextGcEligibleUsage(usage, contextWindowTokens) {
107
+ const contextUsageTokens = modelContextUsage(usage);
108
+ if (contextUsageTokens === undefined ||
109
+ !Number.isSafeInteger(contextWindowTokens) ||
110
+ contextWindowTokens <= 0)
111
+ return undefined;
112
+ const usageRatio = contextUsageTokens / contextWindowTokens;
113
+ if (usageRatio < CONTEXT_GC_START_RATIO || usageRatio >= COMPACT_THRESHOLD_RATIO)
114
+ return undefined;
115
+ return contextUsageTokens;
116
+ }
117
+ export function planContextGc(input) {
118
+ return inspectContextGc(input).plan;
119
+ }
120
+ export function inspectContextGc(input) {
121
+ const contextUsageTokens = contextGcEligibleUsage(input.usage, input.contextWindowTokens);
122
+ if (contextUsageTokens === undefined)
123
+ return {};
124
+ input = {
125
+ ...input,
126
+ tokenCache: new Map(),
127
+ checkpointCache: input.checkpointCache ?? new ContextGcCheckpointCache()
128
+ };
129
+ const usageRatio = contextUsageTokens / input.contextWindowTokens;
130
+ const usageRecord = latestUsageRecord(input.records, input.modelId);
131
+ if (!usageRecord)
132
+ return {};
133
+ const normalRolloutOrdinal = countSuccessfulNormalRollouts(input.records, input.modelId, input.contextWindowTokens);
134
+ const previous = input.checkpointCache.read(input.records, undefined, input.workspace);
135
+ if (previous) {
136
+ const minimumUsageGrowth = contextGcMinimumUsageGrowth(input.contextWindowTokens);
137
+ if (normalRolloutOrdinal - previous.payload.trigger.normalRolloutOrdinal < 2 ||
138
+ contextUsageTokens - previous.payload.trigger.contextInputTokens < minimumUsageGrowth)
139
+ return {};
140
+ }
141
+ if (input.messages.length !== input.messageSources.length)
142
+ return {};
143
+ const groups = buildToolGroups(input.records, input.messages, input.messageSources);
144
+ const existing = previous?.payload.replacements ?? [];
145
+ const existingTargets = new Set(existing.flatMap((replacement) => [
146
+ contextGcTargetKey(replacement.target),
147
+ ...(replacement.kind === 'TOOL_CALL_INPUT'
148
+ ? replacement.providerAliasTargets.map(contextGcTargetKey)
149
+ : [])
150
+ ]));
151
+ const stageA = collectStageACandidates(input, groups, existing, existingTargets);
152
+ const subagent = collectSubagentCandidates(input, groups, existingTargets);
153
+ const deterministicCandidates = [...stageA.eligible, ...subagent.eligible];
154
+ const deterministicProtectedTokens = stageA.protectedTokens + subagent.protectedTokens;
155
+ const stageAPlan = evaluateCandidateSet(input, existing, deterministicCandidates, {
156
+ stage: 'STAGE_A',
157
+ contextUsageTokens,
158
+ usageRatio,
159
+ usageRecord,
160
+ normalRolloutOrdinal,
161
+ protectedCandidateTokens: deterministicProtectedTokens
162
+ });
163
+ if (stageAPlan?.economics.passes) {
164
+ const plan = finalizePlan(input, stageAPlan);
165
+ return plan ? { plan } : {};
166
+ }
167
+ const exec = collectExecCandidates(input, groups, existingTargets);
168
+ const combined = evaluateCandidateSet(input, existing, [...deterministicCandidates, ...exec.eligible], {
169
+ stage: 'STAGE_A_AND_EXEC',
170
+ contextUsageTokens,
171
+ usageRatio,
172
+ usageRecord,
173
+ normalRolloutOrdinal,
174
+ protectedCandidateTokens: deterministicProtectedTokens + exec.protectedTokens
175
+ });
176
+ if (combined?.economics.passes) {
177
+ const plan = finalizePlan(input, combined);
178
+ return plan ? { plan } : {};
179
+ }
180
+ const retryAfterRollouts = Math.min(stageA.retryAfterRollouts, subagent.retryAfterRollouts, exec.retryAfterRollouts);
181
+ return deterministicProtectedTokens + exec.protectedTokens >=
182
+ contextGcMinimumReclaimedTokens(contextUsageTokens, input.contextWindowTokens) &&
183
+ Number.isFinite(retryAfterRollouts)
184
+ ? { retryAfterRollouts }
185
+ : {};
186
+ }
187
+ function finalizePlan(input, evaluated) {
188
+ if (!evaluated)
189
+ return undefined;
190
+ const base = contextGcBaseFromRecords(input.records, evaluated.boundarySequence);
191
+ if (!base)
192
+ return undefined;
193
+ const replacements = normalizeContextGcReplacements(evaluated.replacements);
194
+ if (!replacements || replacements.length === 0)
195
+ return undefined;
196
+ const metrics = {
197
+ stage: evaluated.stage,
198
+ tokenEstimator: CONTEXT_GC_TOKEN_ESTIMATOR,
199
+ beforeTokens: evaluated.beforeTokens,
200
+ afterTokens: evaluated.afterTokens,
201
+ reclaimedTokens: evaluated.reclaimedTokens,
202
+ grossReductionRate: evaluated.economics.grossReductionRate,
203
+ effectiveMinimumGrossReductionRate: evaluated.minimumGrossReductionRate,
204
+ minimumRequiredReclaimedTokens: evaluated.minimumReclaimedTokens,
205
+ cacheRebuildPenaltyCostUnits: evaluated.economics.cacheRebuildPenaltyCostUnits,
206
+ compactDeferralCreditCostUnits: evaluated.compactDeferralCreditCostUnits,
207
+ imminentCompactAvoidanceCreditCostUnits: evaluated.imminentCompactAvoidanceCreditCostUnits,
208
+ appliedCompactCreditCostUnits: evaluated.appliedCompactCreditCostUnits,
209
+ requiredPositiveNetRollouts: evaluated.economics.requiredPositiveNetRollouts,
210
+ requiredTargetRoiRollouts: evaluated.economics.requiredTargetRoiRollouts,
211
+ projectedRolloutsUntilCompactAfterGc: evaluated.projectedRolloutsUntilCompactAfterGc,
212
+ projectedTokensBeforeGc: evaluated.projectedTokensBeforeGc,
213
+ projectedTokensAfterGc: evaluated.projectedTokensAfterGc,
214
+ contextGrowthSampleCount: evaluated.contextGrowthSamples.length,
215
+ contextGrowthP75Tokens: evaluated.contextGrowthP75Tokens,
216
+ expectedNetSavedCostUnits: evaluated.economics.expectedNetSavedCostUnits,
217
+ cacheInvestmentRoi: evaluated.economics.cacheInvestmentRoi,
218
+ minimumObservedRollouts: evaluated.minimumObservedRollouts,
219
+ protectedCandidateTokens: evaluated.protectedCandidateTokens,
220
+ eligibleCandidateCount: evaluated.eligibleCandidateCount,
221
+ eligibleTargetCount: replacements.reduce((count, replacement) => count +
222
+ 1 +
223
+ (replacement.kind === 'TOOL_CALL_INPUT' ? replacement.providerAliasTargets.length : 0), 0),
224
+ cachePenaltyKnown: input.usage.cacheReadTokens !== undefined
225
+ };
226
+ const draft = {
227
+ version: 1,
228
+ base,
229
+ boundarySequence: evaluated.boundarySequence,
230
+ sourceProjectionHash: '',
231
+ replacements,
232
+ trigger: {
233
+ usageSequence: evaluated.usageRecord.sequence,
234
+ contextInputTokens: evaluated.contextUsageTokens,
235
+ contextWindowTokens: input.contextWindowTokens,
236
+ normalRolloutOrdinal: evaluated.normalRolloutOrdinal,
237
+ modelId: input.modelId,
238
+ platform: input.platform
239
+ },
240
+ economics: CONTEXT_GC_POLICY,
241
+ metrics
242
+ };
243
+ return { stage: evaluated.stage, draft, metrics };
244
+ }
245
+ function evaluateCandidateSet(input, existing, candidates, state) {
246
+ if (candidates.length === 0)
247
+ return undefined;
248
+ const replacements = normalizeContextGcReplacements([
249
+ ...existing,
250
+ ...candidates.flatMap((candidate) => candidate.replacements)
251
+ ]);
252
+ if (!replacements)
253
+ return undefined;
254
+ const projected = applyContextGcReplacementsToMessages(input.messages, input.messageSources, replacements);
255
+ const reclaimedTokens = contextGcReclaimedTokens(input, input.messages, projected.messages);
256
+ const minimumGrossReductionRate = contextGcMinimumGrossReductionRate(state.usageRatio);
257
+ const minimumReclaimedTokens = contextGcMinimumReclaimedTokens(state.contextUsageTokens, input.contextWindowTokens);
258
+ if (reclaimedTokens < minimumReclaimedTokens)
259
+ return undefined;
260
+ const beforeTokens = contextGcPrefixTokens(input) + contextGcMessageTokens(input, input.messages);
261
+ const afterTokens = contextGcPrefixTokens(input) + contextGcMessageTokens(input, projected.messages);
262
+ if (reclaimedTokens === 0)
263
+ return undefined;
264
+ const cache = contextGcCacheImpact(input, input.messages, projected.messages, input.usage.cacheReadTokens ?? 0, reclaimedTokens);
265
+ const growthSamples = collectContextGrowthSamples(input.records, input.modelId, input.contextWindowTokens, input.workspace, input.checkpointCache);
266
+ const contextGrowthP75Tokens = percentile75(growthSamples);
267
+ const compactThresholdTokens = Math.floor(input.contextWindowTokens * COMPACT_THRESHOLD_RATIO);
268
+ // Usage describes the request before its generated output and tool results.
269
+ const pendingMessages = input.messages.filter((_message, index) => input.messageSources[index].sequence > state.usageRecord.sequence);
270
+ const pendingTokens = Math.max(0, input.usage.outputTokens ?? 0) + contextGcMessageTokens(input, pendingMessages);
271
+ const projectedTokensBeforeGc = state.contextUsageTokens + pendingTokens;
272
+ const projectedTokensAfterGc = Math.max(0, projectedTokensBeforeGc - reclaimedTokens);
273
+ if (projectedTokensAfterGc >= compactThresholdTokens)
274
+ return undefined;
275
+ const projectedRolloutsUntilCompactAfterGc = contextGrowthP75Tokens <= 0
276
+ ? 0
277
+ : Math.max(0, Math.floor((compactThresholdTokens - projectedTokensAfterGc) / Math.max(1, contextGrowthP75Tokens)));
278
+ const compactDeferralCreditCostUnits = state.usageRatio >= COMPACT_CREDIT_ACTIVATION_RATIO
279
+ ? compactDeferralCredit(input, reclaimedTokens, compactThresholdTokens)
280
+ : 0;
281
+ const imminentCompactAvoidanceCreditCostUnits = contextGcImminentCompactAvoidanceCredit({
282
+ contextUsageTokens: state.contextUsageTokens,
283
+ contextWindowTokens: input.contextWindowTokens,
284
+ compactThresholdTokens,
285
+ projectedTokensBeforeGc,
286
+ projectedTokensAfterGc,
287
+ contextGrowthP75Tokens
288
+ });
289
+ const appliedCompactCreditCostUnits = contextGcAppliedCompactCreditCostUnits(compactDeferralCreditCostUnits, imminentCompactAvoidanceCreditCostUnits);
290
+ const economics = evaluateContextGcEconomics({
291
+ contextUsageTokens: state.contextUsageTokens,
292
+ reclaimedTokens,
293
+ reclaimedCachedTokens: cache.reclaimedCachedTokens,
294
+ cacheReadTokens: input.usage.cacheReadTokens ?? 0,
295
+ preservedCachedPrefixTokens: cache.preservedCachedPrefixTokens,
296
+ compactDeferralCreditCostUnits: appliedCompactCreditCostUnits,
297
+ projectedRolloutsUntilCompactAfterGc,
298
+ contextGrowthSampleCount: growthSamples.length,
299
+ minimumGrossReductionRate,
300
+ minimumReclaimedTokens
301
+ });
302
+ return {
303
+ ...state,
304
+ economics,
305
+ replacements,
306
+ beforeTokens,
307
+ afterTokens,
308
+ reclaimedTokens,
309
+ contextGrowthSamples: growthSamples,
310
+ contextGrowthP75Tokens,
311
+ minimumGrossReductionRate,
312
+ minimumReclaimedTokens,
313
+ projectedRolloutsUntilCompactAfterGc,
314
+ projectedTokensBeforeGc,
315
+ projectedTokensAfterGc,
316
+ compactDeferralCreditCostUnits,
317
+ imminentCompactAvoidanceCreditCostUnits,
318
+ appliedCompactCreditCostUnits,
319
+ boundarySequence: input.records.at(-1)?.sequence ?? state.usageRecord.sequence,
320
+ minimumObservedRollouts: Math.min(...candidates.map((candidate) => candidate.observedRollouts)),
321
+ eligibleCandidateCount: candidates.length
322
+ };
323
+ }
324
+ function collectStageACandidates(input, groups, existingReplacements, existingTargets) {
325
+ const eligible = [];
326
+ let protectedTokens = 0;
327
+ let retryAfterRollouts = Infinity;
328
+ const existingReadReplacements = new Map(existingReplacements.flatMap((replacement) => replacement.kind === 'READ_OUTPUT_ITEMS'
329
+ ? [[contextGcTargetKey(replacement.target), replacement]]
330
+ : []));
331
+ const reads = [...groups.values()].filter((group) => group.toolName === 'read' &&
332
+ group.call?.message.role === 'tool_call' &&
333
+ group.result?.message.role === 'tool');
334
+ const claimedReadItems = new Set([...existingReadReplacements.values()].flatMap((replacement) => replacement.items.map((item) => `${replacement.target.recordId}:${item.index}`)));
335
+ const patches = [...groups.values()]
336
+ .flatMap((patch) => {
337
+ if (patch.toolName !== 'apply_patch' ||
338
+ !patch.call ||
339
+ !patch.result ||
340
+ patch.failed ||
341
+ !patch.completed)
342
+ return [];
343
+ const result = contextGcPatchResultFromCompletedPayload(patch.completed.payload);
344
+ return result ? [{ patch, result, evidenceSequence: patch.result.record.sequence }] : [];
345
+ })
346
+ .sort((left, right) => left.evidenceSequence - right.evidenceSequence);
347
+ for (const { patch, result, evidenceSequence } of patches) {
348
+ const patchCall = patch.call;
349
+ const observedRollouts = successfulRolloutsAfter(input.records, evidenceSequence, input.modelId, input.contextWindowTokens);
350
+ const replacements = [];
351
+ const patchTarget = targetFor(patchCall.record, patchCall.message);
352
+ if (patchTarget && !existingTargets.has(contextGcTargetKey(patchTarget)))
353
+ replacements.push({
354
+ kind: 'TOOL_CALL_INPUT',
355
+ toolName: 'apply_patch',
356
+ callId: patch.callId,
357
+ target: patchTarget,
358
+ replacementArguments: contextGcApplyPatchInputReplacement(patchCall.message.arguments, result, evidenceSequence),
359
+ providerAliasTargets: patch.aliases
360
+ .map(({ record, message }) => targetFor(record, message))
361
+ .filter((target) => target !== undefined),
362
+ evidenceSequence
363
+ });
364
+ for (const read of reads) {
365
+ if (read.result.record.sequence >= evidenceSequence)
366
+ continue;
367
+ const parsed = parseReadContextOutput(read.result.message.content);
368
+ if (!parsed)
369
+ continue;
370
+ const selected = parsed.items.flatMap((item) => {
371
+ const itemKey = `${read.result.record.recordId}:${item.index}`;
372
+ if (claimedReadItems.has(itemKey))
373
+ return [];
374
+ const pathKey = contextGcLogicalPathKey(item.path, input.workspace, input.platform);
375
+ const matched = result.files.find((file) => file.operation !== 'added' &&
376
+ contextGcLogicalPathKey(file.path, input.workspace, input.platform) === pathKey);
377
+ if (!pathKey || !matched || matched.operation === 'added')
378
+ return [];
379
+ claimedReadItems.add(itemKey);
380
+ return [
381
+ {
382
+ index: item.index,
383
+ path: item.path,
384
+ replacement: contextGcStaleReadReplacement({
385
+ path: item.path,
386
+ content: item.content,
387
+ evidenceSequence,
388
+ supersededByCallId: patch.callId,
389
+ operation: matched.operation
390
+ }),
391
+ evidenceSequence
392
+ }
393
+ ];
394
+ });
395
+ if (selected.length === 0)
396
+ continue;
397
+ const target = targetFor(read.result.record, read.result.message);
398
+ if (!target)
399
+ continue;
400
+ const existingRead = existingReadReplacements.get(contextGcTargetKey(target));
401
+ replacements.push({
402
+ kind: 'READ_OUTPUT_ITEMS',
403
+ callId: read.callId,
404
+ target,
405
+ items: [...(existingRead?.items ?? []), ...selected],
406
+ evidenceSequences: [
407
+ ...new Set([...(existingRead?.evidenceSequences ?? []), evidenceSequence])
408
+ ]
409
+ });
410
+ }
411
+ if (replacements.length === 0)
412
+ continue;
413
+ const candidate = {
414
+ replacements,
415
+ observedRollouts,
416
+ evidenceSequence
417
+ };
418
+ if (observedRollouts >= CONTEXT_GC_POLICY.stageAProtectionRollouts)
419
+ eligible.push(candidate);
420
+ else {
421
+ protectedTokens += candidateTokens(input, candidate);
422
+ retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.stageAProtectionRollouts - observedRollouts);
423
+ }
424
+ }
425
+ return { eligible, protectedTokens, retryAfterRollouts };
426
+ }
427
+ function collectSubagentCandidates(input, groups, existingTargets) {
428
+ const eligible = [];
429
+ let protectedTokens = 0;
430
+ let retryAfterRollouts = Infinity;
431
+ const invocations = [...groups.values()]
432
+ .flatMap((group) => {
433
+ if (!isContextGcSubagentToolName(group.toolName) ||
434
+ !group.call ||
435
+ !group.result ||
436
+ group.failed ||
437
+ !group.completed ||
438
+ group.call.message.role !== 'tool_call' ||
439
+ group.result.message.role !== 'tool')
440
+ return [];
441
+ const snapshot = normalizeContextGcSubagentSnapshot(group.result.message.content, group.completed.payload, group.toolName);
442
+ const turnId = snapshot && contextGcSubagentSnapshotTurnId(snapshot);
443
+ return snapshot && turnId ? [{ group, snapshot, turnId }] : [];
444
+ })
445
+ .sort((left, right) => left.group.result.record.sequence - right.group.result.record.sequence);
446
+ for (const [index, source] of invocations.entries()) {
447
+ if (source.group.toolName !== 'agent_wait' && source.group.toolName !== 'agent_message')
448
+ continue;
449
+ const sourceTarget = targetFor(source.group.result.record, source.group.result.message);
450
+ if (!sourceTarget || existingTargets.has(contextGcTargetKey(sourceTarget)))
451
+ continue;
452
+ const needsRetainedPriorFinal = contextGcSubagentNeedsRetainedPriorFinal(source.snapshot);
453
+ const retainedPriorFinal = needsRetainedPriorFinal
454
+ ? invocations
455
+ .slice(0, index)
456
+ .findLast((candidate) => contextGcSubagentPriorFinalIsRetained(source.snapshot, candidate.snapshot))
457
+ : undefined;
458
+ if (needsRetainedPriorFinal && retainedPriorFinal === undefined)
459
+ continue;
460
+ const terminal = invocations
461
+ .slice(index + 1)
462
+ .find((candidate) => candidate.snapshot.agentId === source.snapshot.agentId &&
463
+ candidate.turnId === source.turnId &&
464
+ contextGcSubagentSnapshotIsSuperseded(source.snapshot, candidate.snapshot, retainedPriorFinal?.snapshot));
465
+ if (!terminal)
466
+ continue;
467
+ const sourceContent = source.group.result.message.content;
468
+ if (typeof sourceContent !== 'string')
469
+ continue;
470
+ const evidenceSequence = terminal.group.result.record.sequence;
471
+ const observedRollouts = successfulRolloutsAfter(input.records, evidenceSequence, input.modelId, input.contextWindowTokens);
472
+ const replacement = {
473
+ kind: 'SUBAGENT_OUTPUT',
474
+ toolName: source.group.toolName,
475
+ callId: source.group.callId,
476
+ target: sourceTarget,
477
+ replacementContent: contextGcSubagentOutputReplacement({
478
+ sourceContent,
479
+ source: source.snapshot,
480
+ terminal: terminal.snapshot,
481
+ terminalCallId: terminal.group.callId,
482
+ evidenceSequence
483
+ }),
484
+ terminalToolName: terminal.group.toolName,
485
+ terminalCallId: terminal.group.callId,
486
+ evidenceSequence,
487
+ ...(retainedPriorFinal === undefined
488
+ ? {}
489
+ : {
490
+ retainedPriorFinal: {
491
+ toolName: retainedPriorFinal.group.toolName,
492
+ callId: retainedPriorFinal.group.callId,
493
+ evidenceSequence: retainedPriorFinal.group.result.record.sequence
494
+ }
495
+ })
496
+ };
497
+ const candidate = {
498
+ replacements: [replacement],
499
+ observedRollouts,
500
+ evidenceSequence
501
+ };
502
+ if (observedRollouts >= CONTEXT_GC_POLICY.stageAProtectionRollouts)
503
+ eligible.push(candidate);
504
+ else {
505
+ protectedTokens += candidateTokens(input, candidate);
506
+ retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.stageAProtectionRollouts - observedRollouts);
507
+ }
508
+ }
509
+ return { eligible, protectedTokens, retryAfterRollouts };
510
+ }
511
+ function collectExecCandidates(input, groups, existingTargets) {
512
+ const eligible = [];
513
+ let protectedTokens = 0;
514
+ let retryAfterRollouts = Infinity;
515
+ const execGroups = [...groups.values()]
516
+ .filter((group) => group.toolName === 'exec')
517
+ .sort((left, right) => (left.call?.record.sequence ?? Number.MAX_SAFE_INTEGER) -
518
+ (right.call?.record.sequence ?? Number.MAX_SAFE_INTEGER));
519
+ const invocations = new Map();
520
+ for (const group of execGroups) {
521
+ if (!group.call ||
522
+ !group.result ||
523
+ group.failed ||
524
+ !group.completed ||
525
+ group.call.message.role !== 'tool_call' ||
526
+ group.result.message.role !== 'tool' ||
527
+ !isRecord(group.call.message.arguments))
528
+ continue;
529
+ const result = normalizeContextGcExecResult(group.result.message.content, group.completed.payload);
530
+ if (result)
531
+ invocations.set(group.callId, {
532
+ group,
533
+ input: group.call.message.arguments,
534
+ result
535
+ });
536
+ }
537
+ for (const startGroup of execGroups) {
538
+ const start = invocations.get(startGroup.callId);
539
+ if (!start || start.input.action !== 'start')
540
+ continue;
541
+ const lifecycle = [start];
542
+ let processId;
543
+ let terminal = start.result.status !== 'running';
544
+ if (!terminal) {
545
+ processId = typeof start.result.processId === 'string' ? start.result.processId : undefined;
546
+ if (!processId)
547
+ continue;
548
+ const relatedGroups = execGroups.filter((group) => (group.call?.record.sequence ?? 0) > start.group.call.record.sequence &&
549
+ isRecord(group.call?.message.arguments) &&
550
+ group.call.message.arguments.processId === processId);
551
+ if (relatedGroups.some((group) => !invocations.has(group.callId)) ||
552
+ relatedGroups.length === 0)
553
+ continue;
554
+ for (const relatedGroup of relatedGroups) {
555
+ const invocation = invocations.get(relatedGroup.callId);
556
+ lifecycle.push(invocation);
557
+ if (invocation.result.status !== 'running') {
558
+ terminal = true;
559
+ break;
560
+ }
561
+ }
562
+ }
563
+ if (!terminal || (processId && input.activeProcessIds.has(processId)))
564
+ continue;
565
+ const final = lifecycle.at(-1);
566
+ const visibleTokens = contextGcMessageTokens(input, lifecycle.flatMap((invocation) => [
567
+ invocation.group.call.message,
568
+ invocation.group.result.message,
569
+ ...invocation.group.aliases.map((alias) => alias.message)
570
+ ]));
571
+ if (visibleTokens < LARGE_EXEC_TOKENS)
572
+ continue;
573
+ const evidenceSequence = final.group.result.record.sequence;
574
+ const observedRollouts = successfulRolloutsAfter(input.records, evidenceSequence, input.modelId, input.contextWindowTokens);
575
+ const replacements = [];
576
+ for (const invocation of lifecycle) {
577
+ const callTarget = targetFor(invocation.group.call.record, invocation.group.call.message);
578
+ const resultTarget = targetFor(invocation.group.result.record, invocation.group.result.message);
579
+ if (!callTarget || !resultTarget) {
580
+ replacements.splice(0);
581
+ break;
582
+ }
583
+ if (!existingTargets.has(contextGcTargetKey(callTarget)))
584
+ replacements.push({
585
+ kind: 'TOOL_CALL_INPUT',
586
+ toolName: 'exec',
587
+ callId: invocation.group.callId,
588
+ target: callTarget,
589
+ replacementArguments: contextGcExecInputReplacement(invocation.input),
590
+ providerAliasTargets: invocation.group.aliases
591
+ .map(({ record, message }) => targetFor(record, message))
592
+ .filter((target) => target !== undefined),
593
+ evidenceSequence
594
+ });
595
+ if (!existingTargets.has(contextGcTargetKey(resultTarget)))
596
+ replacements.push({
597
+ kind: 'EXEC_OUTPUT',
598
+ callId: invocation.group.callId,
599
+ target: resultTarget,
600
+ replacementContent: contextGcExecOutputReplacement(invocation.result),
601
+ evidenceSequence
602
+ });
603
+ }
604
+ if (replacements.length === 0)
605
+ continue;
606
+ const candidate = {
607
+ replacements,
608
+ observedRollouts,
609
+ evidenceSequence
610
+ };
611
+ if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts)
612
+ eligible.push(candidate);
613
+ else {
614
+ protectedTokens += candidateTokens(input, candidate);
615
+ retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - observedRollouts);
616
+ }
617
+ }
618
+ return { eligible, protectedTokens, retryAfterRollouts };
619
+ }
620
+ function isContextGcSubagentToolName(value) {
621
+ return (value === 'agent_start' ||
622
+ value === 'agent_wait' ||
623
+ value === 'agent_message' ||
624
+ value === 'agent_cancel');
625
+ }
626
+ function buildToolGroups(records, messages, sources) {
627
+ const groups = new Map();
628
+ const group = (callId, toolName = '') => {
629
+ const current = groups.get(callId);
630
+ if (current) {
631
+ if (!current.toolName && toolName)
632
+ current.toolName = toolName;
633
+ return current;
634
+ }
635
+ const created = { callId, toolName, aliases: [] };
636
+ groups.set(callId, created);
637
+ return created;
638
+ };
639
+ for (const [index, message] of messages.entries()) {
640
+ const record = sources[index];
641
+ if (message.role === 'tool_call' && message.callId && message.name)
642
+ group(message.callId, message.name).call = { message, record };
643
+ else if (message.role === 'tool' && message.callId)
644
+ group(message.callId).result = { message, record };
645
+ else if (message.role === 'provider') {
646
+ const callId = providerToolCallId(message);
647
+ if (callId)
648
+ group(callId, providerToolName(message)).aliases.push({ message, record });
649
+ }
650
+ }
651
+ for (const record of records) {
652
+ if (!isRecord(record.payload))
653
+ continue;
654
+ const type = record.payload.type;
655
+ const callId = record.payload.callId;
656
+ const toolName = record.payload.toolName;
657
+ if (typeof callId !== 'string' || typeof toolName !== 'string')
658
+ continue;
659
+ const current = group(callId, toolName);
660
+ if (type === 'tool.completed')
661
+ current.completed = record;
662
+ else if (type === 'tool.failed')
663
+ current.failed = record;
664
+ }
665
+ return groups;
666
+ }
667
+ function successfulRolloutsAfter(records, evidenceSequence, modelId, contextWindowTokens) {
668
+ return countSuccessfulNormalRollouts(records.filter((record) => record.sequence > evidenceSequence), modelId, contextWindowTokens, modelConfigurationAt(records, evidenceSequence, modelId, contextWindowTokens));
669
+ }
670
+ function countSuccessfulNormalRollouts(records, modelId, contextWindowTokens, initial = {
671
+ modelId,
672
+ contextWindowTokens
673
+ }) {
674
+ let activeModelId = initial.modelId;
675
+ let activeContextWindowTokens = initial.contextWindowTokens;
676
+ let count = 0;
677
+ for (const record of records) {
678
+ if (record.type === 'execution.header' && isRecord(record.payload)) {
679
+ activeModelId =
680
+ typeof record.payload.modelId === 'string' ? record.payload.modelId : activeModelId;
681
+ activeContextWindowTokens =
682
+ typeof record.payload.contextWindowTokens === 'number' &&
683
+ Number.isSafeInteger(record.payload.contextWindowTokens) &&
684
+ record.payload.contextWindowTokens > 0
685
+ ? record.payload.contextWindowTokens
686
+ : activeContextWindowTokens;
687
+ continue;
688
+ }
689
+ if (activeModelId === modelId &&
690
+ activeContextWindowTokens === contextWindowTokens &&
691
+ record.type === 'model.attempt' &&
692
+ isRecord(record.payload) &&
693
+ record.payload.purpose === 'EXECUTION' &&
694
+ record.payload.state === 'SUCCEEDED')
695
+ count++;
696
+ }
697
+ return count;
698
+ }
699
+ function modelConfigurationAt(records, boundarySequence, fallbackModelId, fallbackContextWindowTokens) {
700
+ let modelId = fallbackModelId;
701
+ let contextWindowTokens = fallbackContextWindowTokens;
702
+ for (const record of records) {
703
+ if (record.sequence > boundarySequence)
704
+ break;
705
+ if (record.type !== 'execution.header' || !isRecord(record.payload))
706
+ continue;
707
+ if (typeof record.payload.modelId === 'string')
708
+ modelId = record.payload.modelId;
709
+ if (typeof record.payload.contextWindowTokens === 'number' &&
710
+ Number.isSafeInteger(record.payload.contextWindowTokens) &&
711
+ record.payload.contextWindowTokens > 0)
712
+ contextWindowTokens = record.payload.contextWindowTokens;
713
+ }
714
+ return { modelId, contextWindowTokens };
715
+ }
716
+ function latestUsageRecord(records, selectedModelId) {
717
+ let activeModelId = selectedModelId;
718
+ let latest;
719
+ for (const record of records) {
720
+ if (record.type === 'execution.header') {
721
+ const modelId = modelIdFromRecord(record);
722
+ if (modelId)
723
+ activeModelId = modelId;
724
+ }
725
+ else if (activeModelId === selectedModelId &&
726
+ isRecord(record.payload) &&
727
+ record.payload.type === 'usage.updated' &&
728
+ modelContextUsage(record.payload) !== undefined)
729
+ latest = record;
730
+ }
731
+ return latest;
732
+ }
733
+ function collectContextGrowthSamples(records, modelId, contextWindowTokens, workspace, checkpointCache) {
734
+ const previous = checkpointCache.read(records, undefined, workspace);
735
+ const base = contextGcBaseFromRecords(records);
736
+ const startSequence = previous?.record.sequence ?? (base?.type === 'COMPACT' ? base.sequence : base?.firstSequence);
737
+ let activeModelId = modelId;
738
+ let activeWindow = contextWindowTokens;
739
+ let previousUsage;
740
+ const samples = [];
741
+ for (const record of records) {
742
+ if (startSequence !== undefined && record.sequence <= startSequence)
743
+ continue;
744
+ if (record.type === 'execution.header' && isRecord(record.payload)) {
745
+ const nextModel = modelIdFromRecord(record);
746
+ const nextWindow = record.payload.contextWindowTokens;
747
+ if (nextModel !== modelId ||
748
+ (typeof nextWindow === 'number' && nextWindow !== contextWindowTokens)) {
749
+ activeModelId = nextModel ?? activeModelId;
750
+ activeWindow =
751
+ typeof nextWindow === 'number' && Number.isSafeInteger(nextWindow)
752
+ ? nextWindow
753
+ : activeWindow;
754
+ previousUsage = undefined;
755
+ }
756
+ else {
757
+ activeModelId = modelId;
758
+ activeWindow = contextWindowTokens;
759
+ }
760
+ continue;
761
+ }
762
+ if (activeModelId !== modelId ||
763
+ activeWindow !== contextWindowTokens ||
764
+ !isRecord(record.payload) ||
765
+ record.payload.type !== 'usage.updated')
766
+ continue;
767
+ const usage = modelContextUsage(record.payload);
768
+ if (usage === undefined) {
769
+ previousUsage = undefined;
770
+ continue;
771
+ }
772
+ if (previousUsage !== undefined && usage > previousUsage)
773
+ samples.push(usage - previousUsage);
774
+ previousUsage = usage;
775
+ }
776
+ return samples.slice(-CONTEXT_GC_POLICY.contextGrowthSampleWindow);
777
+ }
778
+ export function contextGcImminentCompactAvoidanceCredit(input) {
779
+ if (input.contextUsageTokens >= input.compactThresholdTokens ||
780
+ input.contextGrowthP75Tokens <= 0 ||
781
+ input.projectedTokensBeforeGc < input.compactThresholdTokens)
782
+ return 0;
783
+ const safetyMarginTokens = Math.max(input.contextWindowTokens * MINIMUM_CONTEXT_WINDOW_REDUCTION_RATIO, input.contextGrowthP75Tokens * CONTEXT_GC_POLICY.immediatePaybackRollouts);
784
+ if (input.projectedTokensAfterGc > input.compactThresholdTokens - safetyMarginTokens)
785
+ return 0;
786
+ return (input.contextUsageTokens *
787
+ CONTEXT_GC_POLICY.normalInputCostRate *
788
+ IMMINENT_COMPACT_CREDIT_DISCOUNT);
789
+ }
790
+ export function contextGcAppliedCompactCreditCostUnits(compactDeferralCreditCostUnits, imminentCompactAvoidanceCreditCostUnits) {
791
+ return Math.max(0, compactDeferralCreditCostUnits, imminentCompactAvoidanceCreditCostUnits);
792
+ }
793
+ function compactDeferralCredit(input, reclaimedTokens, compactThresholdTokens) {
794
+ const base = contextGcBaseFromRecords(input.records);
795
+ if (base?.type !== 'COMPACT')
796
+ return 0;
797
+ const compact = input.records.find((record) => record.sequence === base.sequence);
798
+ if (!compact || !isRecord(compact.payload))
799
+ return 0;
800
+ if (compact.payload.modelId !== input.modelId ||
801
+ compact.payload.contextWindowTokens !== input.contextWindowTokens ||
802
+ !isRecord(compact.payload.usage))
803
+ return 0;
804
+ const usage = compact.payload.usage;
805
+ const contextInput = modelContextUsage(usage);
806
+ const cacheRead = nonnegativeNumber(usage.cacheReadTokens);
807
+ const cacheWrite = nonnegativeNumber(usage.cacheWriteTokens);
808
+ const output = nonnegativeNumber(usage.outputTokens);
809
+ if (contextInput === undefined ||
810
+ cacheRead === undefined ||
811
+ cacheWrite === undefined ||
812
+ output === undefined)
813
+ return 0;
814
+ const baseline = firstMatchingUsageAfter(input.records, compact.sequence, input.modelId, input.contextWindowTokens);
815
+ if (baseline === undefined || baseline >= compactThresholdTokens)
816
+ return 0;
817
+ const fresh = Math.max(0, contextInput - cacheRead - cacheWrite);
818
+ const compactCost = fresh * CONTEXT_GC_POLICY.normalInputCostRate +
819
+ cacheRead * CONTEXT_GC_POLICY.cacheReadCostRate +
820
+ cacheWrite * CONTEXT_GC_POLICY.cacheWriteCostRate +
821
+ output * CONTEXT_GC_POLICY.outputCostRate;
822
+ const cycleCapacity = Math.max(1, compactThresholdTokens - baseline);
823
+ return compactCost * clamp(reclaimedTokens / cycleCapacity, 0, 1);
824
+ }
825
+ function firstMatchingUsageAfter(records, sequence, modelId, contextWindowTokens) {
826
+ let activeModelId = modelId;
827
+ let activeContextWindowTokens = contextWindowTokens;
828
+ for (const record of records) {
829
+ if (record.sequence <= sequence)
830
+ continue;
831
+ if (record.type === 'execution.header' && isRecord(record.payload)) {
832
+ if (typeof record.payload.modelId === 'string')
833
+ activeModelId = record.payload.modelId;
834
+ if (typeof record.payload.contextWindowTokens === 'number' &&
835
+ Number.isSafeInteger(record.payload.contextWindowTokens) &&
836
+ record.payload.contextWindowTokens > 0)
837
+ activeContextWindowTokens = record.payload.contextWindowTokens;
838
+ continue;
839
+ }
840
+ if (activeModelId !== modelId ||
841
+ activeContextWindowTokens !== contextWindowTokens ||
842
+ !isRecord(record.payload) ||
843
+ record.payload.type !== 'usage.updated')
844
+ continue;
845
+ const usage = modelContextUsage(record.payload);
846
+ if (usage !== undefined)
847
+ return usage;
848
+ }
849
+ return undefined;
850
+ }
851
+ function candidateTokens(input, candidate) {
852
+ const targets = new Set(candidate.replacements.flatMap((replacement) => [
853
+ contextGcTargetKey(replacement.target),
854
+ ...(replacement.kind === 'TOOL_CALL_INPUT'
855
+ ? replacement.providerAliasTargets.map(contextGcTargetKey)
856
+ : [])
857
+ ]));
858
+ return contextGcMessageTokens(input, input.messages.filter((message, index) => {
859
+ const target = targetFor(input.messageSources[index], message);
860
+ return target !== undefined && targets.has(contextGcTargetKey(target));
861
+ }));
862
+ }
863
+ function targetFor(record, message) {
864
+ if (record.type !== 'model.context' ||
865
+ (message.role !== 'tool_call' && message.role !== 'tool' && message.role !== 'provider'))
866
+ return undefined;
867
+ const callId = message.role === 'provider' ? providerToolCallId(message) : message.callId;
868
+ const toolName = message.role === 'provider' ? providerToolName(message) : message.name;
869
+ return {
870
+ sequence: record.sequence,
871
+ recordId: record.recordId,
872
+ role: message.role,
873
+ ...(callId === undefined ? {} : { callId }),
874
+ ...(toolName === undefined ? {} : { toolName }),
875
+ sourceHash: contextGcRecordSourceHash(record)
876
+ };
877
+ }
878
+ function modelContextUsage(value) {
879
+ const context = value.contextInputTokens ?? value.inputTokens;
880
+ return typeof context === 'number' && Number.isSafeInteger(context) && context >= 0
881
+ ? context
882
+ : undefined;
883
+ }
884
+ function modelIdFromRecord(record) {
885
+ if (!isRecord(record.payload))
886
+ return undefined;
887
+ return typeof record.payload.modelId === 'string' ? record.payload.modelId : undefined;
888
+ }
889
+ function percentile75(values) {
890
+ if (values.length === 0)
891
+ return 0;
892
+ const sorted = [...values].sort((left, right) => left - right);
893
+ return sorted[Math.max(0, Math.ceil(sorted.length * 0.75) - 1)];
894
+ }
895
+ function firstMatchingRollout(limit, predicate) {
896
+ for (let rollouts = 0; rollouts <= limit; rollouts++)
897
+ if (predicate(rollouts))
898
+ return rollouts;
899
+ return Number.POSITIVE_INFINITY;
900
+ }
901
+ function nonnegativeNumber(value) {
902
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
903
+ }
904
+ function clamp(value, minimum, maximum) {
905
+ return Math.min(maximum, Math.max(minimum, value));
906
+ }
907
+ function isRecord(value) {
908
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
909
+ }