@myagentroam/agent 0.10.68 → 0.11.1

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.
@@ -1,8 +1,6 @@
1
- import { isProviderContextOverflow, recoverModelStream } from '../model/recovery.js';
2
- import { estimatedRequestTokens } from '../runtime/context/request-budget.js';
3
1
  import { restoreMessages } from '../runtime/context/restore.js';
4
2
  import { createNativeSteer } from './native-steer.js';
5
- import { lastConfigurationEffort, latestModelUsage, reasoningEffortBaseline, shouldUseNativeCompaction } from '../runtime/context/usage.js';
3
+ import { lastConfigurationEffort, latestModelUsage, reasoningEffortBaseline } from '../runtime/context/usage.js';
6
4
  import { splitContextualPrompt } from '../runtime/context/contextual-blocks.js';
7
5
  import { contextualInstructionInsertIndex, insertContextualWorldState, latestContextualWorldStateGeneration, parsePersistedContextualWorldState, persistContextualWorldState, resetContextualWorldState, updateContextualWorldState, updatePromptWorldState } from '../runtime/context/world-state.js';
8
6
  import { eventForPersistence, publicModelUsage } from '../runtime/event-projection.js';
@@ -35,10 +33,11 @@ import { childSubagentInstruction } from '../prompts/subagent.js';
35
33
  import { renderSubagentUpdate } from '../subagent/model-update.js';
36
34
  import { AsyncQueue } from '../runtime/async-queue.js';
37
35
  import { CompactionOperation } from '../runtime/compaction/operation.js';
36
+ import { generateCompactionCandidate } from '../runtime/compaction/generate.js';
38
37
  import { CompactCoordinator } from '../runtime/compaction/coordinator.js';
39
38
  import { CompactRequests } from '../runtime/compaction/requests.js';
40
39
  import { compactSnapshotAnchor } from '../runtime/compaction/retained-context.js';
41
- import { DEFAULT_COMPACTION_FOCUS, buildCompactedHistory, buildCompactionInput, buildCompactionMessages, buildNativeCompactionMessages, compactedHistoryMessages } from '../runtime/compaction/history.js';
40
+ import { DEFAULT_COMPACTION_FOCUS, buildCompactedHistory, buildCompactionMessages, compactedHistoryMessages } from '../runtime/compaction/history.js';
42
41
  import { currentLocalTimeContext, environmentContextUpdate, restoreEnvironmentState } from '../runtime/context/environment.js';
43
42
  import { ContextGcRuntime } from '../runtime/context/gc-runtime.js';
44
43
  import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
@@ -195,7 +194,7 @@ export function createSessionOpener(services) {
195
194
  }
196
195
  };
