@myagentroam/agent 0.11.52 → 0.11.53
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.
- package/dist/model/anthropic-messages.d.ts +4 -0
- package/dist/model/anthropic-messages.js +1 -1
- package/dist/model/native-context.d.ts +2 -0
- package/dist/model/native-context.js +26 -0
- package/dist/runtime/context/gc-candidates-code.js +36 -21
- package/dist/runtime/context/gc-candidates-exec.js +33 -56
- package/dist/runtime/context/gc-candidates-read-patch.d.ts +1 -1
- package/dist/runtime/context/gc-candidates-read-patch.js +98 -15
- package/dist/runtime/context/gc-candidates-web.js +172 -8
- package/dist/runtime/context/gc-contracts.d.ts +2 -0
- package/dist/runtime/context/gc-economics.d.ts +0 -6
- package/dist/runtime/context/gc-economics.js +1 -23
- package/dist/runtime/context/gc-tokens.js +18 -8
- package/dist/runtime/context/gc.d.ts +1 -3
- package/dist/runtime/context/gc.js +19 -96
- package/dist/runtime/context/projection.js +11 -1
- package/dist/session/code-mode-search-projection.d.ts +5 -0
- package/dist/session/code-mode-search-projection.js +46 -0
- package/dist/session/context-gc-checkpoint.js +233 -17
- package/dist/session/context-gc-contract.d.ts +3 -2
- package/dist/session/context-gc-exec-lifecycle.d.ts +11 -0
- package/dist/session/context-gc-exec-lifecycle.js +41 -0
- package/dist/session/context-gc-markup.d.ts +1 -0
- package/dist/session/context-gc-markup.js +4 -1
- package/dist/session/context-gc-observations.d.ts +5 -1
- package/dist/session/context-gc-observations.js +186 -33
- package/dist/session/context-gc-protection.d.ts +8 -0
- package/dist/session/context-gc-protection.js +83 -0
- package/dist/session/context-gc-replacements.d.ts +6 -1
- package/dist/session/context-gc-replacements.js +54 -6
- package/dist/session/context-gc-todo.d.ts +13 -0
- package/dist/session/context-gc-todo.js +47 -0
- package/dist/session/tool-callers.d.ts +7 -0
- package/dist/session/tool-callers.js +28 -0
- package/dist/tools/callers.d.ts +1 -7
- package/dist/tools/callers.js +1 -28
- package/dist/tools/code-mode/index.d.ts +1 -3
- package/dist/tools/code-mode/index.js +3 -45
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { contextGcTargetKey } from '../../session/context-gc-checkpoint.js';
|
|
2
|
-
import {
|
|
2
|
+
import { completedTodoWrite, todoInputReplacement, todoSnapshotReplacement, visibleTodoSnapshot } from '../../session/context-gc-todo.js';
|
|
3
|
+
import { contextGcWebOutputReplacement, contextGcToolOutputReplacement, contextGcSkillOutputReplacement, hasGenericContextGcPreview, isGenericContextGcTool } from '../../session/context-gc-replacements.js';
|
|
3
4
|
import { CONTEXT_GC_POLICY } from './gc-economics.js';
|
|
4
|
-
import {
|
|
5
|
+
import { targetFor, isRecord } from './gc-candidates.js';
|
|
5
6
|
export function collectWebCandidates(input, groups, existingTargets, candidateSavings) {
|
|
6
7
|
const eligible = [];
|
|
7
8
|
let protectedTokens = 0;
|
|
@@ -26,7 +27,8 @@ export function collectWebCandidates(input, groups, existingTargets, candidateSa
|
|
|
26
27
|
if (!target || existingTargets.has(contextGcTargetKey(target)))
|
|
27
28
|
continue;
|
|
28
29
|
const evidenceSequence = group.result.record.sequence;
|
|
29
|
-
const
|
|
30
|
+
const protection = input.protection.after(Math.max(evidenceSequence, target.sequence));
|
|
31
|
+
const observedRollouts = protection.rollouts;
|
|
30
32
|
const candidate = {
|
|
31
33
|
replacements: [
|
|
32
34
|
{
|
|
@@ -44,9 +46,10 @@ export function collectWebCandidates(input, groups, existingTargets, candidateSa
|
|
|
44
46
|
const reclaimedTokens = candidateSavings(candidate);
|
|
45
47
|
if (reclaimedTokens <= 0)
|
|
46
48
|
continue;
|
|
47
|
-
if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts
|
|
49
|
+
if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts &&
|
|
50
|
+
protection.thinking >= 3)
|
|
48
51
|
eligible.push(candidate);
|
|
49
|
-
else {
|
|
52
|
+
else if (observedRollouts < CONTEXT_GC_POLICY.largeExecProtectionRollouts) {
|
|
50
53
|
protectedTokens += reclaimedTokens;
|
|
51
54
|
retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - observedRollouts);
|
|
52
55
|
}
|
|
@@ -58,6 +61,164 @@ export function collectToolOutputCandidates(input, groups, existingTargets, cand
|
|
|
58
61
|
const eligible = [];
|
|
59
62
|
let protectedTokens = 0;
|
|
60
63
|
let retryAfterRollouts = Infinity;
|
|
64
|
+
const searches = [...groups.values()]
|
|
65
|
+
.filter((group) => group.toolName === 'tool_search' &&
|
|
66
|
+
!group.failed &&
|
|
67
|
+
group.completed &&
|
|
68
|
+
group.call &&
|
|
69
|
+
group.result?.message.role === 'tool' &&
|
|
70
|
+
group.result.message.toolSearchTools !== undefined)
|
|
71
|
+
.sort((left, right) => left.result.record.sequence - right.result.record.sequence);
|
|
72
|
+
for (const group of searches) {
|
|
73
|
+
const result = group.result;
|
|
74
|
+
const target = targetFor(result.record, result.message);
|
|
75
|
+
const definitions = result.message.toolSearchTools;
|
|
76
|
+
if (!target || definitions.length === 0)
|
|
77
|
+
continue;
|
|
78
|
+
const retainedToolNames = [];
|
|
79
|
+
const evidence = [];
|
|
80
|
+
for (const tool of definitions) {
|
|
81
|
+
const successor = searches.findLast((candidate) => candidate.result.record.sequence > result.record.sequence &&
|
|
82
|
+
candidate.result.message.toolSearchTools.some((item) => item.name === tool.name) &&
|
|
83
|
+
(() => {
|
|
84
|
+
const count = input.protection.after(Math.max(target.sequence, candidate.result.record.sequence));
|
|
85
|
+
return count.rollouts >= 6 && count.thinking >= 3;
|
|
86
|
+
})());
|
|
87
|
+
if (successor)
|
|
88
|
+
evidence.push(successor.result.record.sequence);
|
|
89
|
+
else
|
|
90
|
+
retainedToolNames.push(tool.name);
|
|
91
|
+
}
|
|
92
|
+
if (evidence.length === 0)
|
|
93
|
+
continue;
|
|
94
|
+
const evidenceSequence = Math.max(...evidence);
|
|
95
|
+
const observedRollouts = input.protection.after(Math.max(target.sequence, evidenceSequence)).rollouts;
|
|
96
|
+
const candidate = {
|
|
97
|
+
replacements: [
|
|
98
|
+
{
|
|
99
|
+
kind: 'TOOL_OUTPUT',
|
|
100
|
+
toolName: 'tool_search',
|
|
101
|
+
callId: group.callId,
|
|
102
|
+
target,
|
|
103
|
+
replacementContent: result.message.content,
|
|
104
|
+
retainedToolNames,
|
|
105
|
+
evidenceSequence
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
observedRollouts,
|
|
109
|
+
evidenceSequence
|
|
110
|
+
};
|
|
111
|
+
if (candidateSavings(candidate) > 0)
|
|
112
|
+
eligible.push(candidate);
|
|
113
|
+
}
|
|
114
|
+
const writes = input.records.flatMap((record) => {
|
|
115
|
+
const fact = completedTodoWrite(record);
|
|
116
|
+
return fact ? [{ record, ...fact }] : [];
|
|
117
|
+
});
|
|
118
|
+
for (const group of groups.values()) {
|
|
119
|
+
if (group.failed || !group.completed || !group.call || group.call.message.role !== 'tool_call')
|
|
120
|
+
continue;
|
|
121
|
+
const ownWrite = completedTodoWrite(group.completed);
|
|
122
|
+
const snapshot = group.toolName === 'todo_read' && group.result?.message.role === 'tool'
|
|
123
|
+
? visibleTodoSnapshot(group.result.message.content)
|
|
124
|
+
: undefined;
|
|
125
|
+
const currentRevision = group.toolName === 'todo_write' ? ownWrite?.revision : snapshot?.revision;
|
|
126
|
+
if (currentRevision === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
const targetEntry = group.toolName === 'todo_write' ? group.call : group.result;
|
|
129
|
+
if (!targetEntry)
|
|
130
|
+
continue;
|
|
131
|
+
const target = targetFor(targetEntry.record, targetEntry.message);
|
|
132
|
+
if (!target || existingTargets.has(contextGcTargetKey(target)))
|
|
133
|
+
continue;
|
|
134
|
+
const successor = writes
|
|
135
|
+
.filter((write) => write.revision > currentRevision &&
|
|
136
|
+
input.protection.after(Math.max(target.sequence, group.completed.sequence, write.record.sequence)).rollouts >= 6 &&
|
|
137
|
+
input.protection.after(Math.max(target.sequence, group.completed.sequence, write.record.sequence)).thinking >= 3)
|
|
138
|
+
.sort((a, b) => b.revision - a.revision)[0];
|
|
139
|
+
if (!successor)
|
|
140
|
+
continue;
|
|
141
|
+
const evidenceSequence = successor.record.sequence;
|
|
142
|
+
const observedRollouts = input.protection.after(Math.max(target.sequence, group.completed.sequence, evidenceSequence)).rollouts;
|
|
143
|
+
let replacement;
|
|
144
|
+
if (group.toolName === 'todo_write') {
|
|
145
|
+
const args = group.call.message.arguments;
|
|
146
|
+
if (!isRecord(args) || !Array.isArray(args.items))
|
|
147
|
+
continue;
|
|
148
|
+
replacement = {
|
|
149
|
+
kind: 'TOOL_CALL_INPUT',
|
|
150
|
+
toolName: 'todo_write',
|
|
151
|
+
callId: group.callId,
|
|
152
|
+
target,
|
|
153
|
+
replacementArguments: todoInputReplacement(currentRevision, args.items.length, successor.revision),
|
|
154
|
+
providerAliasTargets: group.aliases
|
|
155
|
+
.map(({ record, message }) => targetFor(record, message))
|
|
156
|
+
.filter((value) => value !== undefined),
|
|
157
|
+
evidenceSequence
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
if (!snapshot)
|
|
162
|
+
continue;
|
|
163
|
+
replacement = {
|
|
164
|
+
kind: 'TOOL_OUTPUT',
|
|
165
|
+
toolName: 'todo_read',
|
|
166
|
+
callId: group.callId,
|
|
167
|
+
target,
|
|
168
|
+
replacementContent: todoSnapshotReplacement(currentRevision, snapshot.count, successor.revision),
|
|
169
|
+
evidenceSequence
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const candidate = { replacements: [replacement], observedRollouts, evidenceSequence };
|
|
173
|
+
if (candidateSavings(candidate) > 0)
|
|
174
|
+
eligible.push(candidate);
|
|
175
|
+
}
|
|
176
|
+
const skills = [...groups.values()]
|
|
177
|
+
.filter((group) => group.toolName === 'skill' &&
|
|
178
|
+
group.call?.message.role === 'tool_call' &&
|
|
179
|
+
isRecord(group.call.message.arguments) &&
|
|
180
|
+
typeof group.call.message.arguments.name === 'string' &&
|
|
181
|
+
group.result?.message.role === 'tool' &&
|
|
182
|
+
group.completed &&
|
|
183
|
+
!group.failed &&
|
|
184
|
+
!group.evidenceOnly)
|
|
185
|
+
.sort((left, right) => left.result.record.sequence - right.result.record.sequence);
|
|
186
|
+
for (const [index, group] of skills.entries()) {
|
|
187
|
+
const target = targetFor(group.result.record, group.result.message);
|
|
188
|
+
if (!target || existingTargets.has(contextGcTargetKey(target)))
|
|
189
|
+
continue;
|
|
190
|
+
const args = group.call.message.arguments;
|
|
191
|
+
const later = skills.slice(index + 1).filter((candidate) => {
|
|
192
|
+
const laterTarget = targetFor(candidate.result.record, candidate.result.message);
|
|
193
|
+
return (laterTarget &&
|
|
194
|
+
candidate.call.message.arguments.name === args.name &&
|
|
195
|
+
!existingTargets.has(contextGcTargetKey(laterTarget)));
|
|
196
|
+
});
|
|
197
|
+
const successor = later.findLast((candidate) => {
|
|
198
|
+
const count = input.protection.after(Math.max(target.sequence, candidate.result.record.sequence));
|
|
199
|
+
return count.rollouts >= 6 && count.thinking >= 3;
|
|
200
|
+
});
|
|
201
|
+
if (!successor)
|
|
202
|
+
continue;
|
|
203
|
+
const evidenceSequence = successor.result.record.sequence;
|
|
204
|
+
const observedRollouts = input.protection.after(Math.max(target.sequence, evidenceSequence)).rollouts;
|
|
205
|
+
const candidate = {
|
|
206
|
+
replacements: [
|
|
207
|
+
{
|
|
208
|
+
kind: 'TOOL_OUTPUT',
|
|
209
|
+
toolName: 'skill',
|
|
210
|
+
callId: group.callId,
|
|
211
|
+
target,
|
|
212
|
+
replacementContent: contextGcSkillOutputReplacement(args.name, group.result.message.content, successor.callId),
|
|
213
|
+
evidenceSequence
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
observedRollouts,
|
|
217
|
+
evidenceSequence
|
|
218
|
+
};
|
|
219
|
+
if (candidateSavings(candidate) > 0)
|
|
220
|
+
eligible.push(candidate);
|
|
221
|
+
}
|
|
61
222
|
for (const group of groups.values()) {
|
|
62
223
|
if (!isGenericContextGcTool(group.toolName) ||
|
|
63
224
|
group.evidenceOnly ||
|
|
@@ -68,6 +229,7 @@ export function collectToolOutputCandidates(input, groups, existingTargets, cand
|
|
|
68
229
|
group.call.message.role !== 'tool_call' ||
|
|
69
230
|
group.result.message.role !== 'tool' ||
|
|
70
231
|
typeof group.result.message.content !== 'string' ||
|
|
232
|
+
!hasGenericContextGcPreview(group.result.message.content) ||
|
|
71
233
|
!isRecord(group.completed.payload) ||
|
|
72
234
|
group.completed.payload.type !== 'tool.completed' ||
|
|
73
235
|
group.completed.payload.callId !== group.callId ||
|
|
@@ -78,7 +240,8 @@ export function collectToolOutputCandidates(input, groups, existingTargets, cand
|
|
|
78
240
|
if (!target || existingTargets.has(contextGcTargetKey(target)))
|
|
79
241
|
continue;
|
|
80
242
|
const evidenceSequence = group.result.record.sequence;
|
|
81
|
-
const
|
|
243
|
+
const protection = input.protection.after(Math.max(evidenceSequence, target.sequence));
|
|
244
|
+
const observedRollouts = protection.rollouts;
|
|
82
245
|
const candidate = {
|
|
83
246
|
replacements: [
|
|
84
247
|
{
|
|
@@ -96,9 +259,10 @@ export function collectToolOutputCandidates(input, groups, existingTargets, cand
|
|
|
96
259
|
const reclaimedTokens = candidateSavings(candidate);
|
|
97
260
|
if (reclaimedTokens <= 0)
|
|
98
261
|
continue;
|
|
99
|
-
if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts
|
|
262
|
+
if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts &&
|
|
263
|
+
protection.thinking >= 3)
|
|
100
264
|
eligible.push(candidate);
|
|
101
|
-
else {
|
|
265
|
+
else if (observedRollouts < CONTEXT_GC_POLICY.largeExecProtectionRollouts) {
|
|
102
266
|
protectedTokens += reclaimedTokens;
|
|
103
267
|
retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - observedRollouts);
|
|
104
268
|
}
|
|
@@ -3,6 +3,7 @@ import type { SessionModelMessage as ModelMessage } from '../../session/model-re
|
|
|
3
3
|
import type { ModelUsage } from '../../model/contracts.js';
|
|
4
4
|
import { ContextGcCheckpointCache, type ContextGcCheckpointDraft, type ContextGcMetrics, type ContextGcPlatform, type ContextGcReplacement } from '../../session/context-gc-checkpoint.js';
|
|
5
5
|
import type { SessionRecord } from '../../session/record-contract.js';
|
|
6
|
+
import type { buildContextGcProtection } from '../../session/context-gc-protection.js';
|
|
6
7
|
export interface ContextGcPlan {
|
|
7
8
|
readonly stage: ContextGcMetrics['stage'];
|
|
8
9
|
readonly draft: ContextGcCheckpointDraft;
|
|
@@ -29,6 +30,7 @@ export interface ContextGcPlannerInput {
|
|
|
29
30
|
readonly activeProcessIds: ReadonlySet<string>;
|
|
30
31
|
readonly tokenCache?: Map<string, number>;
|
|
31
32
|
readonly checkpointCache?: ContextGcCheckpointCache;
|
|
33
|
+
readonly protection?: ReturnType<typeof buildContextGcProtection>;
|
|
32
34
|
}
|
|
33
35
|
export interface ContextCandidate {
|
|
34
36
|
readonly replacements: readonly ContextGcReplacement[];
|
|
@@ -24,16 +24,10 @@ export interface ContextGcEconomicsResult {
|
|
|
24
24
|
readonly grossReductionRate: number;
|
|
25
25
|
readonly passes: boolean;
|
|
26
26
|
}
|
|
27
|
-
interface ContextGcCacheEconomicsProfile {
|
|
28
|
-
readonly reclaimedTokens: number;
|
|
29
|
-
readonly reclaimedCachedTokens: number;
|
|
30
|
-
readonly cacheRebuildPenaltyCostUnits: number;
|
|
31
|
-
}
|
|
32
27
|
export declare function evaluateContextGcEconomics(input: ContextGcEconomicsInput): ContextGcEconomicsResult;
|
|
33
28
|
export declare function contextGcMinimumGrossReductionRate(usageRatio: number): number;
|
|
34
29
|
export declare function contextGcMinimumReclaimedTokens(contextUsageTokens: number, contextWindowTokens: number): number;
|
|
35
30
|
export declare function contextGcEligibleUsage(usage: ModelUsage, contextWindowTokens: number): number | undefined;
|
|
36
|
-
export declare function contextGcMarginalCacheInvestmentPaysBack(current: ContextGcCacheEconomicsProfile, combined: ContextGcCacheEconomicsProfile): boolean;
|
|
37
31
|
export declare function contextGcImminentCompactAvoidanceCredit(input: {
|
|
38
32
|
readonly contextUsageTokens: number;
|
|
39
33
|
readonly contextWindowTokens: number;
|
|
@@ -5,7 +5,7 @@ export const CONTEXT_GC_POLICY = {
|
|
|
5
5
|
contextGrowthSampleWindow: 16,
|
|
6
6
|
minimumContextGrowthSamples: 6,
|
|
7
7
|
minimumObservationRollouts: 1,
|
|
8
|
-
stageAProtectionRollouts:
|
|
8
|
+
stageAProtectionRollouts: 6,
|
|
9
9
|
largeExecProtectionRollouts: 6,
|
|
10
10
|
normalInputCostRate: 1,
|
|
11
11
|
cacheReadCostRate: 0.1,
|
|
@@ -107,28 +107,6 @@ export function contextGcEligibleUsage(usage, contextWindowTokens) {
|
|
|
107
107
|
return undefined;
|
|
108
108
|
return contextUsageTokens;
|
|
109
109
|
}
|
|
110
|
-
export function contextGcMarginalCacheInvestmentPaysBack(current, combined) {
|
|
111
|
-
const marginalPenalty = combined.cacheRebuildPenaltyCostUnits - current.cacheRebuildPenaltyCostUnits;
|
|
112
|
-
if (marginalPenalty <= 0)
|
|
113
|
-
return true;
|
|
114
|
-
const firstRolloutSavings = (profile) => {
|
|
115
|
-
const reclaimedCachedTokens = clamp(profile.reclaimedCachedTokens, 0, Math.max(0, profile.reclaimedTokens));
|
|
116
|
-
return (reclaimedCachedTokens * CONTEXT_GC_POLICY.cacheReadCostRate +
|
|
117
|
-
Math.max(0, profile.reclaimedTokens - reclaimedCachedTokens) *
|
|
118
|
-
CONTEXT_GC_POLICY.normalInputCostRate);
|
|
119
|
-
};
|
|
120
|
-
const marginalFirstRolloutSavings = firstRolloutSavings(combined) - firstRolloutSavings(current);
|
|
121
|
-
const marginalSteadyRolloutSavings = Math.max(0, combined.reclaimedTokens - current.reclaimedTokens) *
|
|
122
|
-
CONTEXT_GC_POLICY.cacheReadCostRate;
|
|
123
|
-
for (let rollouts = 1; rollouts <= CONTEXT_GC_POLICY.immediatePaybackRollouts; rollouts++) {
|
|
124
|
-
const net = -marginalPenalty +
|
|
125
|
-
marginalFirstRolloutSavings +
|
|
126
|
-
Math.max(0, rollouts - 1) * marginalSteadyRolloutSavings;
|
|
127
|
-
if (net > 0 && net / marginalPenalty >= CONTEXT_GC_POLICY.minimumCacheInvestmentRoi)
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
110
|
export function contextGcImminentCompactAvoidanceCredit(input) {
|
|
133
111
|
if (input.contextUsageTokens >= input.compactThresholdTokens ||
|
|
134
112
|
input.contextGrowthP75Tokens <= 0 ||
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { openAiInput } from '../../model/openai-responses.js';
|
|
2
|
+
import { anthropicMessages } from '../../model/anthropic-messages.js';
|
|
3
|
+
import { projectCodeModeSearchResult } from '../../session/code-mode-search-projection.js';
|
|
2
4
|
import { countModelTokens } from '../token-budget.js';
|
|
3
5
|
import { observationModelParts, observationModelText } from '../../session/code-observation-projection.js';
|
|
4
6
|
export { MODEL_TOKEN_ESTIMATOR as CONTEXT_GC_TOKEN_ESTIMATOR } from '../token-budget.js';
|
|
@@ -23,6 +25,8 @@ function serializedEntries(input, messages) {
|
|
|
23
25
|
}))
|
|
24
26
|
}
|
|
25
27
|
: message);
|
|
28
|
+
const codeMode = input.tools.some((tool) => tool.name === 'code_exec');
|
|
29
|
+
messages = messages.map((message) => projectCodeModeSearchResult(message, codeMode));
|
|
26
30
|
const responses = input.modelProtocol === 'OPENAI_RESPONSES' ||
|
|
27
31
|
(input.modelProtocol === undefined &&
|
|
28
32
|
(input.messages ?? messages).some((message) => message.provider === 'OPENAI_RESPONSES'));
|
|
@@ -31,14 +35,20 @@ function serializedEntries(input, messages) {
|
|
|
31
35
|
const contentParts = message.role === 'tool' ? observationModelParts(message) : undefined;
|
|
32
36
|
return contentParts === undefined ? message : { ...message, contentParts };
|
|
33
37
|
}), undefined, input.modelId, true, input.responsesConfigurationUpdates !== false)
|
|
34
|
-
:
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
: input.modelProtocol === 'ANTHROPIC_MESSAGES'
|
|
39
|
+
? anthropicMessages(messages.map((message) => {
|
|
40
|
+
const contentParts = message.role === 'tool' ? observationModelParts(message) : undefined;
|
|
41
|
+
return contentParts === undefined ? message : { ...message, contentParts };
|
|
42
|
+
}), undefined, input.tools, input.modelId, false)
|
|
43
|
+
: messages
|
|
44
|
+
.filter((message) => message.role !== 'provider' ||
|
|
45
|
+
(message.provider === 'ANTHROPIC_MESSAGES' &&
|
|
46
|
+
message.providerModelId === input.modelId))
|
|
47
|
+
.map((original) => {
|
|
48
|
+
const { observations: _observations, observationId: _observationId, toolSearchTools: _toolSearchTools, ...message } = original;
|
|
49
|
+
const content = original.role === 'tool' ? observationModelText(original) : undefined;
|
|
50
|
+
return content === undefined ? message : { ...message, content };
|
|
51
|
+
});
|
|
42
52
|
return values.map((value, index) => {
|
|
43
53
|
const item = value;
|
|
44
54
|
const callId = item.call_id ?? item.callId;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { type ContextGcReplacement } from '../../session/context-gc-checkpoint.js';
|
|
2
1
|
import { type ContextGcPlan, type ContextGcInspection, type ContextGcPlannerInput } from './gc-contracts.js';
|
|
3
|
-
export { CONTEXT_GC_POLICY, type ContextGcEconomicsResult, evaluateContextGcEconomics, contextGcMinimumGrossReductionRate, contextGcMinimumReclaimedTokens, contextGcEligibleUsage,
|
|
2
|
+
export { CONTEXT_GC_POLICY, type ContextGcEconomicsResult, evaluateContextGcEconomics, contextGcMinimumGrossReductionRate, contextGcMinimumReclaimedTokens, contextGcEligibleUsage, contextGcImminentCompactAvoidanceCredit, contextGcAppliedCompactCreditCostUnits } from './gc-economics.js';
|
|
4
3
|
export { type ContextGcPlan, type ContextGcInspection, type ContextGcPlannerInput } from './gc-contracts.js';
|
|
5
4
|
export declare function planContextGc(input: ContextGcPlannerInput): ContextGcPlan | undefined;
|
|
6
5
|
export declare function inspectContextGc(input: ContextGcPlannerInput): ContextGcInspection;
|
|
7
|
-
export declare function contextGcEarliestReplacementSequence(replacements: readonly ContextGcReplacement[]): number;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { ContextGcCheckpointCache, contextGcBaseFromRecords, contextGcCheckpointIntervalEligible, contextGcMinimumUsageGrowth, contextGcTargetKey, normalizeContextGcReplacements } from '../../session/context-gc-checkpoint.js';
|
|
2
|
+
import { buildContextGcProtection } from '../../session/context-gc-protection.js';
|
|
2
3
|
import { CONTEXT_GC_TOKEN_ESTIMATOR, contextGcCacheImpact, contextGcMessageTokens, contextGcPrefixTokens, contextGcReclaimedTokens } from './gc-tokens.js';
|
|
3
4
|
import { applyContextGcReplacementsToMessages, providerToolCallId } from './projection.js';
|
|
4
|
-
import { CONTEXT_GC_POLICY, COMPACT_CREDIT_ACTIVATION_RATIO, COMPACT_THRESHOLD_RATIO, evaluateContextGcEconomics, contextGcMinimumGrossReductionRate, contextGcMinimumReclaimedTokens, contextGcEligibleUsage,
|
|
5
|
+
import { CONTEXT_GC_POLICY, COMPACT_CREDIT_ACTIVATION_RATIO, COMPACT_THRESHOLD_RATIO, evaluateContextGcEconomics, contextGcMinimumGrossReductionRate, contextGcMinimumReclaimedTokens, contextGcEligibleUsage, contextGcImminentCompactAvoidanceCredit, contextGcAppliedCompactCreditCostUnits, modelContextUsage, clamp } from './gc-economics.js';
|
|
5
6
|
import { buildToolGroups, countSuccessfulNormalRollouts, isRecord } from './gc-candidates.js';
|
|
6
7
|
import { collectCodeModeCandidates } from './gc-candidates-code.js';
|
|
7
|
-
import {
|
|
8
|
+
import { collectDeterministicCandidates } from './gc-candidates-read-patch.js';
|
|
8
9
|
import { collectExecCandidates } from './gc-candidates-exec.js';
|
|
9
10
|
import { collectToolOutputCandidates, collectWebCandidates } from './gc-candidates-web.js';
|
|
10
|
-
export { CONTEXT_GC_POLICY, evaluateContextGcEconomics, contextGcMinimumGrossReductionRate, contextGcMinimumReclaimedTokens, contextGcEligibleUsage,
|
|
11
|
+
export { CONTEXT_GC_POLICY, evaluateContextGcEconomics, contextGcMinimumGrossReductionRate, contextGcMinimumReclaimedTokens, contextGcEligibleUsage, contextGcImminentCompactAvoidanceCredit, contextGcAppliedCompactCreditCostUnits } from './gc-economics.js';
|
|
11
12
|
export function planContextGc(input) {
|
|
12
13
|
return inspectContextGc(input).plan;
|
|
13
14
|
}
|
|
@@ -69,7 +70,8 @@ function inspectTextGc(input) {
|
|
|
69
70
|
input = {
|
|
70
71
|
...input,
|
|
71
72
|
tokenCache: new Map(),
|
|
72
|
-
checkpointCache: input.checkpointCache ?? new ContextGcCheckpointCache()
|
|
73
|
+
checkpointCache: input.checkpointCache ?? new ContextGcCheckpointCache(),
|
|
74
|
+
protection: buildContextGcProtection(input.records, input.modelId, input.contextWindowTokens)
|
|
73
75
|
};
|
|
74
76
|
const usageRatio = contextUsageTokens / input.contextWindowTokens;
|
|
75
77
|
const usageRecord = latestUsageRecord(input.records, input.modelId);
|
|
@@ -108,85 +110,42 @@ function inspectTextGc(input) {
|
|
|
108
110
|
? replacement.providerAliasTargets.map(contextGcTargetKey)
|
|
109
111
|
: [])
|
|
110
112
|
]));
|
|
111
|
-
const
|
|
112
|
-
const deterministicCandidates = stageA.eligible;
|
|
113
|
-
const deterministicProtectedTokens = stageA.protectedTokens;
|
|
114
|
-
const stageAPlan = evaluateCandidateSet(input, existing, deterministicCandidates, {
|
|
115
|
-
stage: 'STAGE_A',
|
|
116
|
-
contextUsageTokens,
|
|
117
|
-
usageRatio,
|
|
118
|
-
usageRecord,
|
|
119
|
-
normalRolloutOrdinal,
|
|
120
|
-
protectedCandidateTokens: deterministicProtectedTokens
|
|
121
|
-
});
|
|
122
|
-
if (stageAPlan?.economics.passes) {
|
|
123
|
-
const exec = collectExecCandidates(input, groups, existingTargets, candidateSavings);
|
|
124
|
-
const codeMode = collectCodeModeCandidates(input, groups, existingTargets, candidateSavings);
|
|
125
|
-
const web = collectWebCandidates(input, groups, existingTargets, candidateSavings);
|
|
126
|
-
const tools = collectToolOutputCandidates(input, groups, existingTargets, candidateSavings);
|
|
127
|
-
const protectedCandidateTokens = deterministicProtectedTokens +
|
|
128
|
-
exec.protectedTokens +
|
|
129
|
-
codeMode.protectedTokens +
|
|
130
|
-
web.protectedTokens +
|
|
131
|
-
tools.protectedTokens;
|
|
132
|
-
const stageAPlanWithProtectedCandidates = evaluateCandidateSet(input, existing, deterministicCandidates, {
|
|
133
|
-
stage: 'STAGE_A',
|
|
134
|
-
contextUsageTokens,
|
|
135
|
-
usageRatio,
|
|
136
|
-
usageRecord,
|
|
137
|
-
normalRolloutOrdinal,
|
|
138
|
-
protectedCandidateTokens
|
|
139
|
-
}) ?? stageAPlan;
|
|
140
|
-
const marginal = mergeMarginalLargeOutputCandidates(input, existing, deterministicCandidates, [...exec.eligible, ...codeMode.eligible, ...web.eligible, ...tools.eligible], stageAPlanWithProtectedCandidates, {
|
|
141
|
-
contextUsageTokens,
|
|
142
|
-
usageRatio,
|
|
143
|
-
usageRecord,
|
|
144
|
-
normalRolloutOrdinal,
|
|
145
|
-
protectedCandidateTokens
|
|
146
|
-
});
|
|
147
|
-
const plan = checkpointEligible
|
|
148
|
-
? finalizePlan(input, marginal ?? stageAPlanWithProtectedCandidates)
|
|
149
|
-
: undefined;
|
|
150
|
-
const retryAfterRollouts = Math.min(exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts, tools.retryAfterRollouts);
|
|
151
|
-
if (!plan && checkpointEligible)
|
|
152
|
-
return {};
|
|
153
|
-
return {
|
|
154
|
-
...(plan ? { plan } : {}),
|
|
155
|
-
...(protectedCandidateTokens > 0 && Number.isFinite(retryAfterRollouts)
|
|
156
|
-
? { retryAfterRollouts }
|
|
157
|
-
: {})
|
|
158
|
-
};
|
|
159
|
-
}
|
|
113
|
+
const deterministic = collectDeterministicCandidates(input, groups, existing, existingTargets, candidateSavings);
|
|
160
114
|
const exec = collectExecCandidates(input, groups, existingTargets, candidateSavings);
|
|
161
115
|
const codeMode = collectCodeModeCandidates(input, groups, existingTargets, candidateSavings);
|
|
162
116
|
const web = collectWebCandidates(input, groups, existingTargets, candidateSavings);
|
|
163
117
|
const tools = collectToolOutputCandidates(input, groups, existingTargets, candidateSavings);
|
|
164
|
-
const protectedCandidateTokens =
|
|
118
|
+
const protectedCandidateTokens = deterministic.protectedTokens +
|
|
165
119
|
exec.protectedTokens +
|
|
166
120
|
codeMode.protectedTokens +
|
|
167
121
|
web.protectedTokens +
|
|
168
122
|
tools.protectedTokens;
|
|
169
123
|
const combined = evaluateCandidateSet(input, existing, [
|
|
170
|
-
...
|
|
124
|
+
...deterministic.eligible,
|
|
171
125
|
...exec.eligible,
|
|
172
126
|
...codeMode.eligible,
|
|
173
127
|
...web.eligible,
|
|
174
128
|
...tools.eligible
|
|
175
129
|
], {
|
|
176
|
-
stage: '
|
|
130
|
+
stage: 'UNIFIED',
|
|
177
131
|
contextUsageTokens,
|
|
178
132
|
usageRatio,
|
|
179
133
|
usageRecord,
|
|
180
134
|
normalRolloutOrdinal,
|
|
181
135
|
protectedCandidateTokens
|
|
182
136
|
});
|
|
137
|
+
const retryAfterRollouts = Math.min(deterministic.retryAfterRollouts, exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts, tools.retryAfterRollouts);
|
|
183
138
|
if (combined?.economics.passes && checkpointEligible) {
|
|
184
139
|
const plan = finalizePlan(input, combined);
|
|
185
|
-
return plan
|
|
140
|
+
return plan
|
|
141
|
+
? {
|
|
142
|
+
plan,
|
|
143
|
+
...(protectedCandidateTokens > 0 && Number.isFinite(retryAfterRollouts)
|
|
144
|
+
? { retryAfterRollouts }
|
|
145
|
+
: {})
|
|
146
|
+
}
|
|
147
|
+
: {};
|
|
186
148
|
}
|
|
187
|
-
const retryAfterRollouts = Math.min(stageA.retryAfterRollouts, exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts, tools.retryAfterRollouts);
|
|
188
|
-
// A worthwhile mixed set retains every positive protected candidate's retry
|
|
189
|
-
// hint even when the checkpoint interval prevents a durable draft.
|
|
190
149
|
return protectedCandidateTokens > 0 &&
|
|
191
150
|
(combined?.economics.passes ||
|
|
192
151
|
protectedCandidateTokens >=
|
|
@@ -195,42 +154,6 @@ function inspectTextGc(input) {
|
|
|
195
154
|
? { retryAfterRollouts }
|
|
196
155
|
: {};
|
|
197
156
|
}
|
|
198
|
-
function mergeMarginalLargeOutputCandidates(input, existing, deterministicCandidates, execCandidates, stageAPlan, state) {
|
|
199
|
-
let selected = [...deterministicCandidates];
|
|
200
|
-
let current = stageAPlan;
|
|
201
|
-
const stageAEarliestSequence = contextGcEarliestReplacementSequence(deterministicCandidates.flatMap((candidate) => candidate.replacements));
|
|
202
|
-
for (const candidate of [...execCandidates].sort((left, right) => right.evidenceSequence - left.evidenceSequence)) {
|
|
203
|
-
const evaluated = evaluateCandidateSet(input, existing, [...selected, candidate], {
|
|
204
|
-
stage: 'STAGE_A_AND_EXEC',
|
|
205
|
-
...state
|
|
206
|
-
});
|
|
207
|
-
if (!evaluated?.economics.passes || evaluated.reclaimedTokens <= current.reclaimedTokens)
|
|
208
|
-
continue;
|
|
209
|
-
const doesNotMoveInvalidationEarlier = contextGcEarliestReplacementSequence(candidate.replacements) >= stageAEarliestSequence;
|
|
210
|
-
if (!doesNotMoveInvalidationEarlier &&
|
|
211
|
-
!contextGcMarginalCacheInvestmentPaysBack({
|
|
212
|
-
reclaimedTokens: current.reclaimedTokens,
|
|
213
|
-
reclaimedCachedTokens: current.reclaimedCachedTokens,
|
|
214
|
-
cacheRebuildPenaltyCostUnits: current.economics.cacheRebuildPenaltyCostUnits
|
|
215
|
-
}, {
|
|
216
|
-
reclaimedTokens: evaluated.reclaimedTokens,
|
|
217
|
-
reclaimedCachedTokens: evaluated.reclaimedCachedTokens,
|
|
218
|
-
cacheRebuildPenaltyCostUnits: evaluated.economics.cacheRebuildPenaltyCostUnits
|
|
219
|
-
}))
|
|
220
|
-
continue;
|
|
221
|
-
selected = [...selected, candidate];
|
|
222
|
-
current = evaluated;
|
|
223
|
-
}
|
|
224
|
-
return current === stageAPlan ? undefined : current;
|
|
225
|
-
}
|
|
226
|
-
export function contextGcEarliestReplacementSequence(replacements) {
|
|
227
|
-
return Math.min(...replacements.flatMap((replacement) => [
|
|
228
|
-
replacement.target.sequence,
|
|
229
|
-
...(replacement.kind === 'TOOL_CALL_INPUT'
|
|
230
|
-
? replacement.providerAliasTargets.map((target) => target.sequence)
|
|
231
|
-
: [])
|
|
232
|
-
]));
|
|
233
|
-
}
|
|
234
157
|
function finalizePlan(input, evaluated, includeMedia = false) {
|
|
235
158
|
if (!evaluated)
|
|
236
159
|
return undefined;
|
|
@@ -105,7 +105,17 @@ export function applyContextGcReplacementsToMessages(messages, messageSources, r
|
|
|
105
105
|
replacement.kind === 'SUBAGENT_OUTPUT' ||
|
|
106
106
|
replacement.kind === 'WEB_OUTPUT') &&
|
|
107
107
|
next.role === 'tool')
|
|
108
|
-
next = {
|
|
108
|
+
next = {
|
|
109
|
+
...next,
|
|
110
|
+
content: replacement.replacementContent,
|
|
111
|
+
...(replacement.kind === 'TOOL_OUTPUT' &&
|
|
112
|
+
replacement.retainedToolNames !== undefined &&
|
|
113
|
+
next.toolSearchTools !== undefined
|
|
114
|
+
? {
|
|
115
|
+
toolSearchTools: next.toolSearchTools.filter((tool) => replacement.retainedToolNames.includes(tool.name))
|
|
116
|
+
}
|
|
117
|
+
: {})
|
|
118
|
+
};
|
|
109
119
|
}
|
|
110
120
|
if (next.textBlocks !== undefined && next.content !== message.content)
|
|
111
121
|
next = {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ClientToolDefinition } from '../tools/contracts.js';
|
|
2
|
+
import type { SessionModelMessage as ModelMessage } from './model-request.js';
|
|
3
|
+
export declare function projectProgrammaticTool(tool: ClientToolDefinition): Pick<ClientToolDefinition, 'name' | 'description' | 'inputSchema'>;
|
|
4
|
+
/** Preserve native search definitions while providing the same discovery evidence to other protocols. */
|
|
5
|
+
export declare function projectCodeModeSearchResult(message: ModelMessage, codeModeEnabled: boolean): ModelMessage;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { stableToolDefinition, stableToolSchema } from '../model/tool-definition-projection.js';
|
|
2
|
+
import { allowsDirectTool, allowsToolCaller } from './tool-callers.js';
|
|
3
|
+
export function projectProgrammaticTool(tool) {
|
|
4
|
+
return {
|
|
5
|
+
name: tool.name,
|
|
6
|
+
description: `${allowsDirectTool(tool, true) ? `Call directly or as tools.${tool.name} inside code_exec.` : `Call only as tools.${tool.name} inside code_exec, never as a direct model tool.`}\n${tool.description}`,
|
|
7
|
+
inputSchema: stableToolSchema(tool.inputSchema)
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** Preserve native search definitions while providing the same discovery evidence to other protocols. */
|
|
11
|
+
export function projectCodeModeSearchResult(message, codeModeEnabled) {
|
|
12
|
+
if (!message.toolSearchTools)
|
|
13
|
+
return message;
|
|
14
|
+
if (!codeModeEnabled)
|
|
15
|
+
return {
|
|
16
|
+
...message,
|
|
17
|
+
toolSearchTools: message.toolSearchTools
|
|
18
|
+
.filter((tool) => allowsToolCaller(tool, 'direct'))
|
|
19
|
+
.map(stableToolDefinition)
|
|
20
|
+
};
|
|
21
|
+
const tools = message.toolSearchTools.map((tool) => ({
|
|
22
|
+
...(allowsToolCaller(tool, 'programmatic')
|
|
23
|
+
? {
|
|
24
|
+
...projectProgrammaticTool(tool),
|
|
25
|
+
...(allowsDirectTool(tool, true)
|
|
26
|
+
? { inputMode: tool.inputMode ?? 'json' }
|
|
27
|
+
: {})
|
|
28
|
+
}
|
|
29
|
+
: {
|
|
30
|
+
name: tool.name,
|
|
31
|
+
description: tool.description,
|
|
32
|
+
inputSchema: stableToolSchema(tool.inputSchema),
|
|
33
|
+
inputMode: tool.inputMode ?? 'json'
|
|
34
|
+
}),
|
|
35
|
+
caller: allowsToolCaller(tool, 'programmatic')
|
|
36
|
+
? allowsDirectTool(tool, true)
|
|
37
|
+
? 'both'
|
|
38
|
+
: 'programmatic'
|
|
39
|
+
: 'direct'
|
|
40
|
+
}));
|
|
41
|
+
return {
|
|
42
|
+
...message,
|
|
43
|
+
content: JSON.stringify({ tools }),
|
|
44
|
+
toolSearchTools: tools.map(({ caller: _caller, ...tool }) => tool)
|
|
45
|
+
};
|
|
46
|
+
}
|