@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,9 +1,12 @@
|
|
|
1
1
|
import { posix, win32 } from 'node:path';
|
|
2
2
|
import { isDeepStrictEqual } from 'node:util';
|
|
3
3
|
import { inlineMediaGcAnchored, parseInlineMediaGc } from './inline-media.js';
|
|
4
|
+
import { buildContextGcProtection } from './context-gc-protection.js';
|
|
5
|
+
import { completedTodoWrite, todoInputReplacement, todoSnapshotReplacement, visibleTodoSnapshot } from './context-gc-todo.js';
|
|
6
|
+
import { contextGcRetainedInputMarkup } from './context-gc-markup.js';
|
|
4
7
|
import { codeCallEvidence, codePartialPatchCallEvidence, callsFromRecord, codeObservationEvidence, observationsFromRecord, retainedCodeInputs } from './context-gc-observations.js';
|
|
5
8
|
import { parseReadContextOutput } from '../prompts/read-context.js';
|
|
6
|
-
import { codeCellCompletionSequence, contextGcApplyPatchInputReplacement, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcPatchResultFromFailedPayload, contextGcStaleReadReplacement, contextGcToolOutputReplacement, contextGcWebOutputReplacement, isGenericContextGcTool, normalizeContextGcCodeModeInput, normalizeContextGcExecResult } from './context-gc-replacements.js';
|
|
9
|
+
import { codeCellCompletionSequence, contextGcApplyPatchInputReplacement, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcPatchResultFromFailedPayload, contextGcStaleReadReplacement, contextGcReadRangeCovers, contextGcCoveredReadReplacement, contextGcSkillOutputReplacement, contextGcToolOutputReplacement, contextGcWebOutputReplacement, isGenericContextGcTool, normalizeContextGcCodeModeInput, normalizeContextGcExecResult } from './context-gc-replacements.js';
|
|
7
10
|
import { contextGcSubagentNeedsRetainedPriorFinal, contextGcSubagentOutputReplacement, contextGcSubagentSnapshotIsPreciselyCovered, contextGcSubagentSnapshotIsSuperseded, normalizeContextGcSubagentSnapshot } from './context-gc-subagent.js';
|
|
8
11
|
export const CONTEXT_GC_CHECKPOINT_VERSION = 1;
|
|
9
12
|
export const CONTEXT_GC_MAX_TARGETS = 4_096;
|
|
@@ -188,6 +191,7 @@ export function validateContextGcCheckpoint(records, record, workspace = session
|
|
|
188
191
|
]);
|
|
189
192
|
if (new Set(targets.map(contextGcTargetKey)).size !== targets.length)
|
|
190
193
|
return undefined;
|
|
194
|
+
const protection = buildContextGcProtection(records, payload.trigger.modelId, payload.trigger.contextWindowTokens);
|
|
191
195
|
for (const replacement of changed) {
|
|
192
196
|
for (const target of [
|
|
193
197
|
replacement.target,
|
|
@@ -211,7 +215,34 @@ export function validateContextGcCheckpoint(records, record, workspace = session
|
|
|
211
215
|
sequence > payload.boundarySequence ||
|
|
212
216
|
!recordsBySequence.has(sequence)))
|
|
213
217
|
return undefined;
|
|
214
|
-
|
|
218
|
+
const anchors = replacement.kind === 'READ_OUTPUT_ITEMS'
|
|
219
|
+
? replacement.items.map((item) => Math.max(replacement.target.sequence, item.evidenceSequence))
|
|
220
|
+
: [Math.max(replacement.target.sequence, ...evidence)];
|
|
221
|
+
if (replacement.kind === 'TOOL_CALL_INPUT' && replacement.toolName === 'todo_write') {
|
|
222
|
+
const terminal = records.find((item) => completedTodoWrite(item)?.callId === replacement.callId);
|
|
223
|
+
if (terminal)
|
|
224
|
+
anchors[0] = Math.max(anchors[0], terminal.sequence);
|
|
225
|
+
}
|
|
226
|
+
if ((replacement.kind === 'TOOL_CALL_INPUT' && replacement.toolName === 'code_exec') ||
|
|
227
|
+
(replacement.kind === 'CODE_MODE_OUTPUT' && !replacement.target.observationId)) {
|
|
228
|
+
const result = recordsBySequence.get(replacement.evidenceSequence);
|
|
229
|
+
if (result && isRecord(result.payload) && typeof result.payload.callId === 'string') {
|
|
230
|
+
const callId = result.payload.callId;
|
|
231
|
+
const terminal = records.findLast((item) => item.sequence < result.sequence &&
|
|
232
|
+
item.type === 'tool.completed' &&
|
|
233
|
+
isRecord(item.payload) &&
|
|
234
|
+
item.payload.callId === callId);
|
|
235
|
+
const completion = terminal && codeCellCompletionSequence(records, terminal);
|
|
236
|
+
if (completion !== undefined)
|
|
237
|
+
anchors[0] = Math.max(anchors[0], completion);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (anchors.some((anchor) => {
|
|
241
|
+
const count = protection.after(anchor);
|
|
242
|
+
return count.rollouts < 6 || count.thinking < 3;
|
|
243
|
+
}))
|
|
244
|
+
return undefined;
|
|
245
|
+
if (!replacementMatchesRecords(replacement, records, recordsBySequence, workspace, payload.trigger.platform, payload.replacements, protection, committed.get(contextGcReplacementKey(replacement))))
|
|
215
246
|
return undefined;
|
|
216
247
|
}
|
|
217
248
|
return payload;
|
|
@@ -260,8 +291,18 @@ function targetMatchesRecord(target, record) {
|
|
|
260
291
|
true) &&
|
|
261
292
|
(target.toolName === undefined || target.toolName === messageToolName));
|
|
262
293
|
}
|
|
294
|
+
function resultVisibleAfter(source, observationId, successor) {
|
|
295
|
+
if (successor.sequence !== source.sequence)
|
|
296
|
+
return successor.sequence > source.sequence;
|
|
297
|
+
if (!isRecord(successor.payload))
|
|
298
|
+
return false;
|
|
299
|
+
const observations = observationsFromRecord(source) ?? [];
|
|
300
|
+
const sourceIndex = observations.findIndex((value) => value.id === observationId);
|
|
301
|
+
const successorId = successor.payload.observationId;
|
|
302
|
+
return (sourceIndex >= 0 && observations.findIndex((value) => value.id === successorId) > sourceIndex);
|
|
303
|
+
}
|
|
263
304
|
/** A checkpoint draft is valid only when its replacements match the source records and evidence. */
|
|
264
|
-
function replacementMatchesRecords(replacement, records, recordsBySequence, workspace, platform) {
|
|
305
|
+
function replacementMatchesRecords(replacement, records, recordsBySequence, workspace, platform, allReplacements, protection, previous) {
|
|
265
306
|
let source = recordsBySequence.get(replacement.target.sequence);
|
|
266
307
|
if (!source || !isRecord(source.payload))
|
|
267
308
|
return false;
|
|
@@ -286,7 +327,6 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
286
327
|
}
|
|
287
328
|
else
|
|
288
329
|
source = { ...source, payload: { ...source.payload, content: selected.value } };
|
|
289
|
-
recordsBySequence = new Map(recordsBySequence).set(source.sequence, source);
|
|
290
330
|
}
|
|
291
331
|
if (!isRecord(source.payload))
|
|
292
332
|
return false;
|
|
@@ -302,6 +342,30 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
302
342
|
return replacement.items.every((item) => {
|
|
303
343
|
const original = originalItems.get(item.index);
|
|
304
344
|
const evidence = appliedPatchEvidenceForPath(records, recordsBySequence, item.evidenceSequence, item.path, workspace, platform, sourceToolSequence, item.supersededByCallId);
|
|
345
|
+
if (original && !evidence && item.supersededByCallId) {
|
|
346
|
+
const later = successfulToolEvidence(records, recordsBySequence, item.evidenceSequence, 'read', item.supersededByCallId);
|
|
347
|
+
if (!later ||
|
|
348
|
+
!resultVisibleAfter(source, replacement.target.observationId, later.result) ||
|
|
349
|
+
!isRecord(later.result.payload) ||
|
|
350
|
+
typeof later.result.payload.content !== 'string')
|
|
351
|
+
return false;
|
|
352
|
+
const laterItems = parseReadContextOutput(later.result.payload.content)?.items ?? [];
|
|
353
|
+
const pathKey = contextGcLogicalPathKey(original.path, workspace, platform);
|
|
354
|
+
const observationId = typeof later.result.payload.observationId === 'string'
|
|
355
|
+
? later.result.payload.observationId
|
|
356
|
+
: undefined;
|
|
357
|
+
const laterRecord = recordsBySequence.get(item.evidenceSequence);
|
|
358
|
+
if (laterRecord && observationsFromRecord(laterRecord) !== undefined && !observationId)
|
|
359
|
+
return false;
|
|
360
|
+
const covered = laterItems.some((next) => pathKey !== undefined &&
|
|
361
|
+
contextGcLogicalPathKey(next.path, workspace, platform) === pathKey &&
|
|
362
|
+
contextGcReadRangeCovers(original, next) &&
|
|
363
|
+
!allReplacements.some((prior) => prior.kind === 'READ_OUTPUT_ITEMS' &&
|
|
364
|
+
prior.target.sequence === later.result.sequence &&
|
|
365
|
+
prior.target.observationId === observationId &&
|
|
366
|
+
prior.items.some((existing) => existing.index === next.index)));
|
|
367
|
+
return (covered && item.replacement === contextGcCoveredReadReplacement(original, later.callId));
|
|
368
|
+
}
|
|
305
369
|
return (original?.pathTruncated === false &&
|
|
306
370
|
original?.path === item.path &&
|
|
307
371
|
evidence !== undefined &&
|
|
@@ -321,6 +385,19 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
321
385
|
result !== undefined &&
|
|
322
386
|
isDeepStrictEqual(replacement.replacementArguments, contextGcApplyPatchInputReplacement(source.payload.arguments, result)));
|
|
323
387
|
}
|
|
388
|
+
if (replacement.kind === 'TOOL_CALL_INPUT' && replacement.toolName === 'todo_write') {
|
|
389
|
+
const own = records.find((item) => completedTodoWrite(item)?.callId === replacement.callId);
|
|
390
|
+
const original = own && completedTodoWrite(own);
|
|
391
|
+
const successorRecord = recordsBySequence.get(replacement.evidenceSequence);
|
|
392
|
+
const successor = successorRecord && completedTodoWrite(successorRecord);
|
|
393
|
+
const args = source.payload.arguments;
|
|
394
|
+
return (original !== undefined &&
|
|
395
|
+
successor !== undefined &&
|
|
396
|
+
successor.revision > original.revision &&
|
|
397
|
+
isRecord(args) &&
|
|
398
|
+
Array.isArray(args.items) &&
|
|
399
|
+
isDeepStrictEqual(replacement.replacementArguments, todoInputReplacement(original.revision, args.items.length, successor.revision)));
|
|
400
|
+
}
|
|
324
401
|
if (replacement.kind === 'TOOL_CALL_INPUT' && replacement.toolName === 'code_exec') {
|
|
325
402
|
const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, replacement.toolName);
|
|
326
403
|
const input = normalizeContextGcCodeModeInput(source.payload.arguments);
|
|
@@ -329,10 +406,38 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
329
406
|
evidence.call.sequence === replacement.target.sequence &&
|
|
330
407
|
input !== undefined &&
|
|
331
408
|
isDeepStrictEqual(replacement.replacementArguments, contextGcCodeModeInputReplacement(input, observationsFromRecord(evidence.result)
|
|
332
|
-
? retainedCodeInputs(records, replacement.callId)
|
|
409
|
+
? retainedCodeInputs(records, replacement.callId, (original, successor) => {
|
|
410
|
+
const count = protection.after(Math.max(replacement.target.sequence, replacement.evidenceSequence, codeCellCompletionSequence(records, evidence.completed) ?? 0, original.sequence, successor.sequence));
|
|
411
|
+
return count.rollouts >= 6 && count.thinking >= 3;
|
|
412
|
+
}, {
|
|
413
|
+
activeProcessIds: new Set(),
|
|
414
|
+
evidenceMature: (sequence) => {
|
|
415
|
+
const count = protection.after(Math.max(replacement.target.sequence, replacement.evidenceSequence, codeCellCompletionSequence(records, evidence.completed) ?? 0, sequence));
|
|
416
|
+
return count.rollouts >= 6 && count.thinking >= 3;
|
|
417
|
+
},
|
|
418
|
+
selected: (callId, toolName, summary) => {
|
|
419
|
+
const argument = replacement.replacementArguments;
|
|
420
|
+
const content = typeof argument === 'string'
|
|
421
|
+
? argument
|
|
422
|
+
: isRecord(argument)
|
|
423
|
+
? argument.code
|
|
424
|
+
: undefined;
|
|
425
|
+
return (typeof content === 'string' &&
|
|
426
|
+
content.includes(contextGcRetainedInputMarkup({ callId, toolName, input: summary })));
|
|
427
|
+
}
|
|
428
|
+
})
|
|
333
429
|
: [])));
|
|
334
430
|
}
|
|
335
431
|
if (replacement.kind === 'CODE_MODE_OUTPUT') {
|
|
432
|
+
if (replacement.target.observationId) {
|
|
433
|
+
const observation = observationsFromRecord(source)?.find((value) => value.id === replacement.target.observationId && value.type === 'value');
|
|
434
|
+
return (observation?.type === 'value' &&
|
|
435
|
+
source.payload.callId === replacement.callId &&
|
|
436
|
+
replacement.evidenceSequence === source.sequence &&
|
|
437
|
+
['code_exec', 'code_exec_wait'].includes(toolNameForCallId(records, replacement.callId, source.sequence) ?? '') &&
|
|
438
|
+
replacement.replacementContent ===
|
|
439
|
+
contextGcCodeModeOutputReplacement(observation.value, observation.truncated === true, true));
|
|
440
|
+
}
|
|
336
441
|
const evidence = ['code_exec', 'code_exec_wait']
|
|
337
442
|
.map((name) => successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, name))
|
|
338
443
|
.find((item) => item !== undefined);
|
|
@@ -344,13 +449,112 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
344
449
|
isRecord(evidence.completed.payload) &&
|
|
345
450
|
typeof evidence.completed.payload.truncated === 'boolean' &&
|
|
346
451
|
replacement.replacementContent ===
|
|
347
|
-
contextGcCodeModeOutputReplacement(source.payload.content,
|
|
348
|
-
? observationsFromRecord(source)?.some((value) => value.id === replacement.target.observationId &&
|
|
349
|
-
value.type === 'value' &&
|
|
350
|
-
value.truncated === true) === true
|
|
351
|
-
: evidence.completed.payload.truncated));
|
|
452
|
+
contextGcCodeModeOutputReplacement(source.payload.content, evidence.completed.payload.truncated, false));
|
|
352
453
|
}
|
|
353
454
|
if (replacement.kind === 'TOOL_OUTPUT') {
|
|
455
|
+
if (replacement.toolName === 'tool_search') {
|
|
456
|
+
const original = successfulToolEvidence(records, recordsBySequence, source.sequence, 'tool_search', replacement.callId);
|
|
457
|
+
if (!original ||
|
|
458
|
+
!isRecord(source.payload) ||
|
|
459
|
+
!Array.isArray(source.payload.toolSearchTools) ||
|
|
460
|
+
replacement.retainedToolNames === undefined ||
|
|
461
|
+
replacement.replacementContent !== source.payload.content)
|
|
462
|
+
return false;
|
|
463
|
+
const sourceNames = source.payload.toolSearchTools.flatMap((tool) => isRecord(tool) && typeof tool.name === 'string' ? [tool.name] : []);
|
|
464
|
+
const priorNames = previous?.kind === 'TOOL_OUTPUT' && previous.retainedToolNames !== undefined
|
|
465
|
+
? previous.retainedToolNames
|
|
466
|
+
: sourceNames;
|
|
467
|
+
if (replacement.retainedToolNames.some((name) => !priorNames.includes(name)) ||
|
|
468
|
+
!isDeepStrictEqual(replacement.retainedToolNames, priorNames.filter((name) => replacement.retainedToolNames.includes(name))))
|
|
469
|
+
return false;
|
|
470
|
+
const removed = priorNames.filter((name) => !replacement.retainedToolNames.includes(name));
|
|
471
|
+
if (removed.length === 0)
|
|
472
|
+
return false;
|
|
473
|
+
const successors = records.filter((item) => item.sequence > source.sequence &&
|
|
474
|
+
item.sequence <= replacement.evidenceSequence &&
|
|
475
|
+
item.type === 'model.context' &&
|
|
476
|
+
isRecord(item.payload) &&
|
|
477
|
+
item.payload.role === 'tool' &&
|
|
478
|
+
typeof item.payload.callId === 'string' &&
|
|
479
|
+
Array.isArray(item.payload.toolSearchTools) &&
|
|
480
|
+
successfulToolEvidence(records, recordsBySequence, item.sequence, 'tool_search', item.payload.callId) !== undefined);
|
|
481
|
+
return (removed.every((name) => successors.some((item) => {
|
|
482
|
+
if (!isRecord(item.payload) || !Array.isArray(item.payload.toolSearchTools))
|
|
483
|
+
return false;
|
|
484
|
+
const target = {
|
|
485
|
+
sequence: item.sequence,
|
|
486
|
+
recordId: item.recordId,
|
|
487
|
+
role: 'tool',
|
|
488
|
+
...(typeof item.payload.callId === 'string' ? { callId: item.payload.callId } : {})
|
|
489
|
+
};
|
|
490
|
+
const applied = allReplacements.find((candidate) => contextGcTargetKey(candidate.target) === contextGcTargetKey(target));
|
|
491
|
+
return (item.payload.toolSearchTools.some((tool) => isRecord(tool) && tool.name === name) &&
|
|
492
|
+
!(applied?.kind === 'TOOL_OUTPUT' &&
|
|
493
|
+
applied.retainedToolNames !== undefined &&
|
|
494
|
+
!applied.retainedToolNames.includes(name)));
|
|
495
|
+
})) && successors.some((item) => item.sequence === replacement.evidenceSequence));
|
|
496
|
+
}
|
|
497
|
+
if (replacement.toolName === 'todo_read') {
|
|
498
|
+
const original = successfulToolEvidence(records, recordsBySequence, source.sequence, 'todo_read', replacement.callId);
|
|
499
|
+
const successorRecord = recordsBySequence.get(replacement.evidenceSequence);
|
|
500
|
+
const successor = successorRecord && completedTodoWrite(successorRecord);
|
|
501
|
+
const snapshot = typeof source.payload.content === 'string'
|
|
502
|
+
? visibleTodoSnapshot(source.payload.content)
|
|
503
|
+
: undefined;
|
|
504
|
+
return (original !== undefined &&
|
|
505
|
+
snapshot !== undefined &&
|
|
506
|
+
successor !== undefined &&
|
|
507
|
+
successor.revision > snapshot.revision &&
|
|
508
|
+
replacement.replacementContent ===
|
|
509
|
+
todoSnapshotReplacement(snapshot.revision, snapshot.count, successor.revision));
|
|
510
|
+
}
|
|
511
|
+
if (replacement.toolName === 'skill') {
|
|
512
|
+
const original = successfulToolEvidence(records, recordsBySequence, source.sequence, 'skill', replacement.callId);
|
|
513
|
+
let successorCallId;
|
|
514
|
+
try {
|
|
515
|
+
successorCallId = JSON.parse(replacement.replacementContent).supersededByCallId;
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
if (typeof successorCallId !== 'string')
|
|
521
|
+
return false;
|
|
522
|
+
const successor = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, 'skill', successorCallId);
|
|
523
|
+
const successorRecord = recordsBySequence.get(replacement.evidenceSequence);
|
|
524
|
+
const successorObservationId = isRecord(successor?.result.payload)
|
|
525
|
+
? successor.result.payload.observationId
|
|
526
|
+
: undefined;
|
|
527
|
+
if (!successorRecord ||
|
|
528
|
+
!isRecord(successorRecord.payload) ||
|
|
529
|
+
(observationsFromRecord(successorRecord) !== undefined &&
|
|
530
|
+
typeof successorObservationId !== 'string'))
|
|
531
|
+
return false;
|
|
532
|
+
const successorKey = contextGcTargetKey({
|
|
533
|
+
sequence: successorRecord.sequence,
|
|
534
|
+
recordId: successorRecord.recordId,
|
|
535
|
+
role: 'tool',
|
|
536
|
+
...(typeof successorRecord.payload.callId === 'string'
|
|
537
|
+
? { callId: successorRecord.payload.callId }
|
|
538
|
+
: {}),
|
|
539
|
+
...(typeof successorObservationId === 'string'
|
|
540
|
+
? { observationId: successorObservationId }
|
|
541
|
+
: {})
|
|
542
|
+
});
|
|
543
|
+
if (!original ||
|
|
544
|
+
!successor ||
|
|
545
|
+
!resultVisibleAfter(source, replacement.target.observationId, successor.result) ||
|
|
546
|
+
!isRecord(original.call.payload) ||
|
|
547
|
+
!isRecord(successor.call.payload) ||
|
|
548
|
+
!isRecord(original.call.payload.arguments) ||
|
|
549
|
+
!isRecord(successor.call.payload.arguments) ||
|
|
550
|
+
typeof original.call.payload.arguments.name !== 'string' ||
|
|
551
|
+
original.call.payload.arguments.name !== successor.call.payload.arguments.name ||
|
|
552
|
+
typeof source.payload.content !== 'string' ||
|
|
553
|
+
allReplacements.some((item) => contextGcTargetKey(item.target) === successorKey))
|
|
554
|
+
return false;
|
|
555
|
+
return (replacement.replacementContent ===
|
|
556
|
+
contextGcSkillOutputReplacement(original.call.payload.arguments.name, source.payload.content, successor.callId));
|
|
557
|
+
}
|
|
354
558
|
const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, replacement.toolName, replacement.callId);
|
|
355
559
|
return (isGenericContextGcTool(replacement.toolName) &&
|
|
356
560
|
evidence !== undefined &&
|
|
@@ -359,8 +563,6 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
359
563
|
isRecord(evidence.completed.payload) &&
|
|
360
564
|
typeof evidence.completed.payload.truncated === 'boolean' &&
|
|
361
565
|
typeof source.payload.content === 'string' &&
|
|
362
|
-
isRecord(evidence.result.payload) &&
|
|
363
|
-
evidence.result.payload.content === source.payload.content &&
|
|
364
566
|
replacement.replacementContent ===
|
|
365
567
|
contextGcToolOutputReplacement(replacement.toolName, source.payload.content, evidence.completed.payload.truncated));
|
|
366
568
|
}
|
|
@@ -447,7 +649,9 @@ function successfulToolEvidence(records, recordsBySequence, evidenceSequence, to
|
|
|
447
649
|
!isRecord(call.payload) ||
|
|
448
650
|
call.payload.name !== toolName ||
|
|
449
651
|
(expectedCallId && expectedCallId !== callId)) {
|
|
450
|
-
|
|
652
|
+
const observations = observationsFromRecord(result) ?? [];
|
|
653
|
+
// Repeated observations of a known call use its last visible occurrence.
|
|
654
|
+
for (const observation of expectedCallId ? [...observations].reverse() : observations) {
|
|
451
655
|
if (observation.type !== 'tool_result' ||
|
|
452
656
|
observation.source.toolName !== toolName ||
|
|
453
657
|
(expectedCallId && observation.source.callId !== expectedCallId))
|
|
@@ -766,7 +970,8 @@ function isContextGcReplacement(value) {
|
|
|
766
970
|
(value.toolName === 'apply_patch' ||
|
|
767
971
|
value.toolName === 'exec' ||
|
|
768
972
|
value.toolName === 'exec_wait' ||
|
|
769
|
-
value.toolName === 'code_exec'
|
|
973
|
+
value.toolName === 'code_exec' ||
|
|
974
|
+
value.toolName === 'todo_write') &&
|
|
770
975
|
value.target.toolName === value.toolName &&
|
|
771
976
|
Array.isArray(value.providerAliasTargets) &&
|
|
772
977
|
value.providerAliasTargets.every((target) => isContextGcTarget(target) &&
|
|
@@ -829,13 +1034,23 @@ function isContextGcReplacement(value) {
|
|
|
829
1034
|
'callId',
|
|
830
1035
|
'target',
|
|
831
1036
|
'replacementContent',
|
|
1037
|
+
'retainedToolNames',
|
|
832
1038
|
'evidenceSequence'
|
|
833
1039
|
]) &&
|
|
834
1040
|
value.target.role === 'tool' &&
|
|
835
1041
|
isNonemptyString(value.toolName) &&
|
|
836
|
-
|
|
1042
|
+
(value.toolName === 'skill' ||
|
|
1043
|
+
value.toolName === 'todo_read' ||
|
|
1044
|
+
value.toolName === 'tool_search' ||
|
|
1045
|
+
isGenericContextGcTool(value.toolName)) &&
|
|
837
1046
|
(value.target.toolName === undefined || value.target.toolName === value.toolName) &&
|
|
838
|
-
isNonemptyString(value.replacementContent)
|
|
1047
|
+
(isNonemptyString(value.replacementContent) ||
|
|
1048
|
+
(value.toolName === 'tool_search' && value.replacementContent === '')) &&
|
|
1049
|
+
(value.retainedToolNames === undefined ||
|
|
1050
|
+
(value.toolName === 'tool_search' &&
|
|
1051
|
+
Array.isArray(value.retainedToolNames) &&
|
|
1052
|
+
value.retainedToolNames.every(isNonemptyString) &&
|
|
1053
|
+
new Set(value.retainedToolNames).size === value.retainedToolNames.length)) &&
|
|
839
1054
|
isPositiveInteger(value.evidenceSequence));
|
|
840
1055
|
if (value.kind === 'CODE_MODE_OUTPUT')
|
|
841
1056
|
return (hasOnlyKeys(value, ['kind', 'callId', 'target', 'replacementContent', 'evidenceSequence']) &&
|
|
@@ -906,7 +1121,8 @@ function isContextGcEconomicsPolicy(value) {
|
|
|
906
1121
|
}
|
|
907
1122
|
function isContextGcMetrics(value) {
|
|
908
1123
|
if (!isRecord(value) ||
|
|
909
|
-
(value.stage !== '
|
|
1124
|
+
(value.stage !== 'UNIFIED' &&
|
|
1125
|
+
value.stage !== 'STAGE_A' &&
|
|
910
1126
|
value.stage !== 'STAGE_A_AND_EXEC' &&
|
|
911
1127
|
value.stage !== 'INLINE_MEDIA'))
|
|
912
1128
|
return false;
|
|
@@ -49,7 +49,7 @@ export type ContextGcReplacement = {
|
|
|
49
49
|
readonly evidenceSequences: readonly number[];
|
|
50
50
|
} | {
|
|
51
51
|
readonly kind: 'TOOL_CALL_INPUT';
|
|
52
|
-
readonly toolName: 'apply_patch' | 'exec' | 'exec_wait' | 'code_exec';
|
|
52
|
+
readonly toolName: 'apply_patch' | 'exec' | 'exec_wait' | 'code_exec' | 'todo_write';
|
|
53
53
|
readonly callId: string;
|
|
54
54
|
readonly target: ContextGcTarget;
|
|
55
55
|
readonly replacementArguments: unknown;
|
|
@@ -74,6 +74,7 @@ export type ContextGcReplacement = {
|
|
|
74
74
|
readonly callId: string;
|
|
75
75
|
readonly target: ContextGcTarget;
|
|
76
76
|
readonly replacementContent: string;
|
|
77
|
+
readonly retainedToolNames?: readonly string[];
|
|
77
78
|
readonly evidenceSequence: number;
|
|
78
79
|
} | {
|
|
79
80
|
readonly kind: 'SUBAGENT_OUTPUT';
|
|
@@ -98,7 +99,7 @@ export type ContextGcReplacement = {
|
|
|
98
99
|
readonly evidenceSequence: number;
|
|
99
100
|
};
|
|
100
101
|
export interface ContextGcMetrics {
|
|
101
|
-
readonly stage: 'STAGE_A' | 'STAGE_A_AND_EXEC' | 'INLINE_MEDIA';
|
|
102
|
+
readonly stage: 'UNIFIED' | 'STAGE_A' | 'STAGE_A_AND_EXEC' | 'INLINE_MEDIA';
|
|
102
103
|
readonly tokenEstimator?: 'UTF8_BYTES_V1';
|
|
103
104
|
readonly beforeTokens: number;
|
|
104
105
|
readonly afterTokens: number;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContextGcExecResult } from './context-gc-replacements.js';
|
|
2
|
+
/** The same process-lifetime selection is used for Direct output and retained cell inputs. */
|
|
3
|
+
export interface ContextGcExecFact<T> {
|
|
4
|
+
readonly value: T;
|
|
5
|
+
readonly callId: string;
|
|
6
|
+
readonly toolName: string;
|
|
7
|
+
readonly sequence: number;
|
|
8
|
+
readonly input?: Record<string, unknown>;
|
|
9
|
+
readonly result?: ContextGcExecResult;
|
|
10
|
+
}
|
|
11
|
+
export declare function terminalExecLifecycles<T>(facts: readonly ContextGcExecFact<T>[], active: ReadonlySet<string>): ContextGcExecFact<T>[][];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function terminalExecLifecycles(facts, active) {
|
|
2
|
+
const ordered = [...facts].sort((a, b) => a.sequence - b.sequence);
|
|
3
|
+
const lifecycles = [];
|
|
4
|
+
for (const start of ordered) {
|
|
5
|
+
if (!start.input || !start.result)
|
|
6
|
+
continue;
|
|
7
|
+
const isStart = start.toolName === 'exec' &&
|
|
8
|
+
(start.input.action === 'start' ||
|
|
9
|
+
(start.input.action == null && typeof start.input.command === 'string'));
|
|
10
|
+
const processId = typeof start.input.processId === 'string' ? start.input.processId : start.result.processId;
|
|
11
|
+
if (!isStart) {
|
|
12
|
+
if (start.toolName !== 'exec_wait' || !processId)
|
|
13
|
+
continue;
|
|
14
|
+
const previous = ordered.findLast((item) => item.sequence < start.sequence &&
|
|
15
|
+
(item.input?.processId === processId || item.result?.processId === processId));
|
|
16
|
+
if (previous &&
|
|
17
|
+
previous.result?.status !== 'completed' &&
|
|
18
|
+
previous.result?.status !== 'cancelled')
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const lifecycle = [start];
|
|
22
|
+
let terminal = start.result.status !== 'running';
|
|
23
|
+
if (!terminal) {
|
|
24
|
+
if (!processId)
|
|
25
|
+
continue;
|
|
26
|
+
const related = ordered.filter((item) => item.sequence > start.sequence && item.input?.processId === processId);
|
|
27
|
+
for (const item of related) {
|
|
28
|
+
if (!item.result)
|
|
29
|
+
break;
|
|
30
|
+
lifecycle.push(item);
|
|
31
|
+
if (item.result.status !== 'running') {
|
|
32
|
+
terminal = true;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (terminal && (!processId || !active.has(processId)))
|
|
38
|
+
lifecycles.push(lifecycle);
|
|
39
|
+
}
|
|
40
|
+
return lifecycles;
|
|
41
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { markupAttributes, markupText } from '../prompts/markup.js';
|
|
2
|
+
export function contextGcRetainedInputMarkup(input) {
|
|
3
|
+
return markupText('retained_input', typeof input === 'string' ? input : JSON.stringify(input));
|
|
4
|
+
}
|
|
2
5
|
// Render message placeholders only; structured GC records keep their JSON shape.
|
|
3
6
|
export function contextGcMarkup(kind, fields) {
|
|
4
7
|
const attributes = { kind };
|
|
@@ -8,7 +11,7 @@ export function contextGcMarkup(kind, fields) {
|
|
|
8
11
|
continue;
|
|
9
12
|
if (key === 'retainedInputs' && Array.isArray(value)) {
|
|
10
13
|
for (const input of value)
|
|
11
|
-
children.push(
|
|
14
|
+
children.push(contextGcRetainedInputMarkup(input));
|
|
12
15
|
}
|
|
13
16
|
else if (key === 'contextGc')
|
|
14
17
|
children.push(markupText('instruction', String(value)));
|
|
@@ -51,7 +51,11 @@ export declare function codePartialPatchCallEvidence(records: readonly SessionRe
|
|
|
51
51
|
result: SessionRecord;
|
|
52
52
|
} | undefined;
|
|
53
53
|
export declare function callsFromRecord(record: SessionRecord): CodeToolCallRecord[] | undefined;
|
|
54
|
-
export declare function retainedCodeInputs(records: readonly SessionRecord[], parentCallId: string
|
|
54
|
+
export declare function retainedCodeInputs(records: readonly SessionRecord[], parentCallId: string, todoEvidenceMature?: (original: SessionRecord, successor: SessionRecord) => boolean, execOptions?: {
|
|
55
|
+
readonly activeProcessIds: ReadonlySet<string>;
|
|
56
|
+
readonly evidenceMature: (sequence: number) => boolean;
|
|
57
|
+
readonly selected?: (callId: string, toolName: string, summary: unknown) => boolean;
|
|
58
|
+
}): {
|
|
55
59
|
callId: unknown;
|
|
56
60
|
toolName: unknown;
|
|
57
61
|
input: unknown;
|