197
196
  const execution = (mode, input, internal = {}) => {
198
- services.assertCanStart();
197
+ services.assertCanStart(sessionId);
199
198
  if ([...active.keys()].some((key) => key.startsWith(`${sessionId}:`)))
200
199
  throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Session already has an active execution.');
201
200
  const { models: executionModels, defaultModelId: executionDefaultModelId, generation: executionModelGeneration } = services.modelState();
@@ -280,6 +279,7 @@ export function createSessionOpener(services) {
280
279
  let sequence = 0;
281
280
  let emitTail = Promise.resolve();
282
281
  let releaseSession;
282
+ let failedExecution = false;
283
283
  let exhaustedModelAttempt;
284
284
  // Serialize durable events before either the local stream or Host observes them.
285
285
  let persistedTerminal = false;
@@ -393,31 +393,181 @@ export function createSessionOpener(services) {
393
393
  const contextualWorldStateGeneration = latestContextualWorldStateGeneration(contextRecords);
394
394
  const restoredContextualWorldState = sessionRuntime.contextualWorldState;
395
395
  const persistedContextualWorldState = parsePersistedContextualWorldState(await store.readContextualWorldState(sessionId));
396
- let contextualWorldState;
397
- if (restoredContextualWorldState !== undefined &&
398
- restoredContextualWorldState.generation === contextualWorldStateGeneration)
399
- contextualWorldState = restoredContextualWorldState;
400
- else if (persistedContextualWorldState !== undefined &&
401
- persistedContextualWorldState.generation === contextualWorldStateGeneration) {
402
- contextualWorldState = persistedContextualWorldState;
403
- sessionRuntime.contextualWorldState = contextualWorldState;
404
- }
405
- else {
406
- contextualWorldState = {
407
- generation: contextualWorldStateGeneration,
408
- initialized: false,
409
- instructionSnapshot: undefined,
410
- resourceSnapshot: undefined,
411
- entries: []
412
- };
413
- sessionRuntime.contextualWorldState = contextualWorldState;
414
- }
396
+ const initialContextualWorldState = restoredContextualWorldState !== undefined &&
397
+ restoredContextualWorldState.generation === contextualWorldStateGeneration
398
+ ? restoredContextualWorldState
399
+ : persistedContextualWorldState !== undefined &&
400
+ persistedContextualWorldState.generation === contextualWorldStateGeneration
401
+ ? persistedContextualWorldState
402
+ : {
403
+ generation: contextualWorldStateGeneration,
404
+ initialized: false,
405
+ instructionSnapshot: undefined,
406
+ resourceSnapshot: undefined,
407
+ entries: []
408
+ };
409
+ sessionRuntime.contextualWorldState = initialContextualWorldState;
415
410
  const compactMayContainAgentInstructions = contextRecords.some((record) => compactSummaryFromCheckpoint(record) !== undefined);
416
411
  const restored = await restoreMessages(store, sessionId, contextRecords, {
417
412
  messageSources,
418
413
  includeImages: mode !== 'compact'
419
414
  });
420
- const messages = restored.messages;
415
+ // This execution owns checkpoint adoption, not another copy of the live context.
416
+ const modelContext = (() => {
417
+ const messages = restored.messages;
418
+ let userMessages = [...restored.userMessages];
419
+ let recordedEnvironment = restored.environmentState;
420
+ let instructionInsertIndex = 0;
421
+ let worldState = initialContextualWorldState;
422
+ const commitWorldState = async (next) => {
423
+ await persistContextualWorldState(store, sessionId, next);
424
+ worldState = next;
425
+ sessionRuntime.contextualWorldState = next;
426
+ };
427
+ const resetCompactWorldState = async (generation, committed) => {
428
+ instructionInsertIndex = contextualInstructionInsertIndex(messages);
429
+ const next = resetContextualWorldState(generation, instructionInsertIndex, {
430
+ instructionSnapshot: currentInstructionSnapshot,
431
+ instructionContent: renderAgentInstructions(instructionEntries, true),
432
+ resourceSnapshot: committed
433
+ ? committed.resources
434
+ : retainedSessionResourcesSnapshot(await loadRetainedSessionResources())
435
+ });
436
+ if (!committed)
437
+ await commitWorldState(next);
438
+ else {
439
+ // The checkpoint is authoritative; its sidecar is recoverable on restart.
440
+ worldState = next;
441
+ sessionRuntime.contextualWorldState = next;
442
+ try {
443
+ await persistContextualWorldState(store, sessionId, next);
444
+ }
445
+ catch {
446
+ coreExtensions.diagnose({
447
+ code: 'MAR_AGENT_COMPACT_SIDECAR_PERSIST_FAILED',
448
+ sessionId
449
+ });
450
+ }
451
+ }
452
+ };
453
+ const recordEnvironment = async () => {
454
+ const message = environmentContextUpdate(executionEnvironment, recordedEnvironment);
455
+ if (!message)
456
+ return;
457
+ await store.append(sessionId, {
458
+ type: 'context.environment',
459
+ payload: { version: 1, state: executionEnvironment },
460
+ executionId,
461
+ turnId
462
+ });
463
+ messages.push(message);
464
+ recordedEnvironment = executionEnvironment;
465
+ };
466
+ const syncMcpWorldState = async (index = instructionInsertIndex) => {
467
+ const snapshot = mcpRegistry.navigation();
468
+ if (worldState.mcpSnapshot === snapshot)
469
+ return;
470
+ const next = {
471
+ ...worldState,
472
+ mcpSnapshot: snapshot,
473
+ entries: [
474
+ ...worldState.entries,
475
+ {
476
+ index,
477
+ kind: 'mcp',
478
+ content: snapshot ?? '<mcp_catalog>No deferred MCP tools remain.</mcp_catalog>'
479
+ }
480
+ ]
481
+ };
482
+ await commitWorldState(next);
483
+ };
484
+ return {
485
+ messages,
486
+ get worldState() {
487
+ return worldState;
488
+ },
489
+ commitWorldState,
490
+ recordEnvironment,
491
+ syncMcpWorldState,
492
+ rememberInput(input) {
493
+ userMessages.push(input);
494
+ },
495
+ compactInputs(prefix) {
496
+ return userMessages.filter((entry) => prefix.some((message) => message.role === 'user' &&
497
+ message.contextKind === undefined &&
498
+ (entry.metadata?.recordId
499
+ ? message.metadata?.recordId === entry.metadata.recordId
500
+ : message.content === entry.content)));
501
+ },
502
+ async initializeWorldState() {
503
+ instructionInsertIndex =
504
+ mode === 'compact' ? contextualInstructionInsertIndex(messages) : messages.length;
505
+ // Only the latest Compact may rebuild missing sidecar resources, never a GC boundary.
506
+ if (!worldState.initialized &&
507
+ contextRecords.some((record) => record.recordId === contextualWorldStateGeneration &&
508
+ compactSummaryFromCheckpoint(record) !== undefined))
509
+ await resetCompactWorldState(contextualWorldStateGeneration);
510
+ const nextInstructionWorldState = updateContextualWorldState(worldState, instructionInsertIndex, {
511
+ instructionSnapshot: currentInstructionSnapshot,
512
+ instructionContent: renderAgentInstructions(instructionEntries, compactMayContainAgentInstructions ||
513
+ worldState.instructionSnapshot !== undefined)
514
+ });
515
+ if (!isDeepStrictEqual(worldState, nextInstructionWorldState))
516
+ await commitWorldState(nextInstructionWorldState);
517
+ },
518
+ // Must run inside the existing writer/projection commit window.
519
+ replaceCompactMessages(replacementHistory, retainedModelContext) {
520
+ messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory), ...retainedModelContext);
521
+ pendingAppends.length = 0;
522
+ selected.resetContinuation?.();
523
+ },
524
+ // Derived state follows the committed splice, never delays it past the writer window.
525
+ async adoptCompactCheckpoint(recordId, replacementHistory, retainedModelContext, resources) {
526
+ contextGc.reset();
527
+ restoreMcpDiscovery();
528
+ recordedEnvironment = executionEnvironment;
529
+ userMessages = replacementHistory
530
+ .filter((message) => message.kind !== 'summary')
531
+ .map((message) => ({
532
+ content: message.content,
533
+ ...(message.metadata ? { metadata: { ...message.metadata } } : {})
534
+ }));
535
+ userMessages.push(...retainedModelContext
536
+ .filter((message) => message.role === 'user' && message.contextKind === undefined)
537
+ .map((message) => ({
538
+ content: message.content,
539
+ ...(message.metadata ? { metadata: message.metadata } : {})
540
+ })));
541
+ await resetCompactWorldState(recordId, { resources });
542
+ },
543
+ async adoptGcCheckpoint(committed, appendBoundary) {
544
+ smartContextTokens = undefined;
545
+ selected.resetContinuation?.();
546
+ const refreshed = await restoreMessages(store, sessionId, committed.records, {
547
+ includeImages: true,
548
+ currentTurnId: turnId,
549
+ messageSources,
550
+ projection: committed.projection
551
+ });
552
+ const restoredIds = new Set(committed.records.map((record) => record.recordId));
553
+ const lateMessages = pendingAppends
554
+ .slice(appendBoundary)
555
+ .filter((entry) => !restoredIds.has(entry.recordId))
556
+ .map((entry) => entry.message);
557
+ messages.splice(0, messages.length, ...refreshed.messages, ...lateMessages);
558
+ restoreMcpDiscovery();
559
+ recordedEnvironment = refreshed.environmentState;
560
+ userMessages = [...refreshed.userMessages];
561
+ instructionInsertIndex = contextualInstructionInsertIndex(messages);
562
+ const next = resetContextualWorldState(latestContextualWorldStateGeneration(committed.records), instructionInsertIndex, {
563
+ instructionSnapshot: currentInstructionSnapshot,
564
+ instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined))
565
+ });
566
+ await commitWorldState(next);
567
+ }
568
+ };
569
+ })();
570
+ const messages = modelContext.messages;
421
571
  const pendingInputs = restored.pendingInputs;
