@myagentroam/agent 0.9.142 → 0.9.144

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/agent-core/model/anthropic-messages.js +24 -13
  2. package/dist/agent-core/model/contracts.d.ts +4 -0
  3. package/dist/agent-core/model/openai-responses.js +16 -9
  4. package/dist/agent-core/runtime/async-queue.js +7 -5
  5. package/dist/agent-core/runtime/context/gc-tokens.js +4 -2
  6. package/dist/agent-core/runtime/context/gc.js +147 -14
  7. package/dist/agent-core/runtime/context/projection.js +32 -1
  8. package/dist/agent-core/runtime/events.d.ts +1 -0
  9. package/dist/agent-core/runtime/execution.js +4 -0
  10. package/dist/agent-core/sdk/session.js +1 -1
  11. package/dist/agent-core/session/context-gc-checkpoint.d.ts +4 -1
  12. package/dist/agent-core/session/context-gc-checkpoint.js +123 -26
  13. package/dist/agent-core/session/context-gc-observations.d.ts +38 -0
  14. package/dist/agent-core/session/context-gc-observations.js +148 -0
  15. package/dist/agent-core/session/context-gc-replacements.d.ts +1 -1
  16. package/dist/agent-core/session/context-gc-replacements.js +6 -4
  17. package/dist/agent-core/tools/callers.js +1 -1
  18. package/dist/agent-core/tools/code-mode/cells.d.ts +3 -0
  19. package/dist/agent-core/tools/code-mode/cells.js +62 -6
  20. package/dist/agent-core/tools/code-mode/dispatch.js +7 -4
  21. package/dist/agent-core/tools/code-mode/engine.d.ts +12 -0
  22. package/dist/agent-core/tools/code-mode/index.js +183 -14
  23. package/dist/agent-core/tools/code-mode/observations.d.ts +103 -0
  24. package/dist/agent-core/tools/code-mode/observations.js +195 -0
  25. package/dist/agent-core/tools/code-mode/quickjs-engine.js +121 -20
  26. package/dist/agent-core/tools/code-mode/worker.js +8 -1
  27. package/dist/agent-core/tools/contracts.d.ts +4 -0
  28. package/dist/agent-core/tools/execution.d.ts +2 -0
  29. package/dist/agent-core/tools/execution.js +10 -1
  30. package/dist/agent-core/tools/output-projection.js +35 -1
  31. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { posix, win32 } from 'node:path';
3
3
  import { isDeepStrictEqual } from 'node:util';
4
+ import { codeCallEvidence, callsFromRecord, codeObservationEvidence, observationsFromRecord, retainedCodeInputs } from './context-gc-observations.js';
4
5
  import { parseReadContextOutput } from '../tools/local/read-context.js';
5
6
  import { codeCellCompletionSequence, contextGcApplyPatchInputReplacement, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcStaleReadReplacement, contextGcWebOutputReplacement, normalizeContextGcCodeModeInput, normalizeContextGcExecResult } from './context-gc-replacements.js';
6
7
  import { contextGcSubagentNeedsRetainedPriorFinal, contextGcSubagentOutputReplacement, contextGcSubagentSnapshotIsPreciselyCovered, contextGcSubagentSnapshotIsSuperseded, normalizeContextGcSubagentSnapshot } from './context-gc-subagent.js';
@@ -57,7 +58,7 @@ export function contextGcLogicalPathKey(path, workspace, platform) {
57
58
  return resolved;
58
59
  }
59
60
  export function contextGcTargetKey(target) {
60
- return `${target.sequence}:${target.recordId}:${target.role}:${target.callId ?? ''}:${target.toolName ?? ''}`;
61
+ return `${target.sequence}:${target.recordId}:${target.role}:${target.callId ?? ''}:${target.toolName ?? ''}${target.observationId ? `:${target.observationId}` : ''}`;
61
62
  }
