@myagentroam/agent 0.10.59 → 0.10.60
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/agent-core/runtime/compaction/history.d.ts +1 -1
- package/dist/agent-core/runtime/compaction/retained-context.d.ts +1 -1
- package/dist/agent-core/runtime/compaction/retained-context.js +1 -1
- package/dist/agent-core/runtime/compaction/smart.d.ts +1 -1
- package/dist/agent-core/runtime/compaction/smart.js +1 -1
- package/dist/agent-core/runtime/context/gc-runtime.d.ts +1 -1
- package/dist/agent-core/runtime/context/gc-runtime.js +20 -14
- package/dist/agent-core/runtime/context/gc.d.ts +1 -1
- package/dist/agent-core/runtime/context/gc.js +4 -10
- package/dist/agent-core/runtime/context/projection.d.ts +1 -1
- package/dist/agent-core/runtime/context/projection.js +3 -5
- package/dist/agent-core/runtime/context/restore.d.ts +4 -4
- package/dist/agent-core/runtime/context/restore.js +1 -1
- package/dist/agent-core/runtime/context/usage.d.ts +2 -2
- package/dist/agent-core/runtime/context/usage.js +1 -1
- package/dist/agent-core/runtime/context/world-state.d.ts +2 -2
- package/dist/agent-core/runtime/context/world-state.js +1 -1
- package/dist/agent-core/sdk/agent.js +1 -1
- package/dist/agent-core/sdk/native-steer.d.ts +1 -1
- package/dist/agent-core/sdk/session.d.ts +1 -1
- package/dist/agent-core/sdk/session.js +1 -1
- package/dist/agent-core/sdk/subagent-backend.d.ts +1 -1
- package/dist/agent-core/session/catalog.js +1 -1
- package/dist/agent-core/session/context-gc-checkpoint.d.ts +4 -8
- package/dist/agent-core/session/context-gc-checkpoint.js +82 -137
- package/dist/agent-core/session/context-gc-observations.d.ts +1 -1
- package/dist/agent-core/session/context-gc-replacements.d.ts +4 -7
- package/dist/agent-core/session/context-gc-replacements.js +25 -59
- package/dist/agent-core/session/context-gc-subagent.d.ts +0 -3
- package/dist/agent-core/session/context-gc-subagent.js +2 -16
- package/dist/agent-core/session/deferred-input.d.ts +1 -1
- package/dist/agent-core/session/store/current-state.d.ts +17 -0
- package/dist/agent-core/session/store/current-state.js +53 -0
- package/dist/agent-core/session/store/directory-index.d.ts +18 -0
- package/dist/agent-core/session/store/directory-index.js +163 -0
- package/dist/agent-core/session/store/execution-state.d.ts +17 -0
- package/dist/agent-core/session/store/execution-state.js +178 -0
- package/dist/agent-core/session/store/history.d.ts +25 -0
- package/dist/agent-core/session/store/history.js +157 -0
- package/dist/agent-core/session/store/images.d.ts +21 -0
- package/dist/agent-core/session/store/images.js +248 -0
- package/dist/agent-core/session/store/index.d.ts +4 -0
- package/dist/agent-core/session/store/index.js +2 -0
- package/dist/agent-core/session/{jsonl-store.d.ts → store/jsonl-store.d.ts} +12 -92
- package/dist/agent-core/session/{jsonl-store.js → store/jsonl-store.js} +108 -1053
- package/dist/agent-core/session/store/records.d.ts +23 -0
- package/dist/agent-core/session/store/records.js +249 -0
- package/dist/agent-core/session/store/types.d.ts +83 -0
- package/dist/agent-core/session/store/types.js +1 -0
- package/dist/agent-core/session/tool-result-resources.d.ts +1 -1
- package/dist/agent-core/subagent/session-controller.d.ts +1 -1
- package/dist/agent-core/tools/code-mode/state.d.ts +1 -1
- package/dist/agent-core/tools/code-mode/state.js +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
1
|
import { posix, win32 } from 'node:path';
|
|
3
2
|
import { isDeepStrictEqual } from 'node:util';
|
|
4
3
|
import { codeCallEvidence, codePartialPatchCallEvidence, callsFromRecord, codeObservationEvidence, observationsFromRecord, retainedCodeInputs } from './context-gc-observations.js';
|
|
@@ -10,7 +9,7 @@ export const CONTEXT_GC_MAX_TARGETS = 4_096;
|
|
|
10
9
|
/** Disposable memoization for one evaluation's immutable record snapshot. */
|
|
11
10
|
export class ContextGcCheckpointCache {
|
|
12
11
|
#snapshot;
|
|
13
|
-
read(records, boundarySequence = records.at(-1)?.sequence ?? 0, workspace
|
|
12
|
+
read(records, boundarySequence = records.at(-1)?.sequence ?? 0, workspace) {
|
|
14
13
|
const cached = this.#snapshot;
|
|
15
14
|
if (cached?.records === records &&
|
|
16
15
|
cached.boundarySequence === boundarySequence &&
|
|
@@ -24,18 +23,6 @@ export class ContextGcCheckpointCache {
|
|
|
24
23
|
export function contextGcMinimumUsageGrowth(contextWindowTokens) {
|
|
25
24
|
return Math.max(2_000, Math.floor(contextWindowTokens * 0.05));
|
|
26
25
|
}
|
|
27
|
-
const PROJECTION_RECORD_TYPES = new Set([
|
|
28
|
-
'compact.completed',
|
|
29
|
-
'context.environment',
|
|
30
|
-
'execution.header',
|
|
31
|
-
'message.completed',
|
|
32
|
-
'model.context',
|
|
33
|
-
'plan.completed',
|
|
34
|
-
'turn.user'
|
|
35
|
-
]);
|
|
36
|
-
export function contextGcRecordSourceHash(record) {
|
|
37
|
-
return createHash('sha256').update(stableJson(record.payload)).digest('hex');
|
|
38
|
-
}
|
|
39
26
|
export function contextGcLogicalPathKey(path, workspace, platform) {
|
|
40
27
|
if (!path || !workspace || path.includes('\0') || workspace.includes('\0'))
|
|
41
28
|
return undefined;
|
|
@@ -76,32 +63,34 @@ export function contextGcBaseFromRecords(records, boundarySequence = records.at(
|
|
|
76
63
|
export function contextGcBaseSequence(base) {
|
|
77
64
|
return base.type === 'COMPACT' ? base.sequence : base.firstSequence;
|
|
78
65
|
}
|
|
79
|
-
export function contextGcSourceProjectionHash(records, boundarySequence, base = contextGcBaseFromRecords(records, boundarySequence)) {
|
|
80
|
-
if (!base || !isValidBoundary(boundarySequence))
|
|
81
|
-
return undefined;
|
|
82
|
-
const baseSequence = contextGcBaseSequence(base);
|
|
83
|
-
if (!sameContextGcBase(base, contextGcBaseFromRecords(records, boundarySequence)))
|
|
84
|
-
return undefined;
|
|
85
|
-
const source = records
|
|
86
|
-
.filter((record) => record.sequence >= baseSequence &&
|
|
87
|
-
record.sequence <= boundarySequence &&
|
|
88
|
-
PROJECTION_RECORD_TYPES.has(record.type))
|
|
89
|
-
.map((record) => ({
|
|
90
|
-
sequence: record.sequence,
|
|
91
|
-
recordId: record.recordId,
|
|
92
|
-
type: record.type,
|
|
93
|
-
payload: record.payload
|
|
94
|
-
}));
|
|
95
|
-
return createHash('sha256').update(stableJson(source)).digest('hex');
|
|
96
|
-
}
|
|
97
66
|
export function normalizeContextGcReplacements(replacements) {
|
|
98
67
|
const normalized = new Map();
|
|
68
|
+
const readItems = new Map();
|
|
99
69
|
for (const replacement of replacements) {
|
|
100
70
|
if (!isContextGcReplacement(replacement))
|
|
101
71
|
return undefined;
|
|
102
|
-
|
|
72
|
+
const value = structuredClone(replacement);
|
|
73
|
+
const key = contextGcReplacementKey(value);
|
|
74
|
+
if (value.kind === 'READ_OUTPUT_ITEMS') {
|
|
75
|
+
const items = normalized.get(key)?.kind === 'READ_OUTPUT_ITEMS'
|
|
76
|
+
? readItems.get(key)
|
|
77
|
+
: new Map();
|
|
78
|
+
for (const item of value.items)
|
|
79
|
+
items.set(item.index, item);
|
|
80
|
+
readItems.set(key, items);
|
|
81
|
+
}
|
|
82
|
+
normalized.set(key, value);
|
|
103
83
|
}
|
|
104
|
-
const values = [...normalized.
|
|
84
|
+
const values = [...normalized].map(([key, value]) => {
|
|
85
|
+
if (value.kind !== 'READ_OUTPUT_ITEMS')
|
|
86
|
+
return value;
|
|
87
|
+
const items = [...readItems.get(key).values()];
|
|
88
|
+
return {
|
|
89
|
+
...value,
|
|
90
|
+
items,
|
|
91
|
+
evidenceSequences: [...new Set(items.map((item) => item.evidenceSequence))]
|
|
92
|
+
};
|
|
93
|
+
});
|
|
105
94
|
const targets = new Set(values.flatMap((replacement) => [
|
|
106
95
|
contextGcTargetKey(replacement.target),
|
|
107
96
|
...(replacement.kind === 'TOOL_CALL_INPUT'
|
|
@@ -122,7 +111,6 @@ export function parseContextGcCheckpoint(record) {
|
|
|
122
111
|
!isPositiveInteger(payload.generation) ||
|
|
123
112
|
!isContextGcBase(payload.base) ||
|
|
124
113
|
!isPositiveInteger(payload.boundarySequence) ||
|
|
125
|
-
!isSha256(payload.sourceProjectionHash) ||
|
|
126
114
|
!Array.isArray(payload.replacements) ||
|
|
127
115
|
payload.replacements.length === 0 ||
|
|
128
116
|
!isContextGcTrigger(payload.trigger) ||
|
|
@@ -138,14 +126,13 @@ export function parseContextGcCheckpoint(record) {
|
|
|
138
126
|
generation: payload.generation,
|
|
139
127
|
base: structuredClone(payload.base),
|
|
140
128
|
boundarySequence: payload.boundarySequence,
|
|
141
|
-
sourceProjectionHash: payload.sourceProjectionHash,
|
|
142
129
|
replacements,
|
|
143
130
|
trigger: structuredClone(payload.trigger),
|
|
144
131
|
economics: structuredClone(payload.economics),
|
|
145
132
|
metrics: structuredClone(payload.metrics)
|
|
146
133
|
};
|
|
147
134
|
}
|
|
148
|
-
export function validateContextGcCheckpoint(records, record, workspace = sessionWorkspace(records)) {
|
|
135
|
+
export function validateContextGcCheckpoint(records, record, workspace = sessionWorkspace(records), committedReplacements = []) {
|
|
149
136
|
const payload = parseContextGcCheckpoint(record);
|
|
150
137
|
if (!payload || payload.boundarySequence >= record.sequence)
|
|
151
138
|
return undefined;
|
|
@@ -153,28 +140,45 @@ export function validateContextGcCheckpoint(records, record, workspace = session
|
|
|
153
140
|
if (!sameContextGcBase(payload.base, base))
|
|
154
141
|
return undefined;
|
|
155
142
|
const baseSequence = contextGcBaseSequence(payload.base);
|
|
156
|
-
if (contextGcSourceProjectionHash(records, payload.boundarySequence, payload.base) !==
|
|
157
|
-
payload.sourceProjectionHash)
|
|
158
|
-
return undefined;
|
|
159
143
|
// Later cell completion cannot retroactively authorize an earlier checkpoint.
|
|
160
144
|
records = records.filter((item) => item.sequence <= payload.boundarySequence);
|
|
161
145
|
const recordsBySequence = new Map(records.map((item) => [item.sequence, item]));
|
|
162
146
|
if (!triggerMatchesRecords(records, recordsBySequence, payload))
|
|
163
147
|
return undefined;
|
|
148
|
+
const committed = new Map(committedReplacements.map((replacement) => [contextGcReplacementKey(replacement), replacement]));
|
|
149
|
+
const changed = payload.replacements.flatMap((replacement) => {
|
|
150
|
+
const previous = committed.get(contextGcReplacementKey(replacement));
|
|
151
|
+
if (isDeepStrictEqual(previous, replacement))
|
|
152
|
+
return [];
|
|
153
|
+
if (previous?.kind === 'READ_OUTPUT_ITEMS' && replacement.kind === 'READ_OUTPUT_ITEMS') {
|
|
154
|
+
const retained = new Map(previous.items.map((item) => [item.index, item]));
|
|
155
|
+
const items = replacement.items.filter((item) => !isDeepStrictEqual(retained.get(item.index), item));
|
|
156
|
+
if (items.length > 0)
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
...replacement,
|
|
160
|
+
items,
|
|
161
|
+
evidenceSequences: [...new Set(items.map((item) => item.evidenceSequence))]
|
|
162
|
+
}
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
return [replacement];
|
|
166
|
+
});
|
|
164
167
|
const targets = payload.replacements.flatMap((replacement) => [
|
|
165
168
|
replacement.target,
|
|
166
169
|
...(replacement.kind === 'TOOL_CALL_INPUT' ? replacement.providerAliasTargets : [])
|
|
167
170
|
]);
|
|
168
171
|
if (new Set(targets.map(contextGcTargetKey)).size !== targets.length)
|
|
169
172
|
return undefined;
|
|
170
|
-
for (const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
for (const replacement of changed) {
|
|
174
|
+
for (const target of [
|
|
175
|
+
replacement.target,
|
|
176
|
+
...(replacement.kind === 'TOOL_CALL_INPUT' ? replacement.providerAliasTargets : [])
|
|
177
|
+
]) {
|
|
178
|
+
const source = recordsBySequence.get(target.sequence);
|
|
179
|
+
if (!source || !targetMatchesRecord(target, source))
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
178
182
|
const evidence = replacement.kind === 'READ_OUTPUT_ITEMS'
|
|
179
183
|
? replacement.evidenceSequences
|
|
180
184
|
: [
|
|
@@ -194,18 +198,23 @@ export function validateContextGcCheckpoint(records, record, workspace = session
|
|
|
194
198
|
}
|
|
195
199
|
return payload;
|
|
196
200
|
}
|
|
197
|
-
export function latestValidContextGcCheckpoint(records, boundarySequence = records.at(-1)?.sequence ?? 0,
|
|
201
|
+
export function latestValidContextGcCheckpoint(records, boundarySequence = records.at(-1)?.sequence ?? 0, _workspace) {
|
|
198
202
|
const base = contextGcBaseFromRecords(records, boundarySequence);
|
|
199
203
|
if (!base)
|
|
200
204
|
return undefined;
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
record
|
|
205
|
-
.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
const baseSequence = contextGcBaseSequence(base);
|
|
206
|
+
// Committed local checkpoints are trusted; historical evidence is checked at commit only.
|
|
207
|
+
for (let index = records.length - 1; index >= 0; index--) {
|
|
208
|
+
const record = records[index];
|
|
209
|
+
if (record.sequence < baseSequence)
|
|
210
|
+
break;
|
|
211
|
+
if (record.sequence > boundarySequence || record.type !== 'context.gc.completed')
|
|
212
|
+
continue;
|
|
213
|
+
const payload = parseContextGcCheckpoint(record);
|
|
214
|
+
if (payload &&
|
|
215
|
+
payload.boundarySequence >= baseSequence &&
|
|
216
|
+
payload.boundarySequence < record.sequence &&
|
|
217
|
+
sameContextGcBase(payload.base, base))
|
|
209
218
|
return { record, payload };
|
|
210
219
|
}
|
|
211
220
|
return undefined;
|
|
@@ -222,7 +231,6 @@ export function sameContextGcBase(left, right) {
|
|
|
222
231
|
function targetMatchesRecord(target, record) {
|
|
223
232
|
if (record.type !== 'model.context' ||
|
|
224
233
|
record.recordId !== target.recordId ||
|
|
225
|
-
contextGcRecordSourceHash(record) !== target.sourceHash ||
|
|
226
234
|
!isRecord(record.payload) ||
|
|
227
235
|
record.payload.role !== target.role)
|
|
228
236
|
return false;
|
|
@@ -283,10 +291,7 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
283
291
|
contextGcStaleReadReplacement({
|
|
284
292
|
path: item.path,
|
|
285
293
|
content: original.content,
|
|
286
|
-
|
|
287
|
-
supersededByCallId: evidence.callId,
|
|
288
|
-
operation: evidence.file.operation,
|
|
289
|
-
formatVersion: staleReadReplacementFormatVersion(item.replacement)
|
|
294
|
+
operation: evidence.file.operation
|
|
290
295
|
}));
|
|
291
296
|
});
|
|
292
297
|
}
|
|
@@ -296,7 +301,7 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
296
301
|
return (evidence?.callId === replacement.callId &&
|
|
297
302
|
evidence.call.sequence === replacement.target.sequence &&
|
|
298
303
|
result !== undefined &&
|
|
299
|
-
isDeepStrictEqual(replacement.replacementArguments, contextGcApplyPatchInputReplacement(source.payload.arguments, result
|
|
304
|
+
isDeepStrictEqual(replacement.replacementArguments, contextGcApplyPatchInputReplacement(source.payload.arguments, result)));
|
|
300
305
|
}
|
|
301
306
|
if (replacement.kind === 'TOOL_CALL_INPUT' && replacement.toolName === 'code_exec') {
|
|
302
307
|
const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, replacement.toolName);
|
|
@@ -363,10 +368,7 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
363
368
|
contextGcSubagentOutputReplacement({
|
|
364
369
|
sourceContent: sourceEvidence.result.payload.content,
|
|
365
370
|
source: sourceSnapshot,
|
|
366
|
-
terminal: terminalSnapshot
|
|
367
|
-
terminalCallId: replacement.terminalCallId,
|
|
368
|
-
evidenceSequence: replacement.evidenceSequence,
|
|
369
|
-
formatVersion: subagentReplacementFormatVersion(replacement.replacementContent)
|
|
371
|
+
terminal: terminalSnapshot
|
|
370
372
|
}));
|
|
371
373
|
}
|
|
372
374
|
if (replacement.kind === 'WEB_OUTPUT') {
|
|
@@ -387,50 +389,12 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
387
389
|
if (!isSuccessfulExecLifecycleEvidence(replacement, source, records, recordsBySequence))
|
|
388
390
|
return false;
|
|
389
391
|
if (replacement.kind === 'TOOL_CALL_INPUT')
|
|
390
|
-
return isDeepStrictEqual(replacement.replacementArguments, contextGcExecInputReplacement(source.payload.arguments,
|
|
392
|
+
return isDeepStrictEqual(replacement.replacementArguments, contextGcExecInputReplacement(source.payload.arguments, replacement.toolName));
|
|
391
393
|
const targetEvidence = successfulToolEvidence(records, recordsBySequence, source.sequence, 'exec', replacement.callId) ??
|
|
392
394
|
successfulToolEvidence(records, recordsBySequence, source.sequence, 'exec_wait', replacement.callId);
|
|
393
395
|
const result = normalizeContextGcExecResult(source.payload.content, targetEvidence?.completed.payload);
|
|
394
396
|
return (result !== undefined &&
|
|
395
|
-
replacement.replacementContent ===
|
|
396
|
-
contextGcExecOutputReplacement(result, execOutputReplacementFormatVersion(replacement.replacementContent)));
|
|
397
|
-
}
|
|
398
|
-
function staleReadReplacementFormatVersion(value) {
|
|
399
|
-
return value.includes(' formatVersion="2"') ? 2 : 1;
|
|
400
|
-
}
|
|
401
|
-
function applyPatchReplacementFormatVersion(value) {
|
|
402
|
-
return typeof value === 'string' && value.includes(' formatVersion="2"') ? 2 : 1;
|
|
403
|
-
}
|
|
404
|
-
function subagentReplacementFormatVersion(value) {
|
|
405
|
-
try {
|
|
406
|
-
const parsed = JSON.parse(value);
|
|
407
|
-
return isRecord(parsed) && isRecord(parsed.contextGc) && parsed.contextGc.formatVersion === 2
|
|
408
|
-
? 2
|
|
409
|
-
: 1;
|
|
410
|
-
}
|
|
411
|
-
catch {
|
|
412
|
-
return 1;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
function execInputReplacementFormatVersion(value) {
|
|
416
|
-
if (typeof value === 'string' && value.includes(' formatVersion="3"'))
|
|
417
|
-
return 3;
|
|
418
|
-
if (!isRecord(value) || !isRecord(value.contextGc))
|
|
419
|
-
return 1;
|
|
420
|
-
return value.contextGc.formatVersion === 3 ? 3 : value.contextGc.formatVersion === 2 ? 2 : 1;
|
|
421
|
-
}
|
|
422
|
-
function execOutputReplacementFormatVersion(value) {
|
|
423
|
-
try {
|
|
424
|
-
const parsed = JSON.parse(value);
|
|
425
|
-
return isRecord(parsed) && parsed.contextGcFormatVersion === 3
|
|
426
|
-
? 3
|
|
427
|
-
: isRecord(parsed) && parsed.contextGcFormatVersion === 2
|
|
428
|
-
? 2
|
|
429
|
-
: 1;
|
|
430
|
-
}
|
|
431
|
-
catch {
|
|
432
|
-
return 1;
|
|
433
|
-
}
|
|
397
|
+
replacement.replacementContent === contextGcExecOutputReplacement(result));
|
|
434
398
|
}
|
|
435
399
|
function toolNameForCallId(records, callId, beforeSequence) {
|
|
436
400
|
const call = modelToolCallForCallId(records, callId, beforeSequence);
|
|
@@ -713,7 +677,7 @@ function isContextGcReplacement(value) {
|
|
|
713
677
|
if (value.kind === 'READ_OUTPUT_ITEMS') {
|
|
714
678
|
const items = value.items;
|
|
715
679
|
const evidenceSequences = value.evidenceSequences;
|
|
716
|
-
|
|
680
|
+
if (!(hasOnlyKeys(value, ['kind', 'callId', 'target', 'items', 'evidenceSequences']) &&
|
|
717
681
|
value.target.role === 'tool' &&
|
|
718
682
|
Array.isArray(items) &&
|
|
719
683
|
items.length > 0 &&
|
|
@@ -732,11 +696,14 @@ function isContextGcReplacement(value) {
|
|
|
732
696
|
isPositiveInteger(item.evidenceSequence)) &&
|
|
733
697
|
Array.isArray(evidenceSequences) &&
|
|
734
698
|
evidenceSequences.length > 0 &&
|
|
735
|
-
evidenceSequences.every(isPositiveInteger)
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
699
|
+
evidenceSequences.every(isPositiveInteger)))
|
|
700
|
+
return false;
|
|
701
|
+
const evidence = new Set(evidenceSequences);
|
|
702
|
+
const usedEvidence = new Set(items.map((item) => item.evidenceSequence));
|
|
703
|
+
return (new Set(items.map((item) => item.index)).size === items.length &&
|
|
704
|
+
evidence.size === evidenceSequences.length &&
|
|
705
|
+
usedEvidence.size === evidence.size &&
|
|
706
|
+
evidenceSequences.every((sequence) => usedEvidence.has(sequence)));
|
|
740
707
|
}
|
|
741
708
|
if (value.kind === 'TOOL_CALL_INPUT')
|
|
742
709
|
return (hasOnlyKeys(value, [
|
|
@@ -829,23 +796,14 @@ function isContextGcReplacement(value) {
|
|
|
829
796
|
}
|
|
830
797
|
function isContextGcTarget(value) {
|
|
831
798
|
return (isRecord(value) &&
|
|
832
|
-
hasOnlyKeys(value, [
|
|
833
|
-
'sequence',
|
|
834
|
-
'recordId',
|
|
835
|
-
'role',
|
|
836
|
-
'callId',
|
|
837
|
-
'toolName',
|
|
838
|
-
'sourceHash',
|
|
839
|
-
'observationId'
|
|
840
|
-
]) &&
|
|
799
|
+
hasOnlyKeys(value, ['sequence', 'recordId', 'role', 'callId', 'toolName', 'observationId']) &&
|
|
841
800
|
(value.observationId === undefined ||
|
|
842
801
|
(value.role === 'tool' && isNonemptyString(value.observationId))) &&
|
|
843
802
|
isPositiveInteger(value.sequence) &&
|
|
844
803
|
isNonemptyString(value.recordId) &&
|
|
845
804
|
(value.role === 'tool_call' || value.role === 'tool' || value.role === 'provider') &&
|
|
846
805
|
(value.callId === undefined || isNonemptyString(value.callId)) &&
|
|
847
|
-
(value.toolName === undefined || isNonemptyString(value.toolName))
|
|
848
|
-
isSha256(value.sourceHash));
|
|
806
|
+
(value.toolName === undefined || isNonemptyString(value.toolName)));
|
|
849
807
|
}
|
|
850
808
|
function isContextGcBase(value) {
|
|
851
809
|
return (isRecord(value) &&
|
|
@@ -928,9 +886,6 @@ function hasOnlyKeys(value, keys) {
|
|
|
928
886
|
function isNonemptyString(value) {
|
|
929
887
|
return typeof value === 'string' && value.length > 0;
|
|
930
888
|
}
|
|
931
|
-
function isSha256(value) {
|
|
932
|
-
return typeof value === 'string' && /^[a-f0-9]{64}$/u.test(value);
|
|
933
|
-
}
|
|
934
889
|
function isPositiveInteger(value) {
|
|
935
890
|
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
936
891
|
}
|
|
@@ -943,13 +898,3 @@ function isNonnegativeFinite(value) {
|
|
|
943
898
|
function isValidBoundary(value) {
|
|
944
899
|
return Number.isSafeInteger(value) && value > 0;
|
|
945
900
|
}
|
|
946
|
-
function stableJson(value) {
|
|
947
|
-
if (Array.isArray(value))
|
|
948
|
-
return `[${value.map(stableJson).join(',')}]`;
|
|
949
|
-
if (value !== null && typeof value === 'object')
|
|
950
|
-
return `{${Object.entries(value)
|
|
951
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
952
|
-
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
953
|
-
.join(',')}}`;
|
|
954
|
-
return JSON.stringify(value) ?? 'undefined';
|
|
955
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SessionRecord } from './
|
|
1
|
+
import type { SessionRecord } from './store/index.js';
|
|
2
2
|
import { type CodeObservation, type CodeToolCallRecord } from '../tools/code-mode/observations.js';
|
|
3
3
|
/** Resolve private observation identity to real dispatch facts, not Provider tool calls. */
|
|
4
4
|
export declare function codeObservationEvidence(records: readonly SessionRecord[], result: SessionRecord, observation: Extract<CodeObservation, {
|
|
@@ -47,19 +47,16 @@ export type ContextGcExecResult = {
|
|
|
47
47
|
};
|
|
48
48
|
export declare function contextGcPatchResultFromCompletedPayload(payload: unknown): ContextGcPatchResult | undefined;
|
|
49
49
|
export declare function contextGcPatchResultFromFailedPayload(payload: unknown): ContextGcPatchResult | undefined;
|
|
50
|
-
export declare function contextGcApplyPatchInputReplacement(arguments_: unknown, result: ContextGcPatchResult
|
|
50
|
+
export declare function contextGcApplyPatchInputReplacement(arguments_: unknown, result: ContextGcPatchResult): string;
|
|
51
51
|
export declare function contextGcStaleReadReplacement(input: {
|
|
52
52
|
readonly path: string;
|
|
53
53
|
readonly content: string;
|
|
54
|
-
readonly evidenceSequence: number;
|
|
55
|
-
readonly supersededByCallId: string;
|
|
56
54
|
readonly operation: Exclude<ContextGcPatchFile['operation'], 'added'>;
|
|
57
|
-
readonly formatVersion?: 1 | 2;
|
|
58
55
|
}): string;
|
|
59
56
|
export declare function contextGcWebOutputReplacement(toolName: ContextGcWebToolName, originalContent: string, truncated: boolean): string;
|
|
60
|
-
export declare function contextGcExecInputReplacement(arguments_: unknown,
|
|
61
|
-
export declare function contextGcExecOutputReplacement(result: ContextGcExecResult
|
|
57
|
+
export declare function contextGcExecInputReplacement(arguments_: unknown, toolName?: string): unknown;
|
|
58
|
+
export declare function contextGcExecOutputReplacement(result: ContextGcExecResult): string;
|
|
62
59
|
export declare function normalizeContextGcExecResult(modelContent: unknown, completedPayload: unknown): ContextGcExecResult | undefined;
|
|
63
60
|
export declare function parseContextGcExecResult(value: unknown): ContextGcExecResult | undefined;
|
|
64
61
|
/** A returned tool wait is not proof that its retained JavaScript cell finished. */
|
|
65
|
-
export declare function codeCellCompletionSequence(records: readonly import('./
|
|
62
|
+
export declare function codeCellCompletionSequence(records: readonly import('./store/index.js').SessionRecord[], completed: import('./store/index.js').SessionRecord): number | undefined;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { contextGcMarkup } from './context-gc-markup.js';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
2
|
import { parseApplyPatchResult, parseApplyPatchPartialResult } from '../tools/shared/patch-result.js';
|
|
4
3
|
const RAW_MODEL_CONTEXT_PREVIEW_CHARACTERS = 256;
|
|
5
4
|
const EXEC_INPUT_COMPLETE_CHARACTERS = 512;
|
|
@@ -51,31 +50,22 @@ export function contextGcPatchResultFromFailedPayload(payload) {
|
|
|
51
50
|
return undefined;
|
|
52
51
|
return parseApplyPatchPartialResult(payload.data);
|
|
53
52
|
}
|
|
54
|
-
export function contextGcApplyPatchInputReplacement(arguments_, result
|
|
53
|
+
export function contextGcApplyPatchInputReplacement(arguments_, result) {
|
|
55
54
|
const original = typeof arguments_ === 'string' ? arguments_ : stableJson(arguments_);
|
|
56
55
|
const summary = result.files
|
|
57
56
|
.map((file) => `${file.operation} ${file.path}${file.targetPath ? ` -> ${file.targetPath}` : ''} (+${file.addedLines}/-${file.deletedLines})`)
|
|
58
57
|
.join('; ');
|
|
59
58
|
return contextGcMarkup('successful_apply_patch_input', {
|
|
60
|
-
|
|
59
|
+
formatVersion: 2,
|
|
61
60
|
originalBytes: Buffer.byteLength(original),
|
|
62
|
-
...(formatVersion === 1 ? { originalSha256: sha256(original), evidenceSequence } : {}),
|
|
63
61
|
contextGc: `Applied successfully: ${summary}. Totals: ${result.totals.files} files, +${result.totals.addedLines}/-${result.totals.deletedLines}. The original patch body was reclaimed.`
|
|
64
62
|
});
|
|
65
63
|
}
|
|
66
64
|
export function contextGcStaleReadReplacement(input) {
|
|
67
|
-
const formatVersion = input.formatVersion ?? 2;
|
|
68
65
|
return contextGcMarkup('stale_read', {
|
|
69
66
|
path: input.path,
|
|
70
|
-
|
|
67
|
+
formatVersion: 2,
|
|
71
68
|
originalBytes: Buffer.byteLength(input.content),
|
|
72
|
-
...(formatVersion === 1
|
|
73
|
-
? {
|
|
74
|
-
originalSha256: sha256(input.content),
|
|
75
|
-
supersededByCallId: input.supersededByCallId,
|
|
76
|
-
evidenceSequence: input.evidenceSequence
|
|
77
|
-
}
|
|
78
|
-
: {}),
|
|
79
69
|
operation: input.operation,
|
|
80
70
|
contextGc: 'This read predates a successful apply_patch. Re-read the path before relying on its contents.'
|
|
81
71
|
});
|
|
@@ -92,12 +82,11 @@ export function contextGcWebOutputReplacement(toolName, originalContent, truncat
|
|
|
92
82
|
contextGc: 'Detailed web tool output was reclaimed after six successful rollouts. An incomplete preview is not the complete search result or page, and GC did not access the network.'
|
|
93
83
|
});
|
|
94
84
|
}
|
|
95
|
-
export function contextGcExecInputReplacement(arguments_,
|
|
85
|
+
export function contextGcExecInputReplacement(arguments_, toolName = 'exec') {
|
|
96
86
|
if (!isRecord(arguments_))
|
|
97
87
|
return contextGcMarkup('terminal_exec_input', {
|
|
98
|
-
|
|
99
|
-
originalBytes: Buffer.byteLength(stableJson(arguments_))
|
|
100
|
-
...(formatVersion === 3 ? {} : { originalSha256: sha256(stableJson(arguments_)) })
|
|
88
|
+
formatVersion: 3,
|
|
89
|
+
originalBytes: Buffer.byteLength(stableJson(arguments_))
|
|
101
90
|
});
|
|
102
91
|
const action = typeof arguments_.action === 'string'
|
|
103
92
|
? arguments_.action
|
|
@@ -111,21 +100,20 @@ export function contextGcExecInputReplacement(arguments_, formatVersion = 3, too
|
|
|
111
100
|
action,
|
|
112
101
|
contextGc: {
|
|
113
102
|
kind: 'terminal_exec_input',
|
|
114
|
-
|
|
115
|
-
originalBytes: Buffer.byteLength(original)
|
|
116
|
-
...(formatVersion <= 2 ? { originalSha256: sha256(original) } : {})
|
|
103
|
+
formatVersion: 3,
|
|
104
|
+
originalBytes: Buffer.byteLength(original)
|
|
117
105
|
}
|
|
118
106
|
};
|
|
119
107
|
for (const key of ['cwd', 'processId', 'timeoutMs', 'yieldMs'])
|
|
120
108
|
if (arguments_[key] !== undefined)
|
|
121
109
|
retained[key] = arguments_[key];
|
|
122
110
|
if (typeof arguments_.command === 'string')
|
|
123
|
-
retainExecText(retained, 'command', arguments_.command
|
|
111
|
+
retainExecText(retained, 'command', arguments_.command);
|
|
124
112
|
if (typeof arguments_.data === 'string')
|
|
125
|
-
retainExecText(retained, 'data', arguments_.data
|
|
113
|
+
retainExecText(retained, 'data', arguments_.data);
|
|
126
114
|
return retained;
|
|
127
115
|
}
|
|
128
|
-
export function contextGcExecOutputReplacement(result
|
|
116
|
+
export function contextGcExecOutputReplacement(result) {
|
|
129
117
|
if (result.source === 'MODEL_CONTEXT_TRUNCATED')
|
|
130
118
|
return JSON.stringify({
|
|
131
119
|
status: result.status,
|
|
@@ -139,8 +127,7 @@ export function contextGcExecOutputReplacement(result, formatVersion = 3) {
|
|
|
139
127
|
modelContextTruncated: true,
|
|
140
128
|
modelContextBytes: Buffer.byteLength(result.modelContextContent),
|
|
141
129
|
modelContextPreview: unicodeHeadTail(result.modelContextContent, RAW_MODEL_CONTEXT_PREVIEW_CHARACTERS),
|
|
142
|
-
|
|
143
|
-
...(formatVersion <= 2 ? { modelContextSha256: sha256(result.modelContextContent) } : {}),
|
|
130
|
+
contextGcFormatVersion: 3,
|
|
144
131
|
contextGc: 'The model-visible terminal result was truncated after execution and is no longer valid JSON. Detailed terminal input/output was reclaimed after the process reached a stable terminal state. Re-run the command if full output is required.'
|
|
145
132
|
});
|
|
146
133
|
const stdout = result.stdout;
|
|
@@ -156,32 +143,20 @@ export function contextGcExecOutputReplacement(result, formatVersion = 3) {
|
|
|
156
143
|
stderrBytes: nonnegativeNumber(result.stderrBytes) ?? Buffer.byteLength(stderr),
|
|
157
144
|
stdoutDroppedBytes: nonnegativeNumber(result.stdoutDroppedBytes) ?? 0,
|
|
158
145
|
stderrDroppedBytes: nonnegativeNumber(result.stderrDroppedBytes) ?? 0,
|
|
159
|
-
...(formatVersion <= 2
|
|
160
|
-
? {
|
|
161
|
-
stdoutSha256: sha256(stdout),
|
|
162
|
-
stderrSha256: sha256(stderr)
|
|
163
|
-
}
|
|
164
|
-
: {}),
|
|
165
146
|
stdoutPreview,
|
|
166
147
|
stderrPreview,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
contextGcFormatVersion: formatVersion
|
|
180
|
-
}
|
|
181
|
-
: {}),
|
|
182
|
-
contextGc: formatVersion >= 2
|
|
183
|
-
? 'Detailed terminal input/output was reclaimed after the process reached a stable terminal state. An incomplete preview is not the complete result. Re-run the command if full output is required.'
|
|
184
|
-
: 'Detailed terminal input/output was reclaimed after the process reached a stable terminal state. Re-run the command if full output is required.'
|
|
148
|
+
stdoutLines: lineCount(stdout),
|
|
149
|
+
stderrLines: lineCount(stderr),
|
|
150
|
+
stdoutPreviewComplete: result.truncated === false &&
|
|
151
|
+
result.stdoutDroppedBytes === 0 &&
|
|
152
|
+
result.stdoutBytes === Buffer.byteLength(stdout) &&
|
|
153
|
+
stdoutPreview === stdout,
|
|
154
|
+
stderrPreviewComplete: result.truncated === false &&
|
|
155
|
+
result.stderrDroppedBytes === 0 &&
|
|
156
|
+
result.stderrBytes === Buffer.byteLength(stderr) &&
|
|
157
|
+
stderrPreview === stderr,
|
|
158
|
+
contextGcFormatVersion: 3,
|
|
159
|
+
contextGc: 'Detailed terminal input/output was reclaimed after the process reached a stable terminal state. An incomplete preview is not the complete result. Re-run the command if full output is required.'
|
|
185
160
|
});
|
|
186
161
|
}
|
|
187
162
|
export function normalizeContextGcExecResult(modelContent, completedPayload) {
|
|
@@ -277,19 +252,13 @@ function unicodeHeadTail(value, count) {
|
|
|
277
252
|
.slice(-count)
|
|
278
253
|
.join('')}`;
|
|
279
254
|
}
|
|
280
|
-
function retainExecText(retained, key, value
|
|
281
|
-
if (formatVersion === 1) {
|
|
282
|
-
retained[`${key}Prefix`] = [...value].slice(0, EXEC_INPUT_PREVIEW_CHARACTERS).join('');
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
255
|
+
function retainExecText(retained, key, value) {
|
|
285
256
|
if ([...value].length <= EXEC_INPUT_COMPLETE_CHARACTERS) {
|
|
286
257
|
retained[key] = value;
|
|
287
258
|
return;
|
|
288
259
|
}
|
|
289
260
|
retained[`${key}Preview`] = unicodeHeadTail(value, EXEC_INPUT_PREVIEW_CHARACTERS);
|
|
290
261
|
retained[`${key}Bytes`] = Buffer.byteLength(value);
|
|
291
|
-
if (formatVersion === 2)
|
|
292
|
-
retained[`${key}Sha256`] = sha256(value);
|
|
293
262
|
}
|
|
294
263
|
function lineCount(value) {
|
|
295
264
|
return value.length === 0 ? 0 : value.split('\n').length;
|
|
@@ -297,9 +266,6 @@ function lineCount(value) {
|
|
|
297
266
|
function nonnegativeNumber(value) {
|
|
298
267
|
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
299
268
|
}
|
|
300
|
-
function sha256(value) {
|
|
301
|
-
return createHash('sha256').update(value).digest('hex');
|
|
302
|
-
}
|
|
303
269
|
function stableJson(value) {
|
|
304
270
|
if (Array.isArray(value))
|
|
305
271
|
return `[${value.map(stableJson).join(',')}]`;
|
|
@@ -50,7 +50,4 @@ export declare function contextGcSubagentOutputReplacement(input: {
|
|
|
50
50
|
readonly sourceContent: string;
|
|
51
51
|
readonly source: ContextGcSubagentSnapshot;
|
|
52
52
|
readonly terminal: ContextGcSubagentSnapshot;
|
|
53
|
-
readonly terminalCallId: string;
|
|
54
|
-
readonly evidenceSequence: number;
|
|
55
|
-
readonly formatVersion?: 1 | 2;
|
|
56
53
|
}): string;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
1
|
export function normalizeContextGcSubagentSnapshot(modelContent, completedPayload, toolName) {
|
|
3
2
|
if (typeof modelContent !== 'string' ||
|
|
4
3
|
!isRecord(completedPayload) ||
|
|
@@ -92,7 +91,6 @@ export function contextGcSubagentSnapshotIsPreciselyCovered(source, retained) {
|
|
|
92
91
|
}
|
|
93
92
|
export function contextGcSubagentOutputReplacement(input) {
|
|
94
93
|
const latestMessage = input.source.latestMessage;
|
|
95
|
-
const formatVersion = input.formatVersion ?? 2;
|
|
96
94
|
return JSON.stringify({
|
|
97
95
|
agentId: input.source.agentId,
|
|
98
96
|
description: input.source.description,
|
|
@@ -115,12 +113,10 @@ export function contextGcSubagentOutputReplacement(input) {
|
|
|
115
113
|
turnId: latestMessage.turnId,
|
|
116
114
|
phase: latestMessage.phase,
|
|
117
115
|
timestamp: latestMessage.timestamp,
|
|
118
|
-
...(formatVersion === 1 ? { itemId: latestMessage.itemId } : {}),
|
|
119
116
|
...(latestMessage.truncated === undefined
|
|
120
117
|
? {}
|
|
121
118
|
: { truncated: latestMessage.truncated }),
|
|
122
|
-
textBytes: Buffer.byteLength(latestMessage.text)
|
|
123
|
-
...(formatVersion === 1 ? { textSha256: sha256(latestMessage.text) } : {})
|
|
119
|
+
textBytes: Buffer.byteLength(latestMessage.text)
|
|
124
120
|
}
|
|
125
121
|
}),
|
|
126
122
|
...(input.source.latestActivity === undefined
|
|
@@ -128,18 +124,11 @@ export function contextGcSubagentOutputReplacement(input) {
|
|
|
128
124
|
: { latestActivity: input.source.latestActivity }),
|
|
129
125
|
contextGc: {
|
|
130
126
|
kind: 'superseded_subagent_snapshot',
|
|
131
|
-
|
|
127
|
+
formatVersion: 2,
|
|
132
128
|
originalBytes: Buffer.byteLength(input.sourceContent),
|
|
133
|
-
...(formatVersion === 1 ? { originalSha256: sha256(input.sourceContent) } : {}),
|
|
134
129
|
terminalStatus: input.terminal.status,
|
|
135
130
|
terminalTurnId: contextGcCompletedSubagentTurnId(input.terminal) ??
|
|
136
131
|
contextGcSubagentSnapshotTurnId(input.terminal),
|
|
137
|
-
...(formatVersion === 1
|
|
138
|
-
? {
|
|
139
|
-
terminalCallId: input.terminalCallId,
|
|
140
|
-
evidenceSequence: input.evidenceSequence
|
|
141
|
-
}
|
|
142
|
-
: {}),
|
|
143
132
|
note: input.terminal.status === 'completed'
|
|
144
133
|
? 'This intermediate child-agent snapshot was superseded by retained completed evidence; complete Turn results remain in retained completed snapshots.'
|
|
145
134
|
: 'This duplicate child-agent snapshot was superseded by a retained non-completed terminal snapshot. Only fields exactly covered by the retained snapshot were reclaimed.'
|
|
@@ -327,9 +316,6 @@ function evidenceIsCovered(source, terminal) {
|
|
|
327
316
|
return (source === undefined ||
|
|
328
317
|
source.every((item) => terminal?.some((candidate) => stableJson(candidate) === stableJson(item))));
|
|
329
318
|
}
|
|
330
|
-
function sha256(value) {
|
|
331
|
-
return createHash('sha256').update(value).digest('hex');
|
|
332
|
-
}
|
|
333
319
|
function stableJson(value) {
|
|
334
320
|
if (Array.isArray(value))
|
|
335
321
|
return `[${value.map(stableJson).join(',')}]`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SessionRecord } from './
|
|
1
|
+
import type { SessionRecord } from './store/index.js';
|
|
2
2
|
/** The application is self-contained; the receive record may precede the Compact window. */
|
|
3
3
|
export declare function appliedInputRecord(record: SessionRecord): SessionRecord | undefined;
|
|
4
4
|
/** Only the last checkpoint and its tail are needed, never a historical consume index. */
|