422
572
  const waitInterrupt = createToolWaitInterrupt(() => pendingInputs.size > 0);
423
573
  liveModelMessages = messages;
@@ -427,7 +577,6 @@ export function createSessionOpener(services) {
427
577
  messages.push(pending.message);
428
578
  pendingAppends.length = 0;
429
579
  immediateTarget = messages;
430
- let recordedEnvironment = restored.environmentState;
431
580
  let smartInput;
432
581
  let compactContext;
433
582
  let startCompact;
@@ -472,7 +621,7 @@ export function createSessionOpener(services) {
472
621
  eligible: smartCompaction,
473
622
  requested: false
474
623
  };
475
- compactionUserMessages.push({
624
+ modelContext.rememberInput({
476
625
  content: content.modelText ?? content.text,
477
626
  metadata: inputMetadata
478
627
  });
@@ -545,7 +694,7 @@ export function createSessionOpener(services) {
545
694
  };
546
695
  messages.push(message);
547
696
  messageSources.set(message, applied);
548
- compactionUserMessages.push({ content, metadata });
697
+ modelContext.rememberInput({ content, metadata });
549
698
  smartInput = { recordId: received.recordId, eligible, requested: false };
550
699
  for (const text of (payload.modelContext ?? payload.context ?? []))
551
700
  messages.push({ role: 'user', content: text, contextKind: 'contextual' });
@@ -600,20 +749,6 @@ export function createSessionOpener(services) {
600
749
  signal: controller.signal,
601
750
  ...(restoring ? { restoring: true } : {})
602
751
  }, inputs.boundarySize);
603
- const recordEnvironment = async () => {
604
- const message = environmentContextUpdate(executionEnvironment, recordedEnvironment);
605
- if (!message)
606
- return;
607
- await store.append(sessionId, {
608
- type: 'context.environment',
609
- payload: { version: 1, state: executionEnvironment },
610
- executionId,
611
- turnId
612
- });
613
- messages.push(message);
614
- recordedEnvironment = executionEnvironment;
615
- };
616
- let compactionUserMessages = [...restored.userMessages];
617
752
  const hasRestoredUserImages = messages.some((message) => message.role === 'user' && Boolean(message.images?.length));
618
753
  let modelInputImages;
619
754
  const currentExecutionToolImages = [];
@@ -854,7 +989,6 @@ export function createSessionOpener(services) {
854
989
  });
855
990
  const instructionEntries = instructionContext?.entries ?? [];
856
991
  const currentInstructionSnapshot = renderAgentInstructions(instructionEntries, false);
857
- let agentInstructionInsertIndex = mode === 'compact' ? contextualInstructionInsertIndex(messages) : messages.length;
858
992
  const baseTools = () => [...captureTools().values()]
859
993
  .filter((binding) => binding.mcp === undefined)
860
994
  .map((binding) => binding.definition)
@@ -880,49 +1014,7 @@ export function createSessionOpener(services) {
880
1014
  subagents: subagents?.listSessionResources() ?? []
881
1015
  };
882
1016
  };