62
63
  export function contextGcReplacementKey(replacement) {
63
64
  return contextGcTargetKey(replacement.target);
@@ -154,6 +155,8 @@ export function validateContextGcCheckpoint(records, record, workspace = session
154
155
  if (contextGcSourceProjectionHash(records, payload.boundarySequence, payload.base) !==
155
156
  payload.sourceProjectionHash)
156
157
  return undefined;
158
+ // Later cell completion cannot retroactively authorize an earlier checkpoint.
159
+ records = records.filter((item) => item.sequence <= payload.boundarySequence);
157
160
  const recordsBySequence = new Map(records.map((item) => [item.sequence, item]));
158
161
  if (!triggerMatchesRecords(records, recordsBySequence, payload))
159
162
  return undefined;
@@ -225,13 +228,39 @@ function targetMatchesRecord(target, record) {
225
228
  const messageCallId = modelContextCallId(record.payload);
226
229
  const messageToolName = modelContextToolName(record.payload);
227
230
  return ((target.callId === undefined || target.callId === messageCallId) &&
231
+ (target.observationId === undefined ||
232
+ observationsFromRecord(record)?.some((value) => value.id === target.observationId) ===
233
+ true) &&
228
234
  (target.toolName === undefined || target.toolName === messageToolName));
229
235
  }
230
236
  /** A checkpoint draft is valid only when its replacements match the source records and evidence. */
231
237
  function replacementMatchesRecords(replacement, records, recordsBySequence, workspace, platform) {
232
- const source = recordsBySequence.get(replacement.target.sequence);
238
+ let source = recordsBySequence.get(replacement.target.sequence);
233
239
  if (!source || !isRecord(source.payload))
234
240
  return false;
241
+ if (replacement.target.observationId) {
242
+ const selected = observationsFromRecord(source)?.find((value) => value.id === replacement.target.observationId);
243
+ if (!selected)
244
+ return false;
245
+ if (selected.type === 'tool_result') {
246
+ const evidence = codeObservationEvidence(records, source, selected);
247
+ if (!evidence)
248
+ return false;
249
+ source = {
250
+ ...source,
251
+ payload: {
252
+ ...source.payload,
253
+ callId: selected.source.callId,
254
+ content: selected.result.content
255
+ }
256
+ };
257
+ }
258
+ else
259
+ source = { ...source, payload: { ...source.payload, content: selected.value } };
260
+ recordsBySequence = new Map(recordsBySequence).set(source.sequence, source);
261
+ }
262
+ if (!isRecord(source.payload))
263
+ return false;
235
264
  if (replacement.kind === 'READ_OUTPUT_ITEMS') {
236
265
  if (!workspace || toolNameForCallId(records, replacement.callId, source.sequence) !== 'read')
237
266
  return false;
@@ -243,7 +272,7 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
243
272
  const originalItems = new Map(parsed.items.map((item) => [item.index, item]));
244
273
  return replacement.items.every((item) => {
245
274
  const original = originalItems.get(item.index);
246
- const evidence = successfulPatchEvidenceForPath(records, recordsBySequence, item.evidenceSequence, item.path, workspace, platform);
275
+ const evidence = successfulPatchEvidenceForPath(records, recordsBySequence, item.evidenceSequence, item.path, workspace, platform, item.supersededByCallId);
247
276
  return (original?.pathTruncated === false &&
248
277
  original?.path === item.path &&
249
278
  evidence !== undefined &&
@@ -266,18 +295,19 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
266
295
  result !== undefined &&
267
296
  isDeepStrictEqual(replacement.replacementArguments, contextGcApplyPatchInputReplacement(source.payload.arguments, result, replacement.evidenceSequence, applyPatchReplacementFormatVersion(replacement.replacementArguments))));
268
297
  }
269
- if (replacement.kind === 'TOOL_CALL_INPUT' &&
270
- (replacement.toolName === 'code_mode' || replacement.toolName === 'code_exec')) {
298
+ if (replacement.kind === 'TOOL_CALL_INPUT' && replacement.toolName === 'code_exec') {
271
299
  const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, replacement.toolName);
272
300
  const input = normalizeContextGcCodeModeInput(source.payload.arguments);
273
301
  return (evidence?.callId === replacement.callId &&
274
302
  codeCellCompletionSequence(records, evidence.completed) !== undefined &&
275
303
  evidence.call.sequence === replacement.target.sequence &&
276
304
  input !== undefined &&
277
- isDeepStrictEqual(replacement.replacementArguments, contextGcCodeModeInputReplacement(input)));
305
+ isDeepStrictEqual(replacement.replacementArguments, contextGcCodeModeInputReplacement(input, observationsFromRecord(evidence.result)
306
+ ? retainedCodeInputs(records, replacement.callId)
307
+ : [])));
278
308
  }
279
309
  if (replacement.kind === 'CODE_MODE_OUTPUT') {
280
- const evidence = ['code_mode', 'code_exec', 'code_exec_wait']
310
+ const evidence = ['code_exec', 'code_exec_wait']
281
311
  .map((name) => successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, name))
282
312
  .find((item) => item !== undefined);
283
313
  return (evidence?.callId === replacement.callId &&
@@ -288,12 +318,16 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
288
318
  isRecord(evidence.completed.payload) &&
289
319
  typeof evidence.completed.payload.truncated === 'boolean' &&
290
320
  replacement.replacementContent ===
291
- contextGcCodeModeOutputReplacement(evidence.result.payload.content, evidence.completed.payload.truncated));
321
+ contextGcCodeModeOutputReplacement(source.payload.content, replacement.target.observationId
322
+ ? observationsFromRecord(source)?.some((value) => value.id === replacement.target.observationId &&
323
+ value.type === 'value' &&
324
+ value.truncated === true) === true
325
+ : evidence.completed.payload.truncated));
292
326
  }
293
327
  if (replacement.kind === 'SUBAGENT_OUTPUT') {
294
328
  const retainedPriorFinal = replacement.retainedPriorFinal;
295
329
  const sourceEvidence = successfulToolEvidence(records, recordsBySequence, source.sequence, replacement.toolName);
296
- const terminalEvidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, replacement.terminalToolName);
330
+ const terminalEvidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, replacement.terminalToolName, replacement.terminalCallId);
297
331
  if (!sourceEvidence ||
298
332
  sourceEvidence.callId !== replacement.callId ||
299
333
  !terminalEvidence ||
@@ -304,7 +338,7 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
304
338
  let retainedPriorFinalEvidence;
305
339
  let retainedPriorFinalSnapshot;
306
340
  if (retainedPriorFinal !== undefined) {
307
- retainedPriorFinalEvidence = successfulToolEvidence(records, recordsBySequence, retainedPriorFinal.evidenceSequence, retainedPriorFinal.toolName);
341
+ retainedPriorFinalEvidence = successfulToolEvidence(records, recordsBySequence, retainedPriorFinal.evidenceSequence, retainedPriorFinal.toolName, retainedPriorFinal.callId);
308
342
  if (!retainedPriorFinalEvidence ||
309
343
  retainedPriorFinalEvidence.callId !== retainedPriorFinal.callId ||
310
344
  !isRecord(retainedPriorFinalEvidence.result.payload))
@@ -319,7 +353,7 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
319
353
  (retainedPriorFinal !== undefined) &&
320
354
  (retainedPriorFinalEvidence === undefined ||
321
355
  retainedPriorFinalEvidence.result.sequence < sourceEvidence.result.sequence) &&
322
- sourceEvidence.result.sequence < terminalEvidence.result.sequence &&
356
+ sourceEvidence.completed.sequence < terminalEvidence.completed.sequence &&
323
357
  (contextGcSubagentSnapshotIsSuperseded(sourceSnapshot, terminalSnapshot, retainedPriorFinalSnapshot) ||
324
358
  contextGcSubagentSnapshotIsPreciselyCovered(sourceSnapshot, terminalSnapshot)) &&
325
359
  replacement.replacementContent ===
@@ -351,8 +385,8 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
351
385
  return false;
352
386
  if (replacement.kind === 'TOOL_CALL_INPUT')
353
387
  return isDeepStrictEqual(replacement.replacementArguments, contextGcExecInputReplacement(source.payload.arguments, execInputReplacementFormatVersion(replacement.replacementArguments), replacement.toolName));
354
- const targetEvidence = successfulToolEvidence(records, recordsBySequence, source.sequence, 'exec') ??
355
- successfulToolEvidence(records, recordsBySequence, source.sequence, 'exec_wait');
388
+ const targetEvidence = successfulToolEvidence(records, recordsBySequence, source.sequence, 'exec', replacement.callId) ??
389
+ successfulToolEvidence(records, recordsBySequence, source.sequence, 'exec_wait', replacement.callId);
356
390
  const result = normalizeContextGcExecResult(source.payload.content, targetEvidence?.completed.payload);
357
391
  return (result !== undefined &&
358
392
  replacement.replacementContent ===
@@ -401,7 +435,7 @@ function toolNameForCallId(records, callId, beforeSequence) {
401
435
  ? call.payload.name
402
436
  : undefined;
403
437
  }
404
- function successfulToolEvidence(records, recordsBySequence, evidenceSequence, toolName) {
438
+ function successfulToolEvidence(records, recordsBySequence, evidenceSequence, toolName, expectedCallId) {
405
439
  const result = recordsBySequence.get(evidenceSequence);
406
440
  if (result?.type !== 'model.context' ||
407
441
  !isRecord(result.payload) ||
@@ -410,8 +444,28 @@ function successfulToolEvidence(records, recordsBySequence, evidenceSequence, to
410
444
  return undefined;
411
445
  const callId = result.payload.callId;
412
446
  const call = modelToolCallForCallId(records, callId, result.sequence);
413
- if (!call || !isRecord(call.payload) || call.payload.name !== toolName)
447
+ if (!call ||
448
+ !isRecord(call.payload) ||
449
+ call.payload.name !== toolName ||
450
+ (expectedCallId && expectedCallId !== callId)) {
451
+ for (const observation of observationsFromRecord(result) ?? []) {
452
+ if (observation.type !== 'tool_result' ||
453
+ observation.source.toolName !== toolName ||
454
+ (expectedCallId && observation.source.callId !== expectedCallId))
455
+ continue;
456
+ const evidence = codeObservationEvidence(records, result, observation);
457
+ if (evidence)
458
+ return evidence;
459
+ }
460
+ for (const entry of callsFromRecord(result) ?? []) {
461
+ if (entry.toolName !== toolName || (expectedCallId && entry.callId !== expectedCallId))
462
+ continue;
463
+ const evidence = codeCallEvidence(records, result, entry);
464
+ if (evidence)
465
+ return evidence;
466
+ }
414
467
  return undefined;
468
+ }
415
469
  const completed = records.findLast((record) => record.sequence > call.sequence &&
416
470
  record.sequence < result.sequence &&
417
471
  record.type === 'tool.completed' &&
@@ -441,11 +495,29 @@ function hasFailedToolEvidence(records, call, result, toolName) {
441
495
  record.payload.toolName === toolName);
442
496
  }
443
497
  function modelToolCallForCallId(records, callId, beforeSequence) {
444
- return records.findLast((record) => record.sequence < beforeSequence &&
498
+ const direct = records.findLast((record) => record.sequence < beforeSequence &&
445
499
  record.type === 'model.context' &&
446
500
  isRecord(record.payload) &&
447
501
  record.payload.role === 'tool_call' &&
448
502
  record.payload.callId === callId);
503
+ if (direct)
504
+ return direct;
505
+ const started = records.findLast((record) => record.sequence < beforeSequence &&
506
+ record.type === 'tool.started' &&
507
+ isRecord(record.payload) &&
508
+ record.payload.callId === callId &&
509
+ typeof record.payload.parentCallId === 'string');
510
+ return started && isRecord(started.payload)
511
+ ? {
512
+ ...started,
513
+ payload: {
514
+ role: 'tool_call',
515
+ name: started.payload.toolName,
516
+ callId,
517
+ arguments: started.payload.input
518
+ }
519
+ }
520
+ : undefined;
449
521
  }
450
522
  function modelToolResultForCall(records, call, beforeSequence) {
451
523
  if (!isRecord(call.payload) || typeof call.payload.callId !== 'string')
@@ -458,8 +530,8 @@ function modelToolResultForCall(records, call, beforeSequence) {
458
530
  record.payload.role === 'tool' &&
459
531
  record.payload.callId === callId);
460
532
  }
461
- function successfulPatchEvidenceForPath(records, recordsBySequence, evidenceSequence, path, workspace, platform) {
462
- const evidence = successfulToolEvidence(records, recordsBySequence, evidenceSequence, 'apply_patch');
533
+ function successfulPatchEvidenceForPath(records, recordsBySequence, evidenceSequence, path, workspace, platform, expectedCallId) {
534
+ const evidence = successfulToolEvidence(records, recordsBySequence, evidenceSequence, 'apply_patch', expectedCallId);
463
535
  if (!evidence || !isRecord(evidence.completed.payload))
464
536
  return undefined;
465
537
  const result = contextGcPatchResultFromCompletedPayload(evidence.completed.payload);
@@ -475,8 +547,9 @@ function successfulPatchEvidenceForPath(records, recordsBySequence, evidenceSequ
475
547
  : undefined;
476
548
  }
477
549
  function isSuccessfulExecLifecycleEvidence(replacement, source, records, recordsBySequence) {
478
- const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, 'exec') ??
479
- successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, 'exec_wait');
550
+ const expectedTerminal = replacement.kind === 'EXEC_OUTPUT' ? replacement.terminalCallId : undefined;
551
+ const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, 'exec', expectedTerminal) ??
552
+ successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, 'exec_wait', expectedTerminal);
480
553
  if (!evidence || !isRecord(evidence.call.payload) || !isRecord(evidence.result.payload))
481
554
  return false;
482
555
  const finalResult = normalizeContextGcExecResult(evidence.result.payload.content, evidence.completed.payload);
@@ -497,7 +570,7 @@ function isSuccessfulExecLifecycleEvidence(replacement, source, records, records
497
570
  : modelToolResultForCall(records, targetCall, evidence.result.sequence);
498
571
  if (!targetResult)
499
572
  return false;
500
- const targetEvidence = successfulToolEvidence(records, recordsBySequence, targetResult.sequence, targetCall.payload.name);
573
+ const targetEvidence = successfulToolEvidence(records, recordsBySequence, targetResult.sequence, targetCall.payload.name, replacement.callId);
501
574
  if (!targetEvidence || targetEvidence.call.sequence !== targetCall.sequence)
502
575
  return false;
503
576
  if (targetEvidence.callId === evidence.callId)
@@ -597,7 +670,7 @@ function isContextGcReplacement(value) {
597
670
  if (!isRecord(value) ||
598
671
  !isContextGcTarget(value.target) ||
599
672
  !isNonemptyString(value.callId) ||
600
- value.target.callId !== value.callId)
673
+ (value.target.callId !== value.callId && value.target.observationId === undefined))
601
674
  return false;
602
675
  if (value.kind === 'READ_OUTPUT_ITEMS') {
603
676
  const items = value.items;
@@ -607,7 +680,14 @@ function isContextGcReplacement(value) {
607
680
  Array.isArray(items) &&
608
681
  items.length > 0 &&
609
682
  items.every((item) => isRecord(item) &&
610
- hasOnlyKeys(item, ['index', 'path', 'replacement', 'evidenceSequence']) &&
683
+ hasOnlyKeys(item, [
684
+ 'index',
685
+ 'path',
686
+ 'replacement',
687
+ 'evidenceSequence',
688
+ 'supersededByCallId'
689
+ ]) &&
690
+ (item.supersededByCallId === undefined || isNonemptyString(item.supersededByCallId)) &&
611
691
  isNonnegativeInteger(item.index) &&
612
692
  isNonemptyString(item.path) &&
613
693
  isNonemptyString(item.replacement) &&
@@ -634,7 +714,6 @@ function isContextGcReplacement(value) {
634
714
  (value.toolName === 'apply_patch' ||
635
715
  value.toolName === 'exec' ||
636
716
  value.toolName === 'exec_wait' ||
637
- value.toolName === 'code_mode' ||
638
717
  value.toolName === 'code_exec') &&
639
718
  value.target.toolName === value.toolName &&
640
719
  Array.isArray(value.providerAliasTargets) &&
@@ -697,14 +776,32 @@ function isContextGcReplacement(value) {
697
776
  isNonemptyString(value.replacementContent) &&
698
777
  isPositiveInteger(value.evidenceSequence));
699
778
  return (value.kind === 'EXEC_OUTPUT' &&
700
- hasOnlyKeys(value, ['kind', 'callId', 'target', 'replacementContent', 'evidenceSequence']) &&
779
+ hasOnlyKeys(value, [
780
+ 'kind',
781
+ 'callId',
782
+ 'target',
783
+ 'replacementContent',
784
+ 'evidenceSequence',
785
+ 'terminalCallId'
786
+ ]) &&
787
+ (value.terminalCallId === undefined || isNonemptyString(value.terminalCallId)) &&
701
788
  value.target.role === 'tool' &&
702
789
  isNonemptyString(value.replacementContent) &&
703
790
  isPositiveInteger(value.evidenceSequence));
704
791
  }
705
792
  function isContextGcTarget(value) {
706
793
  return (isRecord(value) &&
707
- hasOnlyKeys(value, ['sequence', 'recordId', 'role', 'callId', 'toolName', 'sourceHash']) &&
794
+ hasOnlyKeys(value, [
795
+ 'sequence',
796
+ 'recordId',
797
+ 'role',
798
+ 'callId',
799
+ 'toolName',
800
+ 'sourceHash',
801
+ 'observationId'
802
+ ]) &&
803
+ (value.observationId === undefined ||
804
+ (value.role === 'tool' && isNonemptyString(value.observationId))) &&
708
805
  isPositiveInteger(value.sequence) &&
709
806
  isNonemptyString(value.recordId) &&
710
807
  (value.role === 'tool_call' || value.role === 'tool' || value.role === 'provider') &&
@@ -0,0 +1,38 @@
1
+ import type { SessionRecord } from './jsonl-store.js';
2
+ import { type CodeObservation, type CodeToolCallRecord } from '../tools/code-mode/observations.js';
3
+ /** Resolve private observation identity to real dispatch facts, not Provider tool calls. */
4
+ export declare function codeObservationEvidence(records: readonly SessionRecord[], result: SessionRecord, observation: Extract<CodeObservation, {
5
+ type: 'tool_result';
6
+ }>): {
7
+ callId: string;
8
+ call: SessionRecord;
9
+ completed: SessionRecord;
10
+ result: SessionRecord;
11
+ } | undefined;
12
+ export declare function observationsFromRecord(record: SessionRecord): CodeObservation[] | undefined;
13
+ export declare function codeCallEvidence(records: readonly SessionRecord[], result: SessionRecord, entry: CodeToolCallRecord): {
14
+ result: {
15
+ payload: {
16
+ role: string;
17
+ callId: string;
18
+ content: string;
19
+ };
20
+ schemaVersion: 1;
21
+ recordId: string;
22
+ sequence: number;
23
+ timestamp: string;
24
+ sessionId: string;
25
+ executionId?: string;
26
+ turnId?: string;
27
+ type: string;
28
+ };
29
+ callId: string;
30
+ call: SessionRecord;
31
+ completed: SessionRecord;
32
+ } | undefined;
33
+ export declare function callsFromRecord(record: SessionRecord): CodeToolCallRecord[] | undefined;
34
+ export declare function retainedCodeInputs(records: readonly SessionRecord[], parentCallId: string): {
35
+ callId: unknown;
36
+ toolName: unknown;
37
+ input: unknown;
38
+ }[];
@@ -0,0 +1,148 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { codeCellCompletionSequence, contextGcPatchResultFromCompletedPayload } from './context-gc-replacements.js';
3
+ import { parseCodeCalls, parseObservations } from '../tools/code-mode/observations.js';
4
+ /** Resolve private observation identity to real dispatch facts, not Provider tool calls. */
5
+ export function codeObservationEvidence(records, result, observation) {
6
+ if (observation.outcome !== 'completed')
7
+ return undefined;
8
+ const started = records.findLast((record) => record.sequence < result.sequence &&
9
+ record.type === 'tool.started' &&
10
+ isRecord(record.payload) &&
11
+ record.payload.callId === observation.source.callId &&
12
+ record.payload.toolName === observation.source.toolName &&
13
+ typeof record.payload.parentCallId === 'string' &&
14
+ isDeepStrictEqual(record.payload.input, observation.source.input));
15
+ if (!started || !isRecord(started.payload))
16
+ return undefined;
17
+ const parentId = started.payload.parentCallId;
18
+ const parent = records.find((record) => record.type === 'model.context' &&
19
+ record.sequence < started.sequence &&
20
+ isRecord(record.payload) &&
21
+ record.payload.role === 'tool_call' &&
22
+ record.payload.callId === parentId &&
23
+ record.payload.name === 'code_exec');
24
+ if (!parent)
25
+ return undefined;
26
+ const original = records.find((record) => record.recordId === result.recordId) ?? result;
27
+ if (!isRecord(original.payload))
28
+ return undefined;
29
+ const outerId = original.payload.callId;
30
+ const outer = records.findLast((record) => record.sequence < result.sequence &&
31
+ record.type === 'tool.completed' &&
32
+ isRecord(record.payload) &&
33
+ record.payload.callId === outerId &&
34
+ (record.payload.toolName === 'code_exec' || record.payload.toolName === 'code_exec_wait'));
35
+ if (!outer ||
36
+ codeCellCompletionSequence(records, outer) === undefined ||
37
+ records.some((record) => record.sequence < result.sequence &&
38
+ record.type === 'tool.failed' &&
39
+ isRecord(record.payload) &&
40
+ record.payload.callId === outerId))
41
+ return undefined;
42
+ if (original.payload.callId !== parentId) {
43
+ const initial = records.find((record) => record.sequence < outer.sequence &&
44
+ record.type === 'tool.completed' &&
45
+ isRecord(record.payload) &&
46
+ record.payload.callId === parentId);
47
+ if (!initial ||
48
+ !isRecord(outer.payload) ||
49
+ outer.payload.toolName !== 'code_exec_wait' ||
50
+ !isRecord(initial.payload) ||
51
+ !isRecord(outer.payload.data) ||
52
+ !isRecord(initial.payload.data) ||
53
+ typeof outer.payload.data.cellId !== 'string' ||
54
+ outer.payload.data.cellId !== initial.payload.data.cellId)
55
+ return undefined;
56
+ }
57
+ const completed = records.findLast((record) => record.sequence > started.sequence &&
58
+ record.sequence < result.sequence &&
59
+ record.type === 'tool.completed' &&
60
+ isRecord(record.payload) &&
61
+ record.payload.callId === observation.source.callId &&
62
+ record.payload.toolName === observation.source.toolName &&
63
+ record.payload.parentCallId === parentId);
64
+ if (!completed ||
65
+ records.some((record) => record.sequence > started.sequence &&
66
+ record.sequence < result.sequence &&
67
+ record.type === 'tool.failed' &&
68
+ isRecord(record.payload) &&
69
+ record.payload.callId === observation.source.callId))
70
+ return undefined;
71
+ const call = {
72
+ ...started,
73
+ payload: {
74
+ role: 'tool_call',
75
+ name: observation.source.toolName,
76
+ callId: observation.source.callId,
77
+ arguments: observation.source.input
78
+ }
79
+ };
80
+ const selected = {
81
+ ...result,
82
+ payload: {
83
+ role: 'tool',
84
+ callId: observation.source.callId,
85
+ content: observation.result.content,
86
+ observationId: observation.id
87
+ }
88
+ };
89
+ return { callId: observation.source.callId, call, completed, result: selected };
90
+ }
91
+ export function observationsFromRecord(record) {
92
+ return isRecord(record.payload) ? parseObservations(record.payload.observations) : undefined;
93
+ }
94
+ export function codeCallEvidence(records, result, entry) {
95
+ if (entry.status !== 'completed')
96
+ return undefined;
97
+ const evidence = codeObservationEvidence(records, result, {
98
+ type: 'tool_result',
99
+ id: `call:${entry.callId}`,
100
+ source: { callId: entry.callId, toolName: entry.toolName, input: entry.input },
101
+ outcome: 'completed',
102
+ result: { content: '' }
103
+ });
104
+ if (!evidence || !isRecord(evidence.completed.payload))
105
+ return undefined;
106
+ return {
107
+ ...evidence,
108
+ result: {
109
+ ...evidence.result,
110
+ payload: {
111
+ role: 'tool',
112
+ callId: entry.callId,
113
+ content: JSON.stringify(evidence.completed.payload.data ?? {})
114
+ }
115
+ }
116
+ };
117
+ }
118
+ export function callsFromRecord(record) {
119
+ return isRecord(record.payload) ? parseCodeCalls(record.payload.calls) : undefined;
120
+ }
121
+ export function retainedCodeInputs(records, parentCallId) {
122
+ return records.flatMap((record) => {
123
+ if (!isRecord(record.payload) ||
124
+ record.type !== 'tool.started' ||
125
+ record.payload.parentCallId !== parentCallId)
126
+ return [];
127
+ const call = record.payload;
128
+ const completedPatch = call.toolName === 'apply_patch' &&
129
+ records.some((next) => next.sequence > record.sequence &&
130
+ next.type === 'tool.completed' &&
131
+ isRecord(next.payload) &&
132
+ next.payload.callId === call.callId &&
133
+ next.payload.toolName === 'apply_patch' &&
134
+ next.payload.parentCallId === parentCallId &&
135
+ contextGcPatchResultFromCompletedPayload(next.payload) !== undefined) &&
136
+ !records.some((next) => next.sequence > record.sequence &&
137
+ next.type === 'tool.failed' &&
138
+ isRecord(next.payload) &&
139
+ next.payload.callId === call.callId);
140
+ // Successful patches have durable file evidence; other inputs retain their direct-mode semantics.
141
+ return completedPatch
142
+ ? []
143
+ : [{ callId: call.callId, toolName: call.toolName, input: call.input }];
144
+ });
145
+ }
146
+ function isRecord(value) {
147
+ return !!value && typeof value === 'object' && !Array.isArray(value);
148
+ }
@@ -4,7 +4,7 @@ export interface ContextGcCodeModeInput {
4
4
  readonly inputMode: 'freeform' | 'json';
5
5
  }
6
6
  export declare function normalizeContextGcCodeModeInput(arguments_: unknown): ContextGcCodeModeInput | undefined;
7
- export declare function contextGcCodeModeInputReplacement(input: ContextGcCodeModeInput): unknown;
7
+ export declare function contextGcCodeModeInputReplacement(input: ContextGcCodeModeInput, retainedInputs?: readonly unknown[]): unknown;
8
8
  export declare function contextGcCodeModeOutputReplacement(originalContent: string, truncated: boolean): string;
9
9
  export interface ContextGcPatchFile {
10
10
  readonly path: string;
@@ -14,9 +14,13 @@ export function normalizeContextGcCodeModeInput(arguments_) {
14
14
  return { code: arguments_.code, inputMode: 'json' };
15
15
  return undefined;
16
16
  }
17
- export function contextGcCodeModeInputReplacement(input) {
17
+ export function contextGcCodeModeInputReplacement(input, retainedInputs = []) {
18
18
  const replacement = `<context_gc kind="completed_code_mode_input" originalBytes="${Buffer.byteLength(input.code)}" lines="${lineCount(input.code)}" inputMode="${input.inputMode}">Completed Code Mode source was reclaimed after six successful rollouts. The paired output, images, completion evidence, and nested tool side effects remain in history.</context_gc>`;
19
- return input.inputMode === 'freeform' ? replacement : { code: replacement };
19
+ const content = replacement +
20
+ (retainedInputs.length
21
+ ? `\nRetained tool instructions: ${JSON.stringify(retainedInputs)}`
22
+ : '');
23
+ return input.inputMode === 'freeform' ? content : { code: content };
20
24
  }
21
25
  export function contextGcCodeModeOutputReplacement(originalContent, truncated) {
22
26
  const preview = unicodeHeadTail(originalContent, CODE_MODE_OUTPUT_PREVIEW_CHARACTERS);
@@ -320,8 +324,6 @@ function isNonnegativeInteger(value) {
320
324
  export function codeCellCompletionSequence(records, completed) {
321
325
  if (!isRecord(completed.payload))
322
326
  return undefined;
323
- if (completed.payload.toolName === 'code_mode')
324
- return completed.sequence;
325
327
  const data = completed.payload.data;
326
328
  if (!isRecord(data) || typeof data.cellId !== 'string')
327
329
  return undefined;
@@ -13,7 +13,7 @@ export function toolCallers(tool) {
13
13
  export function allowsToolCaller(tool, caller) {
14
14
  // Control tools cannot be widened by a Host override or historical definition.
15
15
  if (caller === 'programmatic' &&
16
- ['code_exec', 'code_exec_wait', 'code_mode', 'tool_search', 'question', 'skill'].includes(tool.name))
16
+ ['code_exec', 'code_exec_wait', 'tool_search', 'question', 'skill'].includes(tool.name))
17
17
  return false;
18
18
  return toolCallers(tool).includes(caller);
19
19
  }
@@ -1,6 +1,9 @@
1
1
  import { MarAgentError } from '../../error.js';
2
2
  import type { ToolExecutionContext, ToolExecutionResult } from '../contracts.js';
3
+ import { type CodeObservation } from './observations.js';
3
4
  export interface CellOutput {
5
+ call(value: import('./observations.js').CodeToolCallRecord): void;
6
+ observe(value: CodeObservation): void;
4
7
  text(value: string, originalBytes?: number): void;
5
8
  image(value: NonNullable<ToolExecutionResult['images']>[number]): void;
6
9
  yield(): void;