@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
|
@@ -8,3 +8,7 @@ export declare class AnthropicMessagesAdapter implements ModelAdapter {
|
|
|
8
8
|
private startAttempt;
|
|
9
9
|
}
|
|
10
10
|
export declare function isAnthropicRecoverableStreamError(error: unknown): error is AgentError;
|
|
11
|
+
export declare function anthropicMessages(messages: ModelRequest['messages'], images?: ModelRequest['images'], tools?: ModelRequest['tools'], modelId?: string, automaticCache?: boolean, cachePrefixMessages?: number): {
|
|
12
|
+
role: string;
|
|
13
|
+
content: unknown;
|
|
14
|
+
}[];
|
|
@@ -536,7 +536,7 @@ function boundedError(value, maximum) {
|
|
|
536
536
|
return undefined;
|
|
537
537
|
return [...normalized].slice(0, maximum).join('');
|
|
538
538
|
}
|
|
539
|
-
function anthropicMessages(messages, images, tools = [], modelId, automaticCache = true, cachePrefixMessages) {
|
|
539
|
+
export function anthropicMessages(messages, images, tools = [], modelId, automaticCache = true, cachePrefixMessages) {
|
|
540
540
|
if (cachePrefixMessages !== undefined &&
|
|
541
541
|
(!Number.isSafeInteger(cachePrefixMessages) ||
|
|
542
542
|
cachePrefixMessages < 0 ||
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { ModelMessage } from './contracts.js';
|
|
2
|
+
/** Identity of a persisted complete native thinking item; payload content is never compared. */
|
|
3
|
+
export declare function completedNativeThinkingIdentity(payload: Record<string, unknown>, recordId: string): string | undefined;
|
|
2
4
|
/** Interpret only the existing native context record shapes and their model ownership. */
|
|
3
5
|
export declare function restoreNativeModelContext(payload: Record<string, unknown>, activeModelId?: string): ModelMessage | undefined;
|
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
import { anthropicThinkingBlock } from './anthropic-thinking.js';
|
|
2
|
+
/** Identity of a persisted complete native thinking item; payload content is never compared. */
|
|
3
|
+
export function completedNativeThinkingIdentity(payload, recordId) {
|
|
4
|
+
if (payload.role !== 'provider')
|
|
5
|
+
return undefined;
|
|
6
|
+
if (payload.provider === 'ANTHROPIC_MESSAGES') {
|
|
7
|
+
const item = payload.item;
|
|
8
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
9
|
+
return undefined;
|
|
10
|
+
const block = item;
|
|
11
|
+
return (block.type === 'thinking' &&
|
|
12
|
+
typeof block.thinking === 'string' &&
|
|
13
|
+
typeof block.signature === 'string' &&
|
|
14
|
+
block.signature.length > 0) ||
|
|
15
|
+
(block.type === 'redacted_thinking' &&
|
|
16
|
+
typeof block.data === 'string' &&
|
|
17
|
+
block.data.length > 0)
|
|
18
|
+
? recordId
|
|
19
|
+
: undefined;
|
|
20
|
+
}
|
|
21
|
+
const message = restoreNativeModelContext(payload);
|
|
22
|
+
if (!message?.item)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (message.provider !== 'OPENAI_RESPONSES' || message.item.type !== 'reasoning')
|
|
25
|
+
return undefined;
|
|
26
|
+
return typeof message.item.id === 'string' ? message.item.id : recordId;
|
|
27
|
+
}
|
|
2
28
|
/** Interpret only the existing native context record shapes and their model ownership. */
|
|
3
29
|
export function restoreNativeModelContext(payload, activeModelId) {
|
|
4
30
|
const modelId = typeof payload.providerModelId === 'string' && payload.providerModelId.length > 0
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { retainedCodeInputs } from '../../session/context-gc-observations.js';
|
|
2
2
|
import { contextGcTargetKey } from '../../session/context-gc-checkpoint.js';
|
|
3
|
-
import { codeCellCompletionSequence, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, normalizeContextGcCodeModeInput } from '../../session/context-gc-replacements.js';
|
|
3
|
+
import { codeCellCompletionSequence, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, hasGenericContextGcPreview, normalizeContextGcCodeModeInput } from '../../session/context-gc-replacements.js';
|
|
4
4
|
import { CONTEXT_GC_POLICY } from './gc-economics.js';
|
|
5
|
-
import {
|
|
5
|
+
import { targetFor, isRecord } from './gc-candidates.js';
|
|
6
6
|
export function collectCodeModeCandidates(input, groups, existingTargets, candidateSavings) {
|
|
7
7
|
const eligible = [];
|
|
8
8
|
let protectedTokens = 0;
|
|
@@ -10,22 +10,12 @@ export function collectCodeModeCandidates(input, groups, existingTargets, candid
|
|
|
10
10
|
for (const group of [...groups.values()].filter((item) => ['code_exec', 'code_exec_wait'].includes(item.toolName))) {
|
|
11
11
|
if (!group.call ||
|
|
12
12
|
!group.result ||
|
|
13
|
-
group.failed ||
|
|
14
|
-
!group.completed ||
|
|
15
13
|
group.call.message.role !== 'tool_call' ||
|
|
16
14
|
group.result.message.role !== 'tool' ||
|
|
17
15
|
typeof group.result.message.content !== 'string')
|
|
18
16
|
continue;
|
|
19
|
-
const code = normalizeContextGcCodeModeInput(group.call.message.arguments);
|
|
20
|
-
const completedPayload = group.completed.payload;
|
|
21
|
-
const completionSequence = codeCellCompletionSequence(input.records, group.completed);
|
|
22
|
-
if ((group.toolName !== 'code_exec_wait' && !code) ||
|
|
23
|
-
completionSequence === undefined ||
|
|
24
|
-
!isRecord(completedPayload) ||
|
|
25
|
-
typeof completedPayload.truncated !== 'boolean')
|
|
26
|
-
continue;
|
|
27
17
|
for (const observation of group.result.message.observations ?? []) {
|
|
28
|
-
if (observation.type !== 'value')
|
|
18
|
+
if (observation.type !== 'value' || !hasGenericContextGcPreview(observation.value))
|
|
29
19
|
continue;
|
|
30
20
|
const target = targetFor(group.result.record, {
|
|
31
21
|
...group.result.message,
|
|
@@ -34,29 +24,41 @@ export function collectCodeModeCandidates(input, groups, existingTargets, candid
|
|
|
34
24
|
if (!target || existingTargets.has(contextGcTargetKey(target)))
|
|
35
25
|
continue;
|
|
36
26
|
const evidenceSequence = group.result.record.sequence;
|
|
27
|
+
const protection = input.protection.after(evidenceSequence);
|
|
37
28
|
const candidate = {
|
|
38
29
|
replacements: [
|
|
39
30
|
{
|
|
40
31
|
kind: 'CODE_MODE_OUTPUT',
|
|
41
32
|
callId: group.callId,
|
|
42
33
|
target,
|
|
43
|
-
replacementContent: contextGcCodeModeOutputReplacement(observation.value, observation.truncated === true),
|
|
34
|
+
replacementContent: contextGcCodeModeOutputReplacement(observation.value, observation.truncated === true, true),
|
|
44
35
|
evidenceSequence
|
|
45
36
|
}
|
|
46
37
|
],
|
|
47
|
-
observedRollouts:
|
|
38
|
+
observedRollouts: protection.rollouts,
|
|
48
39
|
evidenceSequence
|
|
49
40
|
};
|
|
50
41
|
const reclaimedTokens = candidateSavings(candidate);
|
|
51
42
|
if (reclaimedTokens <= 0)
|
|
52
43
|
continue;
|
|
53
|
-
if (candidate.observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts
|
|
44
|
+
if (candidate.observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts &&
|
|
45
|
+
protection.thinking >= 3)
|
|
54
46
|
eligible.push(candidate);
|
|
55
|
-
else {
|
|
47
|
+
else if (candidate.observedRollouts < CONTEXT_GC_POLICY.largeExecProtectionRollouts) {
|
|
56
48
|
protectedTokens += reclaimedTokens;
|
|
57
49
|
retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - candidate.observedRollouts);
|
|
58
50
|
}
|
|
59
51
|
}
|
|
52
|
+
if (group.failed || !group.completed)
|
|
53
|
+
continue;
|
|
54
|
+
const code = normalizeContextGcCodeModeInput(group.call.message.arguments);
|
|
55
|
+
const completedPayload = group.completed.payload;
|
|
56
|
+
const completionSequence = codeCellCompletionSequence(input.records, group.completed);
|
|
57
|
+
if ((group.toolName !== 'code_exec_wait' && !code) ||
|
|
58
|
+
completionSequence === undefined ||
|
|
59
|
+
!isRecord(completedPayload) ||
|
|
60
|
+
typeof completedPayload.truncated !== 'boolean')
|
|
61
|
+
continue;
|
|
60
62
|
const callTarget = targetFor(group.call.record, group.call.message);
|
|
61
63
|
const resultTarget = targetFor(group.result.record, group.result.message);
|
|
62
64
|
if (!callTarget || !resultTarget)
|
|
@@ -69,7 +71,18 @@ export function collectCodeModeCandidates(input, groups, existingTargets, candid
|
|
|
69
71
|
toolName: group.toolName,
|
|
70
72
|
callId: group.callId,
|
|
71
73
|
target: callTarget,
|
|
72
|
-
replacementArguments: contextGcCodeModeInputReplacement(code, group.result.message.observations
|
|
74
|
+
replacementArguments: contextGcCodeModeInputReplacement(code, group.result.message.observations
|
|
75
|
+
? retainedCodeInputs(input.records, group.callId, (original, successor) => {
|
|
76
|
+
const maturity = input.protection.after(Math.max(evidenceSequence, completionSequence, original.sequence, successor.sequence));
|
|
77
|
+
return maturity.rollouts >= 6 && maturity.thinking >= 3;
|
|
78
|
+
}, {
|
|
79
|
+
activeProcessIds: input.activeProcessIds,
|
|
80
|
+
evidenceMature: (sequence) => {
|
|
81
|
+
const maturity = input.protection.after(Math.max(evidenceSequence, completionSequence, sequence));
|
|
82
|
+
return maturity.rollouts >= 6 && maturity.thinking >= 3;
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
: []),
|
|
73
86
|
providerAliasTargets: group.aliases
|
|
74
87
|
.map(({ record, message }) => targetFor(record, message))
|
|
75
88
|
.filter((target) => target !== undefined),
|
|
@@ -86,14 +99,16 @@ export function collectCodeModeCandidates(input, groups, existingTargets, candid
|
|
|
86
99
|
});
|
|
87
100
|
if (replacements.length === 0)
|
|
88
101
|
continue;
|
|
89
|
-
const
|
|
102
|
+
const protection = input.protection.after(Math.max(evidenceSequence, completionSequence));
|
|
103
|
+
const observedRollouts = protection.rollouts;
|
|
90
104
|
const candidate = { replacements, observedRollouts, evidenceSequence };
|
|
91
105
|
const reclaimedTokens = candidateSavings(candidate);
|
|
92
106
|
if (reclaimedTokens <= 0)
|
|
93
107
|
continue;
|
|
94
|
-
if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts
|
|
108
|
+
if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts &&
|
|
109
|
+
protection.thinking >= 3)
|
|
95
110
|
eligible.push(candidate);
|
|
96
|
-
else {
|
|
111
|
+
else if (observedRollouts < CONTEXT_GC_POLICY.largeExecProtectionRollouts) {
|
|
97
112
|
protectedTokens += reclaimedTokens;
|
|
98
113
|
retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - observedRollouts);
|
|
99
114
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { contextGcTargetKey } from '../../session/context-gc-checkpoint.js';
|
|
2
2
|
import { contextGcExecInputReplacement, contextGcExecOutputReplacement, normalizeContextGcExecResult } from '../../session/context-gc-replacements.js';
|
|
3
3
|
import { CONTEXT_GC_POLICY } from './gc-economics.js';
|
|
4
|
-
import {
|
|
4
|
+
import { targetFor, isRecord } from './gc-candidates.js';
|
|
5
|
+
import { terminalExecLifecycles } from '../../session/context-gc-exec-lifecycle.js';
|
|
5
6
|
export function collectExecCandidates(input, groups, existingTargets, candidateSavings) {
|
|
6
7
|
const eligible = [];
|
|
7
8
|
let protectedTokens = 0;
|
|
@@ -28,56 +29,18 @@ export function collectExecCandidates(input, groups, existingTargets, candidateS
|
|
|
28
29
|
result
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
? start.result.processId
|
|
42
|
-
: undefined;
|
|
43
|
-
if (!isStart) {
|
|
44
|
-
if (start.group.toolName !== 'exec_wait' || !processId)
|
|
45
|
-
continue;
|
|
46
|
-
// A process may start inside a cell or before the visible history boundary.
|
|
47
|
-
// Begin with its first visible wait, but never split an unfinished lifecycle.
|
|
48
|
-
const previous = execGroups.findLast((group) => (group.call?.record.sequence ?? Infinity) < start.group.call.record.sequence &&
|
|
49
|
-
((isRecord(group.call?.message.arguments) &&
|
|
50
|
-
group.call.message.arguments.processId === processId) ||
|
|
51
|
-
invocations.get(group.callId)?.result.processId === processId));
|
|
52
|
-
const previousStatus = previous && invocations.get(previous.callId)?.result.status;
|
|
53
|
-
if (previous && previousStatus !== 'completed' && previousStatus !== 'cancelled')
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
const lifecycle = [start];
|
|
57
|
-
let terminal = start.result.status !== 'running';
|
|
58
|
-
if (!terminal) {
|
|
59
|
-
if (!processId)
|
|
60
|
-
continue;
|
|
61
|
-
const relatedGroups = execGroups.filter((group) => (group.call?.record.sequence ?? 0) > start.group.call.record.sequence &&
|
|
62
|
-
isRecord(group.call?.message.arguments) &&
|
|
63
|
-
group.call.message.arguments.processId === processId);
|
|
64
|
-
if (relatedGroups.some((group) => !invocations.has(group.callId)) ||
|
|
65
|
-
relatedGroups.length === 0)
|
|
66
|
-
continue;
|
|
67
|
-
for (const relatedGroup of relatedGroups) {
|
|
68
|
-
const invocation = invocations.get(relatedGroup.callId);
|
|
69
|
-
lifecycle.push(invocation);
|
|
70
|
-
if (invocation.result.status !== 'running') {
|
|
71
|
-
terminal = true;
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!terminal || (processId && input.activeProcessIds.has(processId)))
|
|
77
|
-
continue;
|
|
32
|
+
const facts = execGroups.map((group) => ({
|
|
33
|
+
value: group,
|
|
34
|
+
callId: group.callId,
|
|
35
|
+
toolName: group.toolName,
|
|
36
|
+
sequence: group.call?.record.sequence ?? Number.MAX_SAFE_INTEGER,
|
|
37
|
+
...(isRecord(group.call?.message.arguments) ? { input: group.call.message.arguments } : {}),
|
|
38
|
+
...(invocations.get(group.callId) ? { result: invocations.get(group.callId).result } : {})
|
|
39
|
+
}));
|
|
40
|
+
for (const selected of terminalExecLifecycles(facts, input.activeProcessIds)) {
|
|
41
|
+
const lifecycle = selected.map((fact) => invocations.get(fact.callId));
|
|
78
42
|
const final = lifecycle.at(-1);
|
|
79
43
|
const evidenceSequence = final.group.result.record.sequence;
|
|
80
|
-
const observedRollouts = successfulRolloutsAfter(input.records, evidenceSequence, input.modelId, input.contextWindowTokens);
|
|
81
44
|
const replacements = [];
|
|
82
45
|
for (const invocation of lifecycle) {
|
|
83
46
|
if (invocation.group.evidenceOnly)
|
|
@@ -112,20 +75,34 @@ export function collectExecCandidates(input, groups, existingTargets, candidateS
|
|
|
112
75
|
}
|
|
113
76
|
if (replacements.length === 0)
|
|
114
77
|
continue;
|
|
78
|
+
const mature = [];
|
|
79
|
+
const awaitingRollouts = [];
|
|
80
|
+
let observedRollouts = Infinity;
|
|
81
|
+
for (const replacement of replacements) {
|
|
82
|
+
const protection = input.protection.after(Math.max(evidenceSequence, replacement.target.sequence));
|
|
83
|
+
if (protection.rollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts &&
|
|
84
|
+
protection.thinking >= 3) {
|
|
85
|
+
mature.push(replacement);
|
|
86
|
+
observedRollouts = Math.min(observedRollouts, protection.rollouts);
|
|
87
|
+
}
|
|
88
|
+
else if (protection.rollouts < CONTEXT_GC_POLICY.largeExecProtectionRollouts) {
|
|
89
|
+
awaitingRollouts.push(replacement);
|
|
90
|
+
retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - protection.rollouts);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (awaitingRollouts.length)
|
|
94
|
+
protectedTokens += Math.max(0, candidateSavings({ replacements: awaitingRollouts, observedRollouts: 0, evidenceSequence }));
|
|
95
|
+
if (!mature.length)
|
|
96
|
+
continue;
|
|
115
97
|
const candidate = {
|
|
116
|
-
replacements,
|
|
98
|
+
replacements: mature,
|
|
117
99
|
observedRollouts,
|
|
118
100
|
evidenceSequence
|
|
119
101
|
};
|
|
120
102
|
const reclaimedTokens = candidateSavings(candidate);
|
|
121
103
|
if (reclaimedTokens <= 0)
|
|
122
104
|
continue;
|
|
123
|
-
|
|
124
|
-
eligible.push(candidate);
|
|
125
|
-
else {
|
|
126
|
-
protectedTokens += reclaimedTokens;
|
|
127
|
-
retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - observedRollouts);
|
|
128
|
-
}
|
|
105
|
+
eligible.push(candidate);
|
|
129
106
|
}
|
|
130
107
|
return { eligible, protectedTokens, retryAfterRollouts };
|
|
131
108
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ContextGcReplacement } from '../../session/context-gc-checkpoint.js';
|
|
2
2
|
import { type ContextGcPlannerInput, type ContextCandidate, type CandidateSavings, type ToolGroup } from './gc-contracts.js';
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function collectDeterministicCandidates(input: ContextGcPlannerInput, groups: ReadonlyMap<string, ToolGroup>, existingReplacements: readonly ContextGcReplacement[], existingTargets: ReadonlySet<string>, candidateSavings: CandidateSavings): {
|
|
4
4
|
eligible: ContextCandidate[];
|
|
5
5
|
protectedTokens: number;
|
|
6
6
|
retryAfterRollouts: number;
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { contextGcLogicalPathKey, contextGcTargetKey } from '../../session/context-gc-checkpoint.js';
|
|
2
|
-
import { contextGcApplyPatchInputReplacement, contextGcPatchResultFromCompletedPayload, contextGcPatchResultFromFailedPayload, contextGcStaleReadReplacement } from '../../session/context-gc-replacements.js';
|
|
2
|
+
import { contextGcApplyPatchInputReplacement, contextGcPatchResultFromCompletedPayload, contextGcPatchResultFromFailedPayload, contextGcStaleReadReplacement, contextGcReadRangeCovers, contextGcCoveredReadReplacement } from '../../session/context-gc-replacements.js';
|
|
3
3
|
import { parseReadContextOutput } from '../../prompts/read-context.js';
|
|
4
4
|
import { CONTEXT_GC_POLICY } from './gc-economics.js';
|
|
5
|
-
import {
|
|
6
|
-
export function
|
|
5
|
+
import { targetFor } from './gc-candidates.js';
|
|
6
|
+
export function collectDeterministicCandidates(input, groups, existingReplacements, existingTargets, candidateSavings) {
|
|
7
7
|
const eligible = [];
|
|
8
8
|
let protectedTokens = 0;
|
|
9
9
|
let retryAfterRollouts = Infinity;
|
|
10
10
|
const existingReadReplacements = new Map(existingReplacements.flatMap((replacement) => replacement.kind === 'READ_OUTPUT_ITEMS'
|
|
11
11
|
? [[contextGcTargetKey(replacement.target), replacement]]
|
|
12
12
|
: []));
|
|
13
|
-
const reads = [...groups.values()]
|
|
13
|
+
const reads = [...groups.values()]
|
|
14
|
+
.filter((group) => group.toolName === 'read' &&
|
|
14
15
|
group.call?.message.role === 'tool_call' &&
|
|
15
|
-
group.result?.message.role === 'tool')
|
|
16
|
+
group.result?.message.role === 'tool')
|
|
17
|
+
.sort((left, right) => left.result.record.sequence - right.result.record.sequence);
|
|
16
18
|
const claimedReadItems = new Set([...existingReadReplacements.values()].flatMap((replacement) => replacement.items.map((item) => `${replacement.target.recordId}:${replacement.target.observationId ?? ''}:${item.index}`)));
|
|
17
19
|
const patches = [...groups.values()]
|
|
18
20
|
.flatMap((patch) => {
|
|
@@ -31,7 +33,6 @@ export function collectStageACandidates(input, groups, existingReplacements, exi
|
|
|
31
33
|
.sort((left, right) => left.evidenceSequence - right.evidenceSequence);
|
|
32
34
|
for (const { patch, result, terminal, evidenceSequence } of patches) {
|
|
33
35
|
const patchCall = patch.call;
|
|
34
|
-
const observedRollouts = successfulRolloutsAfter(input.records, evidenceSequence, input.modelId, input.contextWindowTokens);
|
|
35
36
|
const replacements = [];
|
|
36
37
|
const candidateReadItems = new Set();
|
|
37
38
|
const patchTarget = targetFor(patchCall.record, patchCall.message);
|
|
@@ -75,7 +76,7 @@ export function collectStageACandidates(input, groups, existingReplacements, exi
|
|
|
75
76
|
operation: matched.operation
|
|
76
77
|
}),
|
|
77
78
|
evidenceSequence,
|
|
78
|
-
|
|
79
|
+
supersededByCallId: patch.callId
|
|
79
80
|
}
|
|
80
81
|
];
|
|
81
82
|
});
|
|
@@ -97,21 +98,103 @@ export function collectStageACandidates(input, groups, existingReplacements, exi
|
|
|
97
98
|
}
|
|
98
99
|
if (replacements.length === 0)
|
|
99
100
|
continue;
|
|
101
|
+
const mature = [];
|
|
102
|
+
const awaitingRollouts = [];
|
|
103
|
+
let observedRollouts = Infinity;
|
|
104
|
+
for (const replacement of replacements) {
|
|
105
|
+
const protection = input.protection.after(Math.max(evidenceSequence, replacement.target.sequence));
|
|
106
|
+
if (protection.rollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts &&
|
|
107
|
+
protection.thinking >= 3) {
|
|
108
|
+
mature.push(replacement);
|
|
109
|
+
observedRollouts = Math.min(observedRollouts, protection.rollouts);
|
|
110
|
+
}
|
|
111
|
+
else if (protection.rollouts < CONTEXT_GC_POLICY.largeExecProtectionRollouts) {
|
|
112
|
+
awaitingRollouts.push(replacement);
|
|
113
|
+
retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - protection.rollouts);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (awaitingRollouts.length) {
|
|
117
|
+
protectedTokens += Math.max(0, candidateSavings({ replacements: awaitingRollouts, observedRollouts: 0, evidenceSequence }));
|
|
118
|
+
}
|
|
119
|
+
if (!mature.length)
|
|
120
|
+
continue;
|
|
100
121
|
const candidate = {
|
|
101
|
-
replacements,
|
|
122
|
+
replacements: mature,
|
|
102
123
|
observedRollouts,
|
|
103
124
|
evidenceSequence
|
|
104
125
|
};
|
|
105
126
|
const reclaimedTokens = candidateSavings(candidate);
|
|
106
127
|
if (reclaimedTokens <= 0)
|
|
107
128
|
continue;
|
|
108
|
-
for (const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
129
|
+
for (const replacement of mature) {
|
|
130
|
+
if (replacement.kind !== 'READ_OUTPUT_ITEMS')
|
|
131
|
+
continue;
|
|
132
|
+
for (const item of replacement.items) {
|
|
133
|
+
const key = `${replacement.target.recordId}:${replacement.target.observationId ?? ''}:${item.index}`;
|
|
134
|
+
if (candidateReadItems.has(key))
|
|
135
|
+
claimedReadItems.add(key);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
eligible.push(candidate);
|
|
139
|
+
}
|
|
140
|
+
const visibleReads = reads.flatMap((group) => {
|
|
141
|
+
if (group.evidenceOnly || group.failed || !group.completed)
|
|
142
|
+
return [];
|
|
143
|
+
const target = targetFor(group.result.record, group.result.message);
|
|
144
|
+
const parsed = parseReadContextOutput(group.result.message.content);
|
|
145
|
+
return target && parsed ? [{ group, target, items: parsed.items }] : [];
|
|
146
|
+
});
|
|
147
|
+
for (const [index, old] of visibleReads.entries()) {
|
|
148
|
+
for (const item of old.items) {
|
|
149
|
+
const itemKey = `${old.target.recordId}:${old.target.observationId ?? ''}:${item.index}`;
|
|
150
|
+
if (claimedReadItems.has(itemKey) || item.content.startsWith('<mar_context_gc '))
|
|
151
|
+
continue;
|
|
152
|
+
const pathKey = contextGcLogicalPathKey(item.path, input.workspace, input.platform);
|
|
153
|
+
if (!pathKey)
|
|
154
|
+
continue;
|
|
155
|
+
const later = visibleReads
|
|
156
|
+
.slice(index + 1)
|
|
157
|
+
.filter((entry) => entry.items.some((candidate) => contextGcLogicalPathKey(candidate.path, input.workspace, input.platform) ===
|
|
158
|
+
pathKey &&
|
|
159
|
+
contextGcReadRangeCovers(item, candidate) &&
|
|
160
|
+
!existingReadReplacements
|
|
161
|
+
.get(contextGcTargetKey(entry.target))
|
|
162
|
+
?.items.some((prior) => prior.index === candidate.index)));
|
|
163
|
+
const successor = later.findLast((entry) => {
|
|
164
|
+
const count = input.protection.after(entry.target.sequence);
|
|
165
|
+
return count.rollouts >= 6 && count.thinking >= 3;
|
|
166
|
+
});
|
|
167
|
+
if (!successor)
|
|
168
|
+
continue;
|
|
169
|
+
const existingRead = existingReadReplacements.get(contextGcTargetKey(old.target));
|
|
170
|
+
const evidenceSequence = successor.target.sequence;
|
|
171
|
+
const replacement = {
|
|
172
|
+
kind: 'READ_OUTPUT_ITEMS',
|
|
173
|
+
callId: old.group.callId,
|
|
174
|
+
target: old.target,
|
|
175
|
+
items: [
|
|
176
|
+
...(existingRead?.items ?? []),
|
|
177
|
+
{
|
|
178
|
+
index: item.index,
|
|
179
|
+
path: item.path,
|
|
180
|
+
replacement: contextGcCoveredReadReplacement(item, successor.group.callId),
|
|
181
|
+
evidenceSequence,
|
|
182
|
+
supersededByCallId: successor.group.callId
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
evidenceSequences: [
|
|
186
|
+
...new Set([...(existingRead?.evidenceSequences ?? []), evidenceSequence])
|
|
187
|
+
]
|
|
188
|
+
};
|
|
189
|
+
const candidate = {
|
|
190
|
+
replacements: [replacement],
|
|
191
|
+
observedRollouts: input.protection.after(evidenceSequence).rollouts,
|
|
192
|
+
evidenceSequence
|
|
193
|
+
};
|
|
194
|
+
if (candidateSavings(candidate) > 0) {
|
|
195
|
+
eligible.push(candidate);
|
|
196
|
+
claimedReadItems.add(itemKey);
|
|
197
|
+
}
|
|
115
198
|
}
|
|
116
199
|
}
|
|
117
200
|
return { eligible, protectedTokens, retryAfterRollouts };
|