883
- const commitWorldState = async (next) => {
884
- await persistContextualWorldState(store, sessionId, next);
885
- contextualWorldState = next;
886
- sessionRuntime.contextualWorldState = next;
887
- };
888
- const resetCompactWorldState = async (generation, committed) => {
889
- agentInstructionInsertIndex = contextualInstructionInsertIndex(messages);
890
- const next = resetContextualWorldState(generation, agentInstructionInsertIndex, {
891
- instructionSnapshot: currentInstructionSnapshot,
892
- instructionContent: renderAgentInstructions(instructionEntries, true),
893
- resourceSnapshot: committed
894
- ? committed.resources
895
- : retainedSessionResourcesSnapshot(await loadRetainedSessionResources())
896
- });
897
- if (!committed)
898
- await commitWorldState(next);
899
- else {
900
- // The checkpoint is authoritative; its sidecar is recoverable on restart.
901
- contextualWorldState = next;
902
- sessionRuntime.contextualWorldState = next;
903
- try {
904
- await persistContextualWorldState(store, sessionId, next);
905
- }
906
- catch {
907
- coreExtensions.diagnose({
908
- code: 'MAR_AGENT_COMPACT_SIDECAR_PERSIST_FAILED',
909
- sessionId
910
- });
911
- }
912
- }
913
- };
914
- // A missing sidecar is rebuilt only for the latest Compact, never for a GC boundary.
915
- if (!contextualWorldState.initialized &&
916
- contextRecords.some((record) => record.recordId === contextualWorldStateGeneration &&
917
- compactSummaryFromCheckpoint(record) !== undefined))
918
- await resetCompactWorldState(contextualWorldStateGeneration);
919
- const nextInstructionWorldState = updateContextualWorldState(contextualWorldState, agentInstructionInsertIndex, {
920
- instructionSnapshot: currentInstructionSnapshot,
921
- instructionContent: renderAgentInstructions(instructionEntries, compactMayContainAgentInstructions ||
922
- contextualWorldState.instructionSnapshot !== undefined)
923
- });
924
- if (!isDeepStrictEqual(contextualWorldState, nextInstructionWorldState))
925
- await commitWorldState(nextInstructionWorldState);
1017
+ await modelContext.initializeWorldState();
926
1018
  const buildExecutionPromptSections = (availableTools = executionTools, promptMode = mode) => buildSessionPromptSections(availableTools, promptMode, selectedModel, executionSystemInstruction, instructionContext?.skillCatalog);
927
1019
  const buildExecutionSystemPrompt = (availableTools = executionTools, promptMode = mode) => renderPromptSections(buildExecutionPromptSections(availableTools, promptMode));
928
1020
  const promptComposition = (availableTools, promptMode) => ({
@@ -933,26 +1025,8 @@ export function createSessionOpener(services) {
933
1025
  tools: availableTools.map((tool) => tool.name)
934
1026
  }
935
1027
  });
936
- const syncMcpWorldState = async (index) => {
937
- const snapshot = mcpRegistry.navigation();
938
- if (contextualWorldState.mcpSnapshot === snapshot)
939
- return;
940
- const next = {
941
- ...contextualWorldState,
942
- mcpSnapshot: snapshot,
943
- entries: [
944
- ...contextualWorldState.entries,
945
- {
946
- index,
947
- kind: 'mcp',
948
- content: snapshot ?? '<mcp_catalog>No deferred MCP tools remain.</mcp_catalog>'
949
- }
950
- ]
951
- };
952
- await commitWorldState(next);
953
- };
954
1028
  if (mode !== 'compact')
955
- await syncMcpWorldState(agentInstructionInsertIndex);
1029
+ await modelContext.syncMcpWorldState();
956
1030
  const writeCompactionRecord = async (summary, _automatic, signal, write) => withRecordProjection(async () => {
957
1031
  signal.throwIfAborted();
958
1032
  const stored = await write(summary, { signal });
@@ -1138,7 +1212,7 @@ export function createSessionOpener(services) {
1138
1212
  ? normalizeMessagesForModel([...preparingRequestMessages, ...messages.slice(preparingRequestBaseLength)], { supportsImages: selectedModel.inputCapabilities.includes('IMAGE') })
1139
1213
  : projectSessionRequestMessages(messages.map((message, index) => index === currentExecutionUserIndex && modelInputImages?.length
1140
1214
  ? { ...message, images: modelInputImages }
1141
- : message), mode === 'compact' ? [] : contextualWorldState.entries, mode !== 'compact' && selectedModel.inputCapabilities.includes('IMAGE'), baseTools().some((tool) => tool.name === 'code_exec'), permissionPolicy.mode === 'auto')
1215
+ : message), mode === 'compact' ? [] : modelContext.worldState.entries, mode !== 'compact' && selectedModel.inputCapabilities.includes('IMAGE'), baseTools().some((tool) => tool.name === 'code_exec'), permissionPolicy.mode === 'auto')
1142
1216
  };
1143
1217
  if (mode !== 'compact') {
1144
1218
  if (pendingInputs.size)
@@ -1153,7 +1227,7 @@ export function createSessionOpener(services) {
1153
1227
  await sessionRuntime.sessionBackend.enqueue({ kind: 'submit', input: { ...content, delivery: 'idle' } }, call, (request, effective, next) => next(request, effective));
1154
1228
  return {};
1155
1229
  }
1156
- await recordEnvironment();
1230
+ await modelContext.recordEnvironment();
1157
1231
  call.signal.throwIfAborted();
1158
1232
  await writeInputRecord(content, internal.inputSource === 'extension' ? 'extension.input' : 'turn.user', {
1159
1233
  ...(internal.extensionSource ? { source: internal.extensionSource } : {}),
@@ -1325,10 +1399,16 @@ export function createSessionOpener(services) {
1325
1399
  });
1326
1400
  let lastServerContextTokens = lastServerUsage?.contextInputTokens;
1327
1401
  let compactBoundary = messages.length;
1402
+ const invalidateCompactUsage = () => {
1403
+ lastServerContextTokens = undefined;
1404
+ lastServerUsage = undefined;
1405
+ smartContextTokens = undefined;
1406
+ requestReasoningEffort = reasoningEffort;
1407
+ effectiveReasoningEffort = reasoningEffort;
1408
+ };
1328
1409
  const compactLeaf = async (input, signal, prepare, compactionId) => {
1329
1410
  if (messages.length < 2)
1330
1411
  return false;
1331
- let compactSummary = '';
1332
1412
  const cutoff = Math.min(compactBoundary, messages.length);
1333
1413
  const current = messages[cutoff - 1];
1334
1414
  const retainCurrent = mode !== 'compact' && current.role === 'user' && current.contextKind === undefined;
@@ -1338,13 +1418,9 @@ export function createSessionOpener(services) {
1338
1418
  message.contextKind === 'summary'))
1339
1419
  return false;
1340
1420
  const compactHistory = structuredClone(prefix);
1341
- const prefixInputs = compactionUserMessages.filter((entry) => prefix.some((message) => message.role === 'user' &&
1342
- message.contextKind === undefined &&
1343
- (entry.metadata?.recordId
1344
- ? message.metadata?.recordId === entry.metadata.recordId
1345
- : message.content === entry.content)));
1421
+ const prefixInputs = modelContext.compactInputs(prefix);
1346
1422
  const snapshotTools = structuredClone(projectTools(executionTools));
1347
- const generation = contextualWorldState.generation;
1423
+ const generation = modelContext.worldState.generation;
1348
1424
  const snapshotUsage = lastServerUsage;
1349
1425
  const snapshotEffort = requestReasoningEffort ?? reasoningEffort;
1350
1426
  const anchorRecordId = prefix
@@ -1356,29 +1432,6 @@ export function createSessionOpener(services) {
1356
1432
  ...(visibleAnchor ? { anchor: visibleAnchor } : {})
1357
1433
  };
1358
1434
  const compactComposition = splitContextualPrompt(await operation((call) => coreExtensions.composeModelSystem(promptComposition(snapshotTools, 'run'), call)));
1359
- const compactSystemPrompt = compactComposition.system;
1360
- const projectedHistory = normalizeMessagesForModel(compactHistory, {
1361
- supportsImages: false
1362
- }).map(projectSmartCompactionMessage);
1363
- const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
1364
- ...(input.focus === undefined ? {} : { focus: input.focus })
1365
- });
1366
- const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages(''), snapshotTools);
1367
- const compactInputTokens = Math.max(0, selectedModel.contextWindowTokens - compactOverheadTokens);
1368
- let usingNativeMessages = shouldUseNativeCompaction({
1369
- estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeCompactMessages, snapshotTools),
1370
- contextWindowTokens: selectedModel.contextWindowTokens,
1371
- latestUsage: snapshotUsage,
1372
- historyMessages: compactHistory,
1373
- appendedMessages: nativeCompactMessages.slice(projectedHistory.length)
1374
- });
1375
- const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
1376
- ...(input.focus === undefined ? {} : { focus: input.focus }),
1377
- maxTokens: Math.max(1, compactInputTokens)
1378
- }));
1379
- let compactMessages = usingNativeMessages
1380
- ? nativeCompactMessages
1381
- : fallbackMessages();
1382
1435
  await emit({
1383
1436
  type: 'context.compacting',
1384
1437
  compactionId,
@@ -1387,79 +1440,43 @@ export function createSessionOpener(services) {
1387
1440
  ...anchor
1388
1441
  });
1389
1442
  let latestUsage;
1390
- const compactionRetryState = { attempts: 0 };
1443
+ let compactSummary;
1391
1444
  const compaction = createCompactionOperation(compactionId, signal);
1392
1445
  const compactTurn = createModelTurnSession(selectedAdapter);
1393
1446
  try {
1394
- const extensionResult = await generateCompactExtension(compactHistory, mode === 'compact' ? 'manual' : 'automatic', input.focus, compaction);
1395
- if (extensionResult) {
1396
- compactSummary = extensionResult.summary;
1397
- latestUsage = extensionResult.usage;
1398
- if (latestUsage)
1399
- await emit({
1400
- type: 'usage.updated',
1401
- purpose: 'compact',
1402
- ...publicModelUsage(latestUsage)
1403
- });
1404
- }
1405
- while (extensionResult === undefined) {
1406
- if (compactInputTokens === 0)
1407
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
1408
- compactSummary = '';
1409
- latestUsage = undefined;
1410
- try {
1411
- const request = await transformModelRequest({
1412
- retryState: compactionRetryState,
1413
- system: compactSystemPrompt,
1414
- ...(compactComposition.systemBlocks === undefined
1415
- ? {}
1416
- : { systemBlocks: compactComposition.systemBlocks }),
1417
- messages: compactMessages,
1418
- tools: snapshotTools,
1419
- toolChoice: 'none',
1420
- promptCacheKey: sessionId,
1421
- reasoningEffort: snapshotEffort,
1422
- onAttemptDiagnostic: recordModelAttempt('COMPACTION')
1423
- }, undefined, selectedModel.id, 'compact');
1424
- for await (const event of streamModel((signal) => compaction.start({ start: (request) => compactTurn.start(request, signal) }, request), compaction.signal)) {
1425
- if (event.type === 'text.completed' &&
1426
- (event.phase ?? 'final_answer') === 'final_answer')
1427
- compactSummary = event.text.trim();
1428
- else if (event.type === 'usage') {
1429
- latestUsage = event.usage;
1430
- await emit({
1431
- type: 'usage.updated',
1432
- purpose: 'compact',
1433
- ...publicModelUsage(event.usage)
1434
- });
1435
- }
1436
- else if (event.type === 'client_tool.call')
1437
- throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
1438
- }
1439
- break;
1440
- }
1441
- catch (error) {
1442
- if (await recoverModelStream(error, compactionRetryState, compaction.signal)) {
1443
- continue;
1444
- }
1445
- if (isProviderContextOverflow(error) && usingNativeMessages) {
1446
- usingNativeMessages = false;
1447
- compactMessages = fallbackMessages();
1448
- compactionRetryState.attempts = 0;
1449
- continue;
1450
- }
1451
- if (isProviderContextOverflow(error))
1452
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'The compaction request exceeds the model context window.', { cause: error });
1453
- throw error;
1454
- }
1455
- }
1447
+ const candidate = await generateCompactionCandidate({
1448
+ history: compactHistory,
1449
+ system: compactComposition.system,
1450
+ ...(compactComposition.systemBlocks === undefined
1451
+ ? {}
1452
+ : { systemBlocks: compactComposition.systemBlocks }),
1453
+ tools: snapshotTools,
1454
+ contextWindowTokens: selectedModel.contextWindowTokens,
1455
+ latestUsage: snapshotUsage,
1456
+ reasoningEffort: snapshotEffort,
1457
+ ...(input.focus === undefined ? {} : { focus: input.focus }),
1458
+ reason: mode === 'compact' ? 'manual' : 'automatic',
1459
+ promptCacheKey: sessionId
1460
+ }, {
1461
+ operation: compaction,
1462
+ turn: compactTurn,
1463
+ generateExtension: generateCompactExtension,
1464
+ transformRequest: (request) => transformModelRequest(request, undefined, selectedModel.id, 'compact'),
1465
+ stream: streamModel,
1466
+ observeUsage: (usage) => emit({
1467
+ type: 'usage.updated',
1468
+ purpose: 'compact',
1469
+ ...publicModelUsage(usage)
1470
+ }),
1471
+ onAttemptDiagnostic: recordModelAttempt('COMPACTION')
1472
+ });
1473
+ compactSummary = candidate.summary;
1474
+ latestUsage = candidate.usage;
1456
1475
  }
1457
1476
  finally {
1458
1477
  await compaction.dispose();
1459
1478
  await compactTurn.close();
1460
1479
  }
1461
- if (!compactSummary)
1462
- throw new MarAgentError('MAR_AGENT_COMPACT_FAILED', 'Automatic compact failed.');
1463
1480
  await emit({
1464
1481
  type: 'context.compacting',
1465
1482
  compactionId,
@@ -1473,7 +1490,7 @@ export function createSessionOpener(services) {
1473
1490
  const replacementHistory = buildCompactedHistory(compactSummary, prefixInputs, compactUserTokenLimit);
1474
1491
  let retainedModelContext = [];
1475
1492
  const compactRecord = await writeCompactionRecord(compactSummary, true, signal, async (actual, options) => {
1476
- if (contextualWorldState.generation !== generation ||
1493
+ if (modelContext.worldState.generation !== generation ||
1477
1494
  prefix.some((message, index) => messages[index] !== message))
1478
1495
  throw new MarAgentError('MAR_AGENT_COMPACT_STALE', 'The compaction prefix has changed.');
1479
1496
  retainedModelContext = messages
@@ -1508,32 +1525,11 @@ export function createSessionOpener(services) {
1508
1525
  executionId,
1509
1526
  turnId
1510
1527
  }, options);
1511
- messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory), ...retainedModelContext);
1512
- pendingAppends.length = 0;
1513
- selected.resetContinuation?.();
1528
+ modelContext.replaceCompactMessages(replacementHistory, retainedModelContext);
1514
1529
  return record;
1515
1530
  });
1516
- contextGc.reset();
1517
- restoreMcpDiscovery();
1518
- recordedEnvironment = executionEnvironment;
1519
- compactionUserMessages = replacementHistory
1520
- .filter((message) => message.kind !== 'summary')
1521
- .map((message) => ({
1522
- content: message.content,
1523
- ...(message.metadata ? { metadata: { ...message.metadata } } : {})
1524
- }));
1525
- compactionUserMessages.push(...retainedModelContext
1526
- .filter((message) => message.role === 'user' && message.contextKind === undefined)
1527
- .map((message) => ({
1528
- content: message.content,
1529
- ...(message.metadata ? { metadata: message.metadata } : {})
1530
- })));
1531
- await resetCompactWorldState(compactRecord.recordId, { resources: compactResources });
1532
- lastServerContextTokens = undefined;
1533
- lastServerUsage = undefined;
1534
- smartContextTokens = undefined;
1535
- requestReasoningEffort = reasoningEffort;
1536
- effectiveReasoningEffort = reasoningEffort;
1531
+ await modelContext.adoptCompactCheckpoint(compactRecord.recordId, replacementHistory, retainedModelContext, compactResources);
1532
+ invalidateCompactUsage();
1537
1533
  await emit({
1538
1534
  type: 'context.compacted',
1539
1535
  compactionId,
@@ -1679,7 +1675,7 @@ export function createSessionOpener(services) {
1679
1675
  });
1680
1676
  updateMcpRegistry();
1681
1677
  executionTools = mcpRegistry.project(baseTools());
1682
- await syncMcpWorldState(messages.length);
1678
+ await modelContext.syncMcpWorldState(messages.length);
1683
1679
  compactBoundary = messages.length;
1684
1680
  const blocking = compactRequests.drain((input) => startCompact({
1685
1681
  ...(input.prompt === undefined ? {} : { focus: input.prompt }),
@@ -1688,9 +1684,9 @@ export function createSessionOpener(services) {
1688
1684
  if (blocking)
1689
1685
  await compactCoordinator.checkpoint(true);
1690
1686
  await ensureContextBudget();
1691
- await syncMcpWorldState(messages.length);
1687
+ await modelContext.syncMcpWorldState(messages.length);
1692
1688
  executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
1693
- await recordEnvironment();
1689
+ await modelContext.recordEnvironment();
1694
1690
  const { roundTools, roundBindings, toolScope, composeSystemPrompt } = captureRoundToolScope();
1695
1691
  const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_exec');
1696
1692
  const residentTools = projectTools(mcpRegistry.projectDirect(baseTools()));
@@ -1706,7 +1702,7 @@ export function createSessionOpener(services) {
1706
1702
  : [...messages, pendingOutputContinuation];
1707
1703
  sources.bind(toolScope);
1708
1704
  const composedSystem = splitContextualPrompt(await composeSystemPrompt());
1709
- const candidateWorld = updatePromptWorldState(contextualWorldState, messages.length, composedSystem.parts);
1705
+ const candidateWorld = updatePromptWorldState(modelContext.worldState, messages.length, composedSystem.parts);
1710
1706
  const modelMessages = projectSessionRequestMessages(requestContextMessages, candidateWorld.entries, selectedModel.inputCapabilities.includes('IMAGE'), codeModeEnabled, permissionPolicy.mode === 'auto');
1711
1707
  const systemPrompt = composedSystem.system;
1712
1708
  const candidateRequest = {
@@ -1732,9 +1728,9 @@ export function createSessionOpener(services) {
1732
1728
  preparingRequestMessages = undefined;
1733
1729
  }
1734
1730
  controller.signal.throwIfAborted();
1735
- if (candidateWorld !== contextualWorldState) {
1731
+ if (candidateWorld !== modelContext.worldState) {
1736
1732
  // Commit only after request middleware accepts the entire candidate.
1737
- await commitWorldState(candidateWorld);
1733
+ await modelContext.commitWorldState(candidateWorld);
1738
1734
  }
1739
1735
  const lateCoreChange = inputs.hasBoundaryCompactPending && (await appendMailboxMessages());
1740
1736
  const lateBlocking = compactRequests.drain((input) => startCompact({
@@ -1821,28 +1817,7 @@ export function createSessionOpener(services) {
1821
1817
  }
1822
1818
  });
1823
1819
  if (committed) {
1824
- smartContextTokens = undefined;
1825
- selected.resetContinuation?.();
1826
- const refreshed = await restoreMessages(store, sessionId, committed.records, {
1827
- includeImages: true,
1828
- currentTurnId: turnId,
1829
- projection: committed.projection
1830
- });
1831
- const restoredIds = new Set(committed.records.map((record) => record.recordId));
1832
- const lateMessages = pendingAppends
1833
- .slice(appendBoundary)
1834
- .filter((entry) => !restoredIds.has(entry.recordId))
1835
- .map((entry) => entry.message);
1836
- messages.splice(0, messages.length, ...refreshed.messages, ...lateMessages);
1837
- restoreMcpDiscovery();
1838
- recordedEnvironment = refreshed.environmentState;
1839
- compactionUserMessages = [...refreshed.userMessages];
1840
- agentInstructionInsertIndex = contextualInstructionInsertIndex(messages);
1841
- const next = resetContextualWorldState(latestContextualWorldStateGeneration(committed.records), agentInstructionInsertIndex, {
1842
- instructionSnapshot: currentInstructionSnapshot,
1843
- instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined))
1844
- });
1845
- await commitWorldState(next);
1820
+ await modelContext.adoptGcCheckpoint(committed, appendBoundary);
1846
1821
  }
1847
1822
  }
1848
1823
  },
@@ -1886,6 +1861,7 @@ export function createSessionOpener(services) {
1886
1861
  };
1887
1862
  }
1888
1863
  catch (error) {
1864
+ failedExecution = true;
1889
1865
  finalAnswer = executionLoop?.finalAnswer ?? finalAnswer;
1890
1866
  acceptsMailbox = false;
1891
1867
  acceptsSteer = false;
@@ -1897,7 +1873,8 @@ export function createSessionOpener(services) {
1897
1873
  delete executionContext.compactRequests;
1898
1874
  if (controller.signal.aborted) {
1899
1875
  subagents?.endParentExecution(executionId);
1900
- await emit({ type: 'execution.cancelled' });
1876
+ // A faulted Store must not prevent result settlement or lease release.
1877
+ await emit({ type: 'execution.cancelled' }).catch(() => undefined);
1901
1878
  return {
1902
1879
  executionId,
1903
1880
  status: 'cancelled',
@@ -1906,7 +1883,8 @@ export function createSessionOpener(services) {
1906
1883
  }
1907
1884
  const diagnostic = modelAttemptFailureDiagnostic(describeMarAgentError(error, 'MAR_AGENT_EXECUTION_FAILED'), exhaustedModelAttempt);
1908
1885
  subagents?.endParentExecution(executionId);
1909
- await emit({ type: 'execution.failed', ...diagnostic });
1886
+ // Keep the original diagnostic if persisting the failure terminal also fails.
1887
+ await emit({ type: 'execution.failed', ...diagnostic }).catch(() => undefined);
1910
1888
  return {
1911
1889
  executionId,
1912
1890
  status: 'failed',
@@ -1940,12 +1918,21 @@ export function createSessionOpener(services) {
1940
1918
  activeExecutionAdapters.delete(adapter);
1941
1919
  }
1942
1920
  subagents?.endParentExecution(executionId);
1943
- await releaseSession?.();
1944
- if (sessionRuntime.execution === executionContext)
1945
- delete sessionRuntime.execution;
1946
- active.delete(`${sessionId}:${executionId}`);
1947
- sessionRuntime.sessionBackend?.kick();
1948
- queue.close();
1921
+ try {
1922
+ await releaseSession?.();
1923
+ }
1924
+ catch (error) {
1925
+ // Preserve the first failure/cancellation; successful executions still report release errors.
1926
+ if (!failedExecution)
1927
+ throw error;
1928
+ }
1929
+ finally {
1930
+ if (sessionRuntime.execution === executionContext)
1931
+ delete sessionRuntime.execution;
1932
+ active.delete(`${sessionId}:${executionId}`);
1933
+ sessionRuntime.sessionBackend?.kick();
1934
+ queue.close();
1935
+ }
1949
1936
  }
1950
1937
  });
1951
1938
  const executionKey = `${sessionId}:${executionId}`;
@@ -2169,6 +2156,7 @@ export function createSessionOpener(services) {
2169
2156
  sessionRuntime.agentsBackend ??= createExtensionAgentsBackend({
2170
2157
  existingController: () => sessionRuntime.subagents,
2171
2158
  controller: async () => {
2159
+ // Access to managed children is not a new Execution admission for their parent.
2172
2160
  services.assertCanStart();
2173
2161
  if (sessionRuntime.subagents === undefined)
2174
2162
  await openSession(sessionId);
@@ -2219,11 +2207,17 @@ export function createSessionOpener(services) {
2219
2207
  run: (input) => execution('run', input),
2220
2208
  plan: (input) => execution('plan', input),
2221
2209
  steer: (input) => sessionRuntime.execution?.steer?.(input) ?? Promise.resolve({ status: 'notAccepted' }),
2222
- compact: (input = {}) => sessionRuntime.execution?.compactRequests?.request(input) ??
2223
- execution('compact', {
2210
+ compact: (input = {}) => {
2211
+ const requests = sessionRuntime.execution?.compactRequests;
2212
+ if (requests) {
2213
+ services.assertSessionAdmission(sessionId);
2214
+ return requests.request(input);
2215
+ }
2216
+ return execution('compact', {
2224
2217
  prompt: input.prompt ?? DEFAULT_COMPACTION_FOCUS,
2225
2218
  ...input
2226
- }),
2219
+ });
2220
+ },
2227
2221
  cancel: async (executionId) => active.get(`${sessionId}:${executionId}`)?.abort(),
2228
2222
  startSubagentTurn: (turn) => execution(turn.role === 'explore' ? 'plan' : 'run', {
2229
2223
  prompt: turn.prompt,