@myagentroam/agent 0.10.29 → 0.10.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-core/extensions/session-input.d.ts +2 -0
- package/dist/agent-core/extensions/session-input.js +21 -3
- package/dist/agent-core/model/contracts.d.ts +1 -0
- package/dist/agent-core/model/openai-responses.js +6 -2
- package/dist/agent-core/runtime/compaction/coordinator.d.ts +15 -0
- package/dist/agent-core/runtime/compaction/coordinator.js +88 -0
- package/dist/agent-core/runtime/compaction/requests.d.ts +16 -0
- package/dist/agent-core/runtime/compaction/requests.js +114 -0
- package/dist/agent-core/runtime/compaction/retained-context.d.ts +6 -0
- package/dist/agent-core/runtime/compaction/retained-context.js +52 -0
- package/dist/agent-core/runtime/compaction/smart.d.ts +14 -0
- package/dist/agent-core/runtime/compaction/smart.js +59 -0
- package/dist/agent-core/runtime/context/restore.d.ts +1 -0
- package/dist/agent-core/runtime/context/restore.js +14 -4
- package/dist/agent-core/runtime/context/usage.js +3 -1
- package/dist/agent-core/runtime/event-projection.js +1 -1
- package/dist/agent-core/runtime/events.d.ts +21 -0
- package/dist/agent-core/runtime/execution-policy.d.ts +2 -0
- package/dist/agent-core/runtime/execution-policy.js +2 -0
- package/dist/agent-core/runtime/execution.d.ts +1 -0
- package/dist/agent-core/runtime/execution.js +1 -0
- package/dist/agent-core/sdk/extension-session.js +4 -2
- package/dist/agent-core/sdk/session.d.ts +2 -0
- package/dist/agent-core/sdk/session.js +350 -192
- package/dist/agent-core/sdk/types.d.ts +4 -1
- package/dist/agent-core/session/catalog.js +2 -2
- package/dist/agent-core/session/jsonl-store.d.ts +2 -0
- package/dist/agent-core/session/jsonl-store.js +9 -1
- package/dist/agent-core/tools/agent/smart-compact.d.ts +9 -0
- package/dist/agent-core/tools/agent/smart-compact.js +20 -0
- package/dist/agent-core/tools/registry.js +4 -1
- package/dist/agent-core/tools/runtime-context.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { isProviderContextOverflow, recoverModelStream } from '../model/recovery
|
|
|
2
2
|
import { estimatedRequestTokens } from '../runtime/context/request-budget.js';
|
|
3
3
|
import { restoreMessages } from '../runtime/context/restore.js';
|
|
4
4
|
import { lastConfigurationEffort, latestModelUsage, reasoningEffortBaseline, shouldUseNativeCompaction } from '../runtime/context/usage.js';
|
|
5
|
-
import { splitContextualPrompt
|
|
5
|
+
import { splitContextualPrompt } from '../runtime/context/contextual-blocks.js';
|
|
6
6
|
import { contextualInstructionInsertIndex, insertContextualWorldState, latestContextualWorldStateGeneration, parsePersistedContextualWorldState, persistContextualWorldState, resetContextualWorldState, updateContextualWorldState, updatePromptWorldState } from '../runtime/context/world-state.js';
|
|
7
7
|
import { eventForPersistence, publicModelUsage } from '../runtime/event-projection.js';
|
|
8
8
|
import { ExecutionLoop } from '../runtime/execution.js';
|
|
@@ -32,12 +32,16 @@ import { retainedSessionResourcesSnapshot } from '../prompts/resources.js';
|
|
|
32
32
|
import { childSubagentInstruction } from '../prompts/subagent.js';
|
|
33
33
|
import { AsyncQueue } from '../runtime/async-queue.js';
|
|
34
34
|
import { CompactionOperation } from '../runtime/compaction/operation.js';
|
|
35
|
+
import { CompactCoordinator } from '../runtime/compaction/coordinator.js';
|
|
36
|
+
import { CompactRequests } from '../runtime/compaction/requests.js';
|
|
37
|
+
import { compactSnapshotAnchor } from '../runtime/compaction/retained-context.js';
|
|
35
38
|
import { DEFAULT_COMPACTION_FOCUS, buildCompactedHistory, buildCompactionInput, buildCompactionMessages, buildNativeCompactionMessages, compactedHistoryMessages } from '../runtime/compaction/history.js';
|
|
36
39
|
import { currentLocalTimeContext, environmentContextUpdate, restoreEnvironmentState } from '../runtime/context/environment.js';
|
|
37
40
|
import { ContextGcRuntime } from '../runtime/context/gc-runtime.js';
|
|
38
41
|
import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
|
|
39
42
|
import { loadAgentInstructions, renderAgentInstructions } from '../runtime/context/instructions.js';
|
|
40
43
|
import { normalizeMessagesForModel } from '../runtime/context/normalize.js';
|
|
44
|
+
import { latestSmartContextTokens, projectSmartCompactionMessage, requestSmartCompaction } from '../runtime/compaction/smart.js';
|
|
41
45
|
import { compactSummaryFromCheckpoint, inputMetadataFromRecord } from '../session/jsonl-store.js';
|
|
42
46
|
import { SubagentSessionController } from '../subagent/session-controller.js';
|
|
43
47
|
import { agentStartToolDefinitionWithModels } from '../tools/agent/agent-start.js';
|
|
@@ -197,6 +201,11 @@ export function createSessionOpener(services) {
|
|
|
197
201
|
executionModels.get(input.modelId ?? executionDefaultModelId)?.conversationModel === false)
|
|
198
202
|
throw new MarAgentError('MAR_AGENT_MODEL_DISABLED', 'The selected model is not a conversation model.');
|
|
199
203
|
const executionId = randomUUID(), turnId = internal.turnId ?? randomUUID(), queue = new AsyncQueue(), controller = new AbortController();
|
|
204
|
+
const compactCoordinator = new CompactCoordinator();
|
|
205
|
+
const messageSources = new WeakMap();
|
|
206
|
+
let liveModelMessages = [];
|
|
207
|
+
const compactRequests = new CompactRequests(executionId, () => compactCoordinator.assertNotReentrant());
|
|
208
|
+
sessionRuntime.compactRequests = compactRequests;
|
|
200
209
|
const permissionPolicy = new ExecutionToolPermissions(mode === 'compact' ? 'full-access' : resolvePermissionMode(input.permissionMode), { sessionId, executionId }, host.requestToolApproval
|
|
201
210
|
? {
|
|
202
211
|
request: (request, signal) => host.requestToolApproval(request, signal),
|
|
@@ -265,7 +274,6 @@ export function createSessionOpener(services) {
|
|
|
265
274
|
acceptParent(message);
|
|
266
275
|
let acceptsMailbox = sessionSource.type === 'subagent' && mode !== 'compact';
|
|
267
276
|
let sequence = 0;
|
|
268
|
-
let manualCompactionStarted = false;
|
|
269
277
|
let emitTail = Promise.resolve();
|
|
270
278
|
let releaseSession;
|
|
271
279
|
let exhaustedModelAttempt;
|
|
@@ -284,14 +292,21 @@ export function createSessionOpener(services) {
|
|
|
284
292
|
const operation = emitTail.then(async () => {
|
|
285
293
|
assertLive?.();
|
|
286
294
|
const persistent = eventForPersistence(full);
|
|
287
|
-
if (persistent !== undefined && releaseSession !== undefined)
|
|
288
|
-
await store.append(sessionId, {
|
|
295
|
+
if (persistent !== undefined && releaseSession !== undefined) {
|
|
296
|
+
const stored = await store.append(sessionId, {
|
|
289
297
|
type: full.type,
|
|
290
298
|
payload: persistent,
|
|
291
299
|
executionId,
|
|
292
300
|
turnId
|
|
293
301
|
});
|
|
302
|
+
if (full.type === 'message.completed') {
|
|
303
|
+
const message = liveModelMessages.findLast((item) => item.role === 'assistant' && item.content === full.text);
|
|
304
|
+
if (message)
|
|
305
|
+
messageSources.set(message, stored);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
294
308
|
queue.push(full);
|
|
309
|
+
compactRequests.publish(full);
|
|
295
310
|
if (internal.publishHostEvents !== false)
|
|
296
311
|
await host.emit(full);
|
|
297
312
|
});
|
|
@@ -337,7 +352,6 @@ export function createSessionOpener(services) {
|
|
|
337
352
|
const result = withToolPermissions(permissionPolicy, async () => {
|
|
338
353
|
let finalAnswer = '';
|
|
339
354
|
let executionLoop;
|
|
340
|
-
let manualCompaction;
|
|
341
355
|
let executionToolSources;
|
|
342
356
|
let selectedTurn;
|
|
343
357
|
const contextGc = new ContextGcRuntime();
|
|
@@ -392,9 +406,11 @@ export function createSessionOpener(services) {
|
|
|
392
406
|
}
|
|
393
407
|
const compactMayContainAgentInstructions = contextRecords.some((record) => compactSummaryFromCheckpoint(record) !== undefined);
|
|
394
408
|
const restored = await restoreMessages(store, sessionId, contextRecords, {
|
|
409
|
+
messageSources,
|
|
395
410
|
includeImages: mode !== 'compact'
|
|
396
411
|
});
|
|
397
412
|
const messages = restored.messages;
|
|
413
|
+
liveModelMessages = messages;
|
|
398
414
|
const restoredRecordIds = new Set(contextRecords.map((record) => record.recordId));
|
|
399
415
|
for (const pending of pendingAppends)
|
|
400
416
|
if (!restoredRecordIds.has(pending.recordId))
|
|
@@ -402,13 +418,25 @@ export function createSessionOpener(services) {
|
|
|
402
418
|
pendingAppends.length = 0;
|
|
403
419
|
immediateTarget = messages;
|
|
404
420
|
let recordedEnvironment = restored.environmentState;
|
|
421
|
+
let smartInput;
|
|
405
422
|
let compactContext;
|
|
423
|
+
let startCompact;
|
|
406
424
|
const writeInputRecord = async (content, type, metadata, call, committed) => {
|
|
407
425
|
call.signal.throwIfAborted();
|
|
408
426
|
if (content.images?.length && !selectedModel.inputCapabilities.includes('IMAGE'))
|
|
409
427
|
throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
|
|
410
428
|
await withRecordProjection(async () => {
|
|
411
429
|
call.signal.throwIfAborted();
|
|
430
|
+
const smartPercent = smartContextTokens === undefined
|
|
431
|
+
? undefined
|
|
432
|
+
: (smartContextTokens * 100) / selectedModel.contextWindowTokens;
|
|
433
|
+
const smartCompaction = mode !== 'compact' &&
|
|
434
|
+
sessionSource.type === 'user' &&
|
|
435
|
+
type === 'turn.user' &&
|
|
436
|
+
metadata.inputSource === undefined &&
|
|
437
|
+
smartPercent !== undefined &&
|
|
438
|
+
smartPercent >= AGENT_EXECUTION_POLICY.smartCompactionPercent &&
|
|
439
|
+
smartPercent < AGENT_EXECUTION_POLICY.automaticCompactionPercent;
|
|
412
440
|
const images = content.images?.length
|
|
413
441
|
? await store.persistInputImages(sessionId, content.images)
|
|
414
442
|
: [];
|
|
@@ -417,6 +445,7 @@ export function createSessionOpener(services) {
|
|
|
417
445
|
payload: {
|
|
418
446
|
...metadata,
|
|
419
447
|
prompt: content.text,
|
|
448
|
+
...(smartCompaction ? { smartCompaction: true } : {}),
|
|
420
449
|
...(content.modelText === undefined ? {} : { modelText: content.modelText }),
|
|
421
450
|
...(content.context === undefined ? {} : { context: content.context }),
|
|
422
451
|
...(content.modelContext === undefined
|
|
@@ -428,6 +457,11 @@ export function createSessionOpener(services) {
|
|
|
428
457
|
turnId
|
|
429
458
|
}, { signal: call.signal });
|
|
430
459
|
const inputMetadata = inputMetadataFromRecord(record);
|
|
460
|
+
smartInput = {
|
|
461
|
+
recordId: record.recordId,
|
|
462
|
+
eligible: smartCompaction,
|
|
463
|
+
requested: false
|
|
464
|
+
};
|
|
431
465
|
compactionUserMessages.push({
|
|
432
466
|
content: content.modelText ?? content.text,
|
|
433
467
|
metadata: inputMetadata
|
|
@@ -439,8 +473,14 @@ export function createSessionOpener(services) {
|
|
|
439
473
|
};
|
|
440
474
|
const appendMailboxMessages = async () => inputs.drain(async (entry, request, call) => {
|
|
441
475
|
call.signal.throwIfAborted();
|
|
442
|
-
if (request.kind === 'compact')
|
|
476
|
+
if (request.kind === 'compact') {
|
|
477
|
+
if (request.input.async) {
|
|
478
|
+
const flight = startCompact(request.input);
|
|
479
|
+
entry.start();
|
|
480
|
+
return { changed: await flight.result, executionId };
|
|
481
|
+
}
|
|
443
482
|
return { changed: await compactContext(request.input), executionId };
|
|
483
|
+
}
|
|
444
484
|
if (request.kind === 'append') {
|
|
445
485
|
const result = await appendExtensionMessage(entry, request, call);
|
|
446
486
|
return { ...result, executionId };
|
|
@@ -527,6 +567,7 @@ export function createSessionOpener(services) {
|
|
|
527
567
|
const selected = createModelTurnSession(selectedAdapter);
|
|
528
568
|
selectedTurn = selected;
|
|
529
569
|
let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
|
|
570
|
+
let smartContextTokens = latestSmartContextTokens(contextRecords, selectedModel.id, selectedModel.contextWindowTokens);
|
|
530
571
|
const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
|
|
531
572
|
const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
|
|
532
573
|
const useEffortUpdates = usesResponsesConfigurationUpdates(selectedModel);
|
|
@@ -765,14 +806,31 @@ export function createSessionOpener(services) {
|
|
|
765
806
|
contextualWorldState = next;
|
|
766
807
|
sessionRuntime.contextualWorldState = next;
|
|
767
808
|
};
|
|
768
|
-
const resetCompactWorldState = async (generation) => {
|
|
809
|
+
const resetCompactWorldState = async (generation, committed) => {
|
|
769
810
|
agentInstructionInsertIndex = contextualInstructionInsertIndex(messages);
|
|
770
811
|
const next = resetContextualWorldState(generation, agentInstructionInsertIndex, {
|
|
771
812
|
instructionSnapshot: currentInstructionSnapshot,
|
|
772
813
|
instructionContent: renderAgentInstructions(instructionEntries, true),
|
|
773
|
-
resourceSnapshot:
|
|
814
|
+
resourceSnapshot: committed
|
|
815
|
+
? committed.resources
|
|
816
|
+
: retainedSessionResourcesSnapshot(await loadRetainedSessionResources())
|
|
774
817
|
});
|
|
775
|
-
|
|
818
|
+
if (!committed)
|
|
819
|
+
await commitWorldState(next);
|
|
820
|
+
else {
|
|
821
|
+
// The checkpoint is authoritative; its sidecar is recoverable on restart.
|
|
822
|
+
contextualWorldState = next;
|
|
823
|
+
sessionRuntime.contextualWorldState = next;
|
|
824
|
+
try {
|
|
825
|
+
await persistContextualWorldState(store, sessionId, next);
|
|
826
|
+
}
|
|
827
|
+
catch {
|
|
828
|
+
coreExtensions.diagnose({
|
|
829
|
+
code: 'MAR_AGENT_COMPACT_SIDECAR_PERSIST_FAILED',
|
|
830
|
+
sessionId
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
}
|
|
776
834
|
};
|
|
777
835
|
// A missing sidecar is rebuilt only for the latest Compact, never for a GC boundary.
|
|
778
836
|
if (!contextualWorldState.initialized &&
|
|
@@ -848,8 +906,24 @@ export function createSessionOpener(services) {
|
|
|
848
906
|
const parentFrame = subagents === undefined || mode === 'compact'
|
|
849
907
|
? undefined
|
|
850
908
|
: subagents.captureParentFrame(executionId);
|
|
909
|
+
const roundSmartInput = smartInput;
|
|
851
910
|
const toolContext = {
|
|
852
911
|
host: host,
|
|
912
|
+
requestSmartCompact: () => {
|
|
913
|
+
controller.signal.throwIfAborted();
|
|
914
|
+
requestSmartCompaction(roundSmartInput === smartInput ? roundSmartInput : undefined, () => {
|
|
915
|
+
// The Session owns this background operation, not the short-lived tool callback.
|
|
916
|
+
services.extensions.operations.enter({
|
|
917
|
+
sessionId,
|
|
918
|
+
sessionEpoch: coreExtensions.epoch,
|
|
919
|
+
executionId,
|
|
920
|
+
turnId,
|
|
921
|
+
actor: 'sdk',
|
|
922
|
+
entry: 'native-api',
|
|
923
|
+
signal: controller.signal
|
|
924
|
+
}, () => startCompact({ async: true }));
|
|
925
|
+
});
|
|
926
|
+
},
|
|
853
927
|
permissionPolicy,
|
|
854
928
|
writeToolResultResource: (text) => store.writeToolResultResource(sessionId, text),
|
|
855
929
|
todo,
|
|
@@ -1062,8 +1136,6 @@ export function createSessionOpener(services) {
|
|
|
1062
1136
|
const initialSystemPrompt = mode === 'compact'
|
|
1063
1137
|
? buildExecutionSystemPrompt(executionTools, 'run')
|
|
1064
1138
|
: buildExecutionSystemPrompt();
|
|
1065
|
-
let useManualCompactionFallback;
|
|
1066
|
-
const manualAppendBoundary = pendingAppends.length;
|
|
1067
1139
|
const generateCompactExtension = async (history, reason, focus, compaction) => {
|
|
1068
1140
|
if (!coreExtensions.hooks.hasInterceptors('compact.generate'))
|
|
1069
1141
|
return undefined;
|
|
@@ -1114,50 +1186,6 @@ export function createSessionOpener(services) {
|
|
|
1114
1186
|
acceptsDiagnostics = false;
|
|
1115
1187
|
}
|
|
1116
1188
|
};
|
|
1117
|
-
let manualExtension;
|
|
1118
|
-
if (mode === 'compact' && coreExtensions.hooks.hasInterceptors('compact.generate')) {
|
|
1119
|
-
manualCompactionStarted = true;
|
|
1120
|
-
manualCompaction = createCompactionOperation(manualCompactionId, compactSignal);
|
|
1121
|
-
await emit({ type: 'context.compacting', compactionId: manualCompactionId });
|
|
1122
|
-
manualExtension = await generateCompactExtension(messages, 'manual', compactFocus, manualCompaction);
|
|
1123
|
-
}
|
|
1124
|
-
if (mode === 'compact' && manualExtension === undefined) {
|
|
1125
|
-
preparedCompactPrompt = await operation((call) => coreExtensions.composeModelSystem(promptComposition(executionTools, 'run'), call));
|
|
1126
|
-
const compactComposition = splitContextualPrompt(preparedCompactPrompt);
|
|
1127
|
-
const compactSystemPrompt = compactComposition.system;
|
|
1128
|
-
const projectCompact = (input) => projectCompactPromptMessages(input, compactComposition.parts, contextualWorldState.promptSnapshot ?? []);
|
|
1129
|
-
const compactHistory = [...messages];
|
|
1130
|
-
const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries), { supportsImages: false });
|
|
1131
|
-
const nativeMessages = buildNativeCompactionMessages(projectedHistory, {
|
|
1132
|
-
focus: compactFocus
|
|
1133
|
-
});
|
|
1134
|
-
const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, projectCompact(buildCompactionMessages('')), executionTools);
|
|
1135
|
-
const compactInputTokens = selectedModel.contextWindowTokens - compactOverheadTokens;
|
|
1136
|
-
if (compactInputTokens <= 0)
|
|
1137
|
-
throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
|
|
1138
|
-
const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
|
|
1139
|
-
focus: compactFocus,
|
|
1140
|
-
maxTokens: compactInputTokens
|
|
1141
|
-
}));
|
|
1142
|
-
const useNative = shouldUseNativeCompaction({
|
|
1143
|
-
estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, projectCompact(nativeMessages), executionTools),
|
|
1144
|
-
contextWindowTokens: selectedModel.contextWindowTokens,
|
|
1145
|
-
latestUsage: lastServerUsage,
|
|
1146
|
-
historyMessages: compactHistory,
|
|
1147
|
-
appendedMessages: projectCompact(nativeMessages).slice(projectedHistory.length)
|
|
1148
|
-
});
|
|
1149
|
-
messages.splice(0, messages.length, ...(useNative ? nativeMessages : fallbackMessages()));
|
|
1150
|
-
if (useNative) {
|
|
1151
|
-
let available = true;
|
|
1152
|
-
useManualCompactionFallback = () => {
|
|
1153
|
-
if (!available)
|
|
1154
|
-
return false;
|
|
1155
|
-
available = false;
|
|
1156
|
-
messages.splice(0, messages.length, ...fallbackMessages(), ...pendingAppends.slice(manualAppendBoundary).map((entry) => entry.message));
|
|
1157
|
-
return true;
|
|
1158
|
-
};
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
1189
|
await store.append(sessionId, {
|
|
1162
1190
|
type: 'execution.header',
|
|
1163
1191
|
payload: {
|
|
@@ -1173,29 +1201,53 @@ export function createSessionOpener(services) {
|
|
|
1173
1201
|
turnId
|
|
1174
1202
|
});
|
|
1175
1203
|
let lastServerContextTokens = lastServerUsage?.contextInputTokens;
|
|
1176
|
-
|
|
1204
|
+
let compactBoundary = messages.length;
|
|
1205
|
+
const compactLeaf = async (input, signal, prepare, compactionId) => {
|
|
1177
1206
|
if (messages.length < 2)
|
|
1178
1207
|
return false;
|
|
1179
1208
|
let compactSummary = '';
|
|
1180
|
-
const
|
|
1181
|
-
const
|
|
1182
|
-
const
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1209
|
+
const cutoff = Math.min(compactBoundary, messages.length);
|
|
1210
|
+
const current = messages[cutoff - 1];
|
|
1211
|
+
const retainCurrent = mode !== 'compact' && current.role === 'user' && current.contextKind === undefined;
|
|
1212
|
+
const prefix = messages.slice(0, cutoff - (retainCurrent ? 1 : 0));
|
|
1213
|
+
if (!prefix.some((message) => message.role !== 'user' ||
|
|
1214
|
+
message.contextKind === undefined ||
|
|
1215
|
+
message.contextKind === 'summary'))
|
|
1216
|
+
return false;
|
|
1217
|
+
const compactHistory = structuredClone(prefix);
|
|
1218
|
+
const prefixInputs = compactionUserMessages.filter((entry) => prefix.some((message) => message.role === 'user' &&
|
|
1219
|
+
message.contextKind === undefined &&
|
|
1220
|
+
(entry.metadata?.recordId
|
|
1221
|
+
? message.metadata?.recordId === entry.metadata.recordId
|
|
1222
|
+
: message.content === entry.content)));
|
|
1223
|
+
const snapshotTools = structuredClone(projectTools(executionTools));
|
|
1224
|
+
const generation = contextualWorldState.generation;
|
|
1225
|
+
const snapshotUsage = lastServerUsage;
|
|
1226
|
+
const snapshotEffort = requestReasoningEffort ?? reasoningEffort;
|
|
1227
|
+
const anchorRecordId = prefix
|
|
1228
|
+
.map((message) => messageSources.get(message)?.recordId ?? message.metadata?.recordId)
|
|
1229
|
+
.findLast((id) => id !== undefined);
|
|
1230
|
+
const visibleAnchor = compactSnapshotAnchor(prefix, messageSources);
|
|
1231
|
+
const anchor = {
|
|
1232
|
+
...(anchorRecordId ? { anchorRecordId } : {}),
|
|
1233
|
+
...(visibleAnchor ? { anchor: visibleAnchor } : {})
|
|
1234
|
+
};
|
|
1235
|
+
const compactComposition = splitContextualPrompt(await operation((call) => coreExtensions.composeModelSystem(promptComposition(snapshotTools, 'run'), call)));
|
|
1185
1236
|
const compactSystemPrompt = compactComposition.system;
|
|
1186
|
-
const
|
|
1187
|
-
|
|
1237
|
+
const projectedHistory = normalizeMessagesForModel(compactHistory, {
|
|
1238
|
+
supportsImages: false
|
|
1239
|
+
}).map(projectSmartCompactionMessage);
|
|
1188
1240
|
const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
|
|
1189
1241
|
...(input.focus === undefined ? {} : { focus: input.focus })
|
|
1190
1242
|
});
|
|
1191
|
-
const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt,
|
|
1243
|
+
const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages(''), snapshotTools);
|
|
1192
1244
|
const compactInputTokens = Math.max(0, selectedModel.contextWindowTokens - compactOverheadTokens);
|
|
1193
1245
|
let usingNativeMessages = shouldUseNativeCompaction({
|
|
1194
|
-
estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt,
|
|
1246
|
+
estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeCompactMessages, snapshotTools),
|
|
1195
1247
|
contextWindowTokens: selectedModel.contextWindowTokens,
|
|
1196
|
-
latestUsage:
|
|
1248
|
+
latestUsage: snapshotUsage,
|
|
1197
1249
|
historyMessages: compactHistory,
|
|
1198
|
-
appendedMessages:
|
|
1250
|
+
appendedMessages: nativeCompactMessages.slice(projectedHistory.length)
|
|
1199
1251
|
});
|
|
1200
1252
|
const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
|
|
1201
1253
|
...(input.focus === undefined ? {} : { focus: input.focus }),
|
|
@@ -1204,18 +1256,28 @@ export function createSessionOpener(services) {
|
|
|
1204
1256
|
let compactMessages = usingNativeMessages
|
|
1205
1257
|
? nativeCompactMessages
|
|
1206
1258
|
: fallbackMessages();
|
|
1207
|
-
|
|
1208
|
-
|
|
1259
|
+
await emit({
|
|
1260
|
+
type: 'context.compacting',
|
|
1261
|
+
compactionId,
|
|
1262
|
+
async: input.async === true,
|
|
1263
|
+
stage: 'GENERATING',
|
|
1264
|
+
...anchor
|
|
1265
|
+
});
|
|
1209
1266
|
let latestUsage;
|
|
1210
1267
|
const compactionRetryState = { attempts: 0 };
|
|
1211
1268
|
const compaction = createCompactionOperation(compactionId, signal);
|
|
1269
|
+
const compactTurn = createModelTurnSession(selectedAdapter);
|
|
1212
1270
|
try {
|
|
1213
|
-
const extensionResult = await generateCompactExtension(compactHistory, 'automatic', input.focus, compaction);
|
|
1271
|
+
const extensionResult = await generateCompactExtension(compactHistory, mode === 'compact' ? 'manual' : 'automatic', input.focus, compaction);
|
|
1214
1272
|
if (extensionResult) {
|
|
1215
1273
|
compactSummary = extensionResult.summary;
|
|
1216
1274
|
latestUsage = extensionResult.usage;
|
|
1217
1275
|
if (latestUsage)
|
|
1218
|
-
await emit({
|
|
1276
|
+
await emit({
|
|
1277
|
+
type: 'usage.updated',
|
|
1278
|
+
purpose: 'compact',
|
|
1279
|
+
...publicModelUsage(latestUsage)
|
|
1280
|
+
});
|
|
1219
1281
|
}
|
|
1220
1282
|
while (extensionResult === undefined) {
|
|
1221
1283
|
if (compactInputTokens === 0)
|
|
@@ -1229,20 +1291,24 @@ export function createSessionOpener(services) {
|
|
|
1229
1291
|
...(compactComposition.systemBlocks === undefined
|
|
1230
1292
|
? {}
|
|
1231
1293
|
: { systemBlocks: compactComposition.systemBlocks }),
|
|
1232
|
-
messages:
|
|
1233
|
-
tools:
|
|
1294
|
+
messages: compactMessages,
|
|
1295
|
+
tools: snapshotTools,
|
|
1234
1296
|
toolChoice: 'none',
|
|
1235
1297
|
promptCacheKey: sessionId,
|
|
1236
|
-
reasoningEffort:
|
|
1298
|
+
reasoningEffort: snapshotEffort,
|
|
1237
1299
|
onAttemptDiagnostic: recordModelAttempt('COMPACTION')
|
|
1238
1300
|
}, undefined, selectedModel.id, 'compact');
|
|
1239
|
-
for await (const event of streamModel((signal) => compaction.start({ start: (request) =>
|
|
1301
|
+
for await (const event of streamModel((signal) => compaction.start({ start: (request) => compactTurn.start(request, signal) }, request), compaction.signal)) {
|
|
1240
1302
|
if (event.type === 'text.completed' &&
|
|
1241
1303
|
(event.phase ?? 'final_answer') === 'final_answer')
|
|
1242
1304
|
compactSummary = event.text.trim();
|
|
1243
1305
|
else if (event.type === 'usage') {
|
|
1244
1306
|
latestUsage = event.usage;
|
|
1245
|
-
await emit({
|
|
1307
|
+
await emit({
|
|
1308
|
+
type: 'usage.updated',
|
|
1309
|
+
purpose: 'compact',
|
|
1310
|
+
...publicModelUsage(event.usage)
|
|
1311
|
+
});
|
|
1246
1312
|
}
|
|
1247
1313
|
else if (event.type === 'client_tool.call')
|
|
1248
1314
|
throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
|
|
@@ -1267,32 +1333,36 @@ export function createSessionOpener(services) {
|
|
|
1267
1333
|
}
|
|
1268
1334
|
finally {
|
|
1269
1335
|
await compaction.dispose();
|
|
1336
|
+
await compactTurn.close();
|
|
1270
1337
|
}
|
|
1271
1338
|
if (!compactSummary)
|
|
1272
1339
|
throw new MarAgentError('MAR_AGENT_COMPACT_FAILED', 'Automatic compact failed.');
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1340
|
+
await emit({
|
|
1341
|
+
type: 'context.compacting',
|
|
1342
|
+
compactionId,
|
|
1343
|
+
async: !compactCoordinator.waiting && input.async === true,
|
|
1344
|
+
stage: compactCoordinator.waiting ? 'WAITING' : 'READY',
|
|
1345
|
+
...anchor
|
|
1346
|
+
});
|
|
1347
|
+
await prepare();
|
|
1279
1348
|
signal.throwIfAborted();
|
|
1349
|
+
const compactResources = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
|
|
1350
|
+
const replacementHistory = buildCompactedHistory(compactSummary, prefixInputs, compactUserTokenLimit);
|
|
1351
|
+
let retainedModelContext = [];
|
|
1280
1352
|
const compactRecord = await writeCompactionRecord(compactSummary, true, signal, async (actual, options) => {
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
: undefined);
|
|
1288
|
-
const retainedModelContext = pendingAppends
|
|
1289
|
-
.slice(appendBoundary)
|
|
1290
|
-
.map((entry) => entry.message);
|
|
1353
|
+
if (contextualWorldState.generation !== generation ||
|
|
1354
|
+
prefix.some((message, index) => messages[index] !== message))
|
|
1355
|
+
throw new MarAgentError('MAR_AGENT_COMPACT_STALE', 'The compaction prefix has changed.');
|
|
1356
|
+
retainedModelContext = messages
|
|
1357
|
+
.slice(prefix.length)
|
|
1358
|
+
.filter((message) => message.contextKind !== 'private' || message.role === 'provider');
|
|
1291
1359
|
const record = await store.compact(sessionId, {
|
|
1292
|
-
summary:
|
|
1360
|
+
summary: actual,
|
|
1293
1361
|
replacementHistory,
|
|
1294
1362
|
retainedModelContext,
|
|
1295
|
-
|
|
1363
|
+
snapshotMessageCount: prefix.length,
|
|
1364
|
+
...(anchorRecordId ? { snapshotRecordId: anchorRecordId } : {}),
|
|
1365
|
+
automatic: mode !== 'compact',
|
|
1296
1366
|
environment: executionEnvironment,
|
|
1297
1367
|
modelId: selectedModel.id,
|
|
1298
1368
|
contextWindowTokens: selectedModel.contextWindowTokens,
|
|
@@ -1314,30 +1384,119 @@ export function createSessionOpener(services) {
|
|
|
1314
1384
|
content: message.content,
|
|
1315
1385
|
...(message.metadata ? { metadata: { ...message.metadata } } : {})
|
|
1316
1386
|
}));
|
|
1317
|
-
|
|
1387
|
+
compactionUserMessages.push(...retainedModelContext
|
|
1388
|
+
.filter((message) => message.role === 'user' && message.contextKind === undefined)
|
|
1389
|
+
.map((message) => ({
|
|
1390
|
+
content: message.content,
|
|
1391
|
+
...(message.metadata ? { metadata: message.metadata } : {})
|
|
1392
|
+
})));
|
|
1393
|
+
await resetCompactWorldState(compactRecord.recordId, { resources: compactResources });
|
|
1318
1394
|
lastServerContextTokens = undefined;
|
|
1319
1395
|
lastServerUsage = undefined;
|
|
1396
|
+
smartContextTokens = undefined;
|
|
1320
1397
|
requestReasoningEffort = reasoningEffort;
|
|
1321
1398
|
effectiveReasoningEffort = reasoningEffort;
|
|
1322
|
-
await emit({
|
|
1399
|
+
await emit({
|
|
1400
|
+
type: 'context.compacted',
|
|
1401
|
+
compactionId,
|
|
1402
|
+
async: input.async === true,
|
|
1403
|
+
retainedContext: retainedModelContext.length > 0,
|
|
1404
|
+
...anchor
|
|
1405
|
+
});
|
|
1323
1406
|
return true;
|
|
1324
1407
|
};
|
|
1408
|
+
startCompact = (input = {}) => {
|
|
1409
|
+
const id = manualCompactionId ?? randomUUID();
|
|
1410
|
+
const flight = compactCoordinator.start(id, input.focus, async (prepare, signal) => {
|
|
1411
|
+
const linked = AbortSignal.any([signal, controller.signal, compactSignal]);
|
|
1412
|
+
try {
|
|
1413
|
+
if (mode === 'compact' && inputs.currentCompact())
|
|
1414
|
+
return await compactLeaf(input, linked, prepare, id);
|
|
1415
|
+
return (await performCompact(input, async (effective, call) => ({
|
|
1416
|
+
changed: await compactLeaf({ ...effective, async: input.async === true }, AbortSignal.any([linked, call.signal]), prepare, id),
|
|
1417
|
+
executionId
|
|
1418
|
+
}))).changed;
|
|
1419
|
+
}
|
|
1420
|
+
catch (error) {
|
|
1421
|
+
const diagnostic = describeMarAgentError(error, 'MAR_AGENT_COMPACT_FAILED');
|
|
1422
|
+
await emit({
|
|
1423
|
+
type: 'context.compact_failed',
|
|
1424
|
+
compactionId: id,
|
|
1425
|
+
code: diagnostic.code,
|
|
1426
|
+
async: input.async === true,
|
|
1427
|
+
cancelled: linked.aborted
|
|
1428
|
+
});
|
|
1429
|
+
throw error;
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
if (smartInput)
|
|
1433
|
+
smartInput.requested = true;
|
|
1434
|
+
return flight;
|
|
1435
|
+
};
|
|
1325
1436
|
compactContext = async (input = {}) => {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1437
|
+
const flight = startCompact(input);
|
|
1438
|
+
if (input.async)
|
|
1439
|
+
return false;
|
|
1440
|
+
await emit({
|
|
1441
|
+
type: 'context.compacting',
|
|
1442
|
+
compactionId: flight.id,
|
|
1443
|
+
stage: 'WAITING',
|
|
1444
|
+
async: false
|
|
1445
|
+
});
|
|
1446
|
+
return (await compactCoordinator.checkpoint(true)) ?? false;
|
|
1331
1447
|
};
|
|
1448
|
+
compactRequests.bind((input) => compactCoordinator.pending
|
|
1449
|
+
? startCompact({
|
|
1450
|
+
...(input.prompt === undefined ? {} : { focus: input.prompt }),
|
|
1451
|
+
async: input.async === true
|
|
1452
|
+
})
|
|
1453
|
+
: undefined);
|
|
1454
|
+
if (mode === 'compact') {
|
|
1455
|
+
startCompact({ focus: compactFocus });
|
|
1456
|
+
compactRequests.drain((input) => startCompact({
|
|
1457
|
+
...(input.prompt === undefined ? {} : { focus: input.prompt }),
|
|
1458
|
+
async: input.async === true
|
|
1459
|
+
}));
|
|
1460
|
+
await compactCoordinator.checkpoint(true);
|
|
1461
|
+
compactRequests.seal();
|
|
1462
|
+
compactRequests.drain((input) => startCompact({
|
|
1463
|
+
...(input.prompt === undefined ? {} : { focus: input.prompt }),
|
|
1464
|
+
async: input.async === true
|
|
1465
|
+
}));
|
|
1466
|
+
await compactCoordinator.checkpoint(true);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1332
1469
|
const ensureContextBudget = async () => {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1470
|
+
const percent = lastServerContextTokens === undefined
|
|
1471
|
+
? undefined
|
|
1472
|
+
: (lastServerContextTokens * 100) / selectedModel.contextWindowTokens;
|
|
1473
|
+
if (percent !== undefined &&
|
|
1474
|
+
percent >= AGENT_EXECUTION_POLICY.automaticCompactionPercent) {
|
|
1475
|
+
const hadPending = compactCoordinator.pending;
|
|
1476
|
+
let compacted;
|
|
1477
|
+
try {
|
|
1478
|
+
compacted = await compactContext();
|
|
1479
|
+
}
|
|
1480
|
+
catch (error) {
|
|
1481
|
+
controller.signal.throwIfAborted();
|
|
1482
|
+
if (!hadPending)
|
|
1483
|
+
throw error;
|
|
1484
|
+
compactBoundary = messages.length;
|
|
1485
|
+
compacted = await compactContext();
|
|
1486
|
+
}
|
|
1487
|
+
if (!compacted)
|
|
1488
|
+
throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Provider usage reached the compaction threshold but no history can be compacted.');
|
|
1489
|
+
}
|
|
1490
|
+
else {
|
|
1491
|
+
const adopted = await compactCoordinator.checkpoint(false).catch(() => {
|
|
1492
|
+
controller.signal.throwIfAborted();
|
|
1493
|
+
});
|
|
1494
|
+
if (!adopted &&
|
|
1495
|
+
percent !== undefined &&
|
|
1496
|
+
percent >= AGENT_EXECUTION_POLICY.backgroundCompactionPercent &&
|
|
1497
|
+
!compactCoordinator.pending)
|
|
1498
|
+
startCompact({ async: true });
|
|
1499
|
+
}
|
|
1341
1500
|
};
|
|
1342
1501
|
executionLoop = new ExecutionLoop({
|
|
1343
1502
|
mode,
|
|
@@ -1349,27 +1508,30 @@ export function createSessionOpener(services) {
|
|
|
1349
1508
|
toolImages: currentExecutionToolImages,
|
|
1350
1509
|
publish: emit,
|
|
1351
1510
|
appendContext: (message) => withRecordProjection(async () => {
|
|
1352
|
-
await store.append(sessionId, {
|
|
1511
|
+
const stored = await store.append(sessionId, {
|
|
1353
1512
|
type: 'model.context',
|
|
1354
1513
|
payload: message,
|
|
1355
1514
|
executionId,
|
|
1356
1515
|
turnId
|
|
1357
1516
|
});
|
|
1517
|
+
messageSources.set(message, stored);
|
|
1358
1518
|
}),
|
|
1359
|
-
prepareRound: async (pendingOutputContinuation)
|
|
1360
|
-
|
|
1361
|
-
await appendMailboxMessages();
|
|
1519
|
+
prepareRound: async function prepareRound(pendingOutputContinuation) {
|
|
1520
|
+
await appendMailboxMessages();
|
|
1362
1521
|
updateMcpRegistry();
|
|
1363
1522
|
executionTools = mcpRegistry.project(baseTools());
|
|
1364
|
-
|
|
1365
|
-
|
|
1523
|
+
await syncMcpWorldState(messages.length);
|
|
1524
|
+
compactBoundary = messages.length;
|
|
1525
|
+
const blocking = compactRequests.drain((input) => startCompact({
|
|
1526
|
+
...(input.prompt === undefined ? {} : { focus: input.prompt }),
|
|
1527
|
+
async: input.async === true
|
|
1528
|
+
}));
|
|
1529
|
+
if (blocking)
|
|
1530
|
+
await compactCoordinator.checkpoint(true);
|
|
1366
1531
|
await ensureContextBudget();
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
|
|
1371
|
-
await recordEnvironment();
|
|
1372
|
-
}
|
|
1532
|
+
await syncMcpWorldState(messages.length);
|
|
1533
|
+
executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
|
|
1534
|
+
await recordEnvironment();
|
|
1373
1535
|
const { roundTools, roundBindings, toolScope, composeSystemPrompt } = captureRoundToolScope();
|
|
1374
1536
|
const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_exec');
|
|
1375
1537
|
const residentTools = projectTools(mcpRegistry.projectDirect(baseTools()));
|
|
@@ -1385,13 +1547,8 @@ export function createSessionOpener(services) {
|
|
|
1385
1547
|
: [...messages, pendingOutputContinuation];
|
|
1386
1548
|
sources.bind(toolScope);
|
|
1387
1549
|
const composedSystem = splitContextualPrompt(await composeSystemPrompt());
|
|
1388
|
-
const candidateWorld =
|
|
1389
|
-
|
|
1390
|
-
: updatePromptWorldState(contextualWorldState, messages.length, composedSystem.parts);
|
|
1391
|
-
const projectedMessages = projectSessionRequestMessages(requestContextMessages, mode === 'compact' ? [] : candidateWorld.entries, mode !== 'compact' && selectedModel.inputCapabilities.includes('IMAGE'), codeModeEnabled, permissionPolicy.mode === 'auto');
|
|
1392
|
-
const modelMessages = mode === 'compact'
|
|
1393
|
-
? projectCompactPromptMessages(projectedMessages, composedSystem.parts, contextualWorldState.promptSnapshot ?? [])
|
|
1394
|
-
: projectedMessages;
|
|
1550
|
+
const candidateWorld = updatePromptWorldState(contextualWorldState, messages.length, composedSystem.parts);
|
|
1551
|
+
const modelMessages = projectSessionRequestMessages(requestContextMessages, candidateWorld.entries, selectedModel.inputCapabilities.includes('IMAGE'), codeModeEnabled, permissionPolicy.mode === 'auto');
|
|
1395
1552
|
const systemPrompt = composedSystem.system;
|
|
1396
1553
|
const candidateRequest = {
|
|
1397
1554
|
system: systemPrompt,
|
|
@@ -1401,12 +1558,11 @@ export function createSessionOpener(services) {
|
|
|
1401
1558
|
messages: modelMessages,
|
|
1402
1559
|
tools: requestTools,
|
|
1403
1560
|
...(permissionPolicy.mode === 'auto' ? { allowHostedTools: false } : {}),
|
|
1404
|
-
...(mode === 'compact' ? { toolChoice: 'none' } : {}),
|
|
1405
1561
|
promptCacheKey: sessionId,
|
|
1406
1562
|
reasoningEffort,
|
|
1407
1563
|
...(requestReasoningEffort === undefined ? {} : { requestReasoningEffort }),
|
|
1408
1564
|
...(modelInputImages?.length ? { images: modelInputImages } : {}),
|
|
1409
|
-
onAttemptDiagnostic: recordModelAttempt(
|
|
1565
|
+
onAttemptDiagnostic: recordModelAttempt('EXECUTION')
|
|
1410
1566
|
};
|
|
1411
1567
|
preparingRequestMessages = modelMessages;
|
|
1412
1568
|
preparingRequestBaseLength = messages.length;
|
|
@@ -1422,44 +1578,65 @@ export function createSessionOpener(services) {
|
|
|
1422
1578
|
// Commit only after request middleware accepts the entire candidate.
|
|
1423
1579
|
await commitWorldState(candidateWorld);
|
|
1424
1580
|
}
|
|
1581
|
+
const lateCoreChange = inputs.hasBoundaryCompactPending && (await appendMailboxMessages());
|
|
1582
|
+
const lateBlocking = compactRequests.drain((input) => startCompact({
|
|
1583
|
+
...(input.prompt === undefined ? {} : { focus: input.prompt }),
|
|
1584
|
+
async: input.async === true
|
|
1585
|
+
}));
|
|
1586
|
+
const lateAdopted = lateBlocking && (await compactCoordinator.checkpoint(true));
|
|
1587
|
+
if (lateCoreChange || lateAdopted) {
|
|
1588
|
+
// Middleware may admit a blocking join after the initial checkpoint.
|
|
1589
|
+
// Never sample the candidate assembled against the replaced context.
|
|
1590
|
+
return prepareRound(pendingOutputContinuation);
|
|
1591
|
+
}
|
|
1425
1592
|
return { request, toolScope };
|
|
1426
1593
|
},
|
|
1427
1594
|
startModel: async function* (request) {
|
|
1428
|
-
const snapshots =
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
const snapshot = snapshots?.record(selectedModel.id, request);
|
|
1432
|
-
if (manualCompactionId !== undefined && !manualCompactionStarted) {
|
|
1433
|
-
manualCompactionStarted = true;
|
|
1434
|
-
manualCompaction = createCompactionOperation(manualCompactionId, compactSignal);
|
|
1435
|
-
await emit({ type: 'context.compacting', compactionId: manualCompactionId });
|
|
1436
|
-
}
|
|
1437
|
-
const compaction = manualCompaction;
|
|
1595
|
+
const snapshots = (sessionRuntime.requestSnapshots ??=
|
|
1596
|
+
new ExtensionRequestSnapshots());
|
|
1597
|
+
const snapshot = snapshots.record(selectedModel.id, request);
|
|
1438
1598
|
yield* streamModel(async function* (nativeSignal) {
|
|
1439
|
-
const
|
|
1440
|
-
? compaction.start({ start: (value) => selected.start(value, nativeSignal) }, request)
|
|
1441
|
-
: selected.start(request, nativeSignal);
|
|
1442
|
-
for await (const event of events) {
|
|
1599
|
+
for await (const event of selected.start(request, nativeSignal)) {
|
|
1443
1600
|
if (snapshot && event.type === 'completed' && event.reason !== 'cancelled')
|
|
1444
1601
|
snapshots.complete(snapshot);
|
|
1445
1602
|
yield event;
|
|
1446
1603
|
}
|
|
1447
|
-
},
|
|
1604
|
+
}, controller.signal);
|
|
1448
1605
|
},
|
|
1449
|
-
recoverySignal: () =>
|
|
1606
|
+
recoverySignal: () => controller.signal,
|
|
1450
1607
|
compactContext,
|
|
1451
|
-
|
|
1608
|
+
afterModel: async (closedPrefixLength) => {
|
|
1609
|
+
compactBoundary = closedPrefixLength;
|
|
1610
|
+
if (lastServerContextTokens !== undefined &&
|
|
1611
|
+
(lastServerContextTokens * 100) / selectedModel.contextWindowTokens >=
|
|
1612
|
+
AGENT_EXECUTION_POLICY.backgroundCompactionPercent &&
|
|
1613
|
+
!compactCoordinator.pending)
|
|
1614
|
+
startCompact({ async: true });
|
|
1615
|
+
},
|
|
1452
1616
|
observeContextTokens: (tokens) => {
|
|
1453
1617
|
lastServerContextTokens = tokens;
|
|
1618
|
+
smartContextTokens = tokens;
|
|
1454
1619
|
},
|
|
1455
1620
|
observeUsage: (usage) => {
|
|
1456
1621
|
if (usage !== undefined) {
|
|
1457
1622
|
lastServerContextTokens = usage.contextInputTokens;
|
|
1623
|
+
smartContextTokens = usage.contextInputTokens;
|
|
1458
1624
|
lastServerUsage = { ...usage, historyMessages: [...messages] };
|
|
1459
1625
|
}
|
|
1460
1626
|
},
|
|
1461
1627
|
checkpoint: async (round, latestUsage) => {
|
|
1462
|
-
|
|
1628
|
+
compactBoundary = messages.length;
|
|
1629
|
+
const blocking = compactRequests.drain((input) => startCompact({
|
|
1630
|
+
...(input.prompt === undefined ? {} : { focus: input.prompt }),
|
|
1631
|
+
async: input.async === true
|
|
1632
|
+
}));
|
|
1633
|
+
if (blocking)
|
|
1634
|
+
await compactCoordinator.checkpoint(true);
|
|
1635
|
+
else
|
|
1636
|
+
await compactCoordinator.checkpoint(false).catch(() => {
|
|
1637
|
+
controller.signal.throwIfAborted();
|
|
1638
|
+
});
|
|
1639
|
+
if (!compactCoordinator.pending &&
|
|
1463
1640
|
sessionSource.type !== 'subagent' &&
|
|
1464
1641
|
latestUsage !== undefined) {
|
|
1465
1642
|
// Only a committed GC checkpoint may replace active context and invalidate continuation.
|
|
@@ -1486,6 +1663,7 @@ export function createSessionOpener(services) {
|
|
|
1486
1663
|
}
|
|
1487
1664
|
});
|
|
1488
1665
|
if (committed) {
|
|
1666
|
+
smartContextTokens = undefined;
|
|
1489
1667
|
selected.resetContinuation?.();
|
|
1490
1668
|
const refreshed = await restoreMessages(store, sessionId, committed.records, {
|
|
1491
1669
|
includeImages: true,
|
|
@@ -1511,51 +1689,23 @@ export function createSessionOpener(services) {
|
|
|
1511
1689
|
}
|
|
1512
1690
|
},
|
|
1513
1691
|
mailbox: {
|
|
1514
|
-
hasPending: () => inputs.hasBoundaryPending && !inputs.executing
|
|
1692
|
+
hasPending: () => inputs.hasBoundaryPending && !inputs.executing,
|
|
1515
1693
|
append: appendMailboxMessages,
|
|
1516
1694
|
close: () => {
|
|
1517
1695
|
acceptsMailbox = false;
|
|
1518
1696
|
}
|
|
1519
1697
|
}
|
|
1520
1698
|
});
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1699
|
+
const completed = await executionLoop.run();
|
|
1700
|
+
compactRequests.seal();
|
|
1701
|
+
compactRequests.drain((input) => startCompact({
|
|
1702
|
+
...(input.prompt === undefined ? {} : { focus: input.prompt }),
|
|
1703
|
+
async: input.async === true
|
|
1704
|
+
}));
|
|
1705
|
+
await compactCoordinator.checkpoint(true).catch(() => {
|
|
1706
|
+
controller.signal.throwIfAborted();
|
|
1707
|
+
});
|
|
1528
1708
|
finalAnswer = completed.finalAnswer;
|
|
1529
|
-
const compactSummary = mode === 'compact' ? finalAnswer.trim() : '';
|
|
1530
|
-
if (manualCompaction) {
|
|
1531
|
-
manualCompaction.signal.throwIfAborted();
|
|
1532
|
-
await manualCompaction.dispose();
|
|
1533
|
-
}
|
|
1534
|
-
if (mode === 'compact') {
|
|
1535
|
-
const compactRecord = await writeCompactionRecord(compactSummary, false, compactSignal, async (actual, options) => {
|
|
1536
|
-
const retainedModelContext = pendingAppends
|
|
1537
|
-
.slice(manualAppendBoundary)
|
|
1538
|
-
.map((entry) => entry.message);
|
|
1539
|
-
const replacementHistory = buildCompactedHistory(actual, compactionUserMessages, compactUserTokenLimit);
|
|
1540
|
-
const record = await store.compact(sessionId, {
|
|
1541
|
-
summary: actual,
|
|
1542
|
-
retainedModelContext,
|
|
1543
|
-
replacementHistory,
|
|
1544
|
-
modelId: selectedModel.id,
|
|
1545
|
-
contextWindowTokens: selectedModel.contextWindowTokens,
|
|
1546
|
-
...(completed.usage === undefined ? {} : { usage: completed.usage }),
|
|
1547
|
-
executionId,
|
|
1548
|
-
turnId
|
|
1549
|
-
}, options);
|
|
1550
|
-
messages.splice(0, messages.length, ...compactedHistoryMessages(replacementHistory), ...retainedModelContext);
|
|
1551
|
-
pendingAppends.length = 0;
|
|
1552
|
-
selected.resetContinuation?.();
|
|
1553
|
-
return record;
|
|
1554
|
-
});
|
|
1555
|
-
await resetCompactWorldState(compactRecord.recordId);
|
|
1556
|
-
}
|
|
1557
|
-
if (mode === 'compact' && manualCompactionId !== undefined)
|
|
1558
|
-
await emit({ type: 'context.compacted', compactionId: manualCompactionId });
|
|
1559
1709
|
};
|
|
1560
1710
|
if (mode === 'compact')
|
|
1561
1711
|
await performCompact({ focus: input.prompt }, async (effective, call) => {
|
|
@@ -1579,6 +1729,10 @@ export function createSessionOpener(services) {
|
|
|
1579
1729
|
catch (error) {
|
|
1580
1730
|
finalAnswer = executionLoop?.finalAnswer ?? finalAnswer;
|
|
1581
1731
|
acceptsMailbox = false;
|
|
1732
|
+
await compactCoordinator.close(error);
|
|
1733
|
+
compactRequests.close(new MarAgentError('MAR_AGENT_CANCELLED', 'Execution ended.'));
|
|
1734
|
+
if (sessionRuntime.compactRequests === compactRequests)
|
|
1735
|
+
delete sessionRuntime.compactRequests;
|
|
1582
1736
|
if (controller.signal.aborted) {
|
|
1583
1737
|
subagents?.endParentExecution(executionId);
|
|
1584
1738
|
await emit({ type: 'execution.cancelled' });
|
|
@@ -1602,6 +1756,9 @@ export function createSessionOpener(services) {
|
|
|
1602
1756
|
finally {
|
|
1603
1757
|
if (sessionRuntime.requestMessages?.executionId === executionId)
|
|
1604
1758
|
delete sessionRuntime.requestMessages;
|
|
1759
|
+
compactRequests.close(new MarAgentError('MAR_AGENT_CANCELLED', 'Execution ended.'));
|
|
1760
|
+
if (sessionRuntime.compactRequests === compactRequests)
|
|
1761
|
+
delete sessionRuntime.compactRequests;
|
|
1605
1762
|
if (sessionRuntime.appendedMessages?.executionId === executionId)
|
|
1606
1763
|
delete sessionRuntime.appendedMessages;
|
|
1607
1764
|
if (promptDefaults?.executionId === executionId)
|
|
@@ -1611,7 +1768,7 @@ export function createSessionOpener(services) {
|
|
|
1611
1768
|
if (sessionRuntime.toolSources === executionToolSources)
|
|
1612
1769
|
delete sessionRuntime.toolSources;
|
|
1613
1770
|
internal.signal?.removeEventListener('abort', abortSource);
|
|
1614
|
-
await
|
|
1771
|
+
await compactCoordinator.close(controller.signal.reason ?? new MarAgentError('MAR_AGENT_CANCELLED', 'Execution ended.'));
|
|
1615
1772
|
await selectedTurn?.close().catch(() => undefined);
|
|
1616
1773
|
try {
|
|
1617
1774
|
await closeModelAdapters(executionScopedAdapters);
|
|
@@ -1900,10 +2057,11 @@ export function createSessionOpener(services) {
|
|
|
1900
2057
|
id: sessionId,
|
|
1901
2058
|
run: (input) => execution('run', input),
|
|
1902
2059
|
plan: (input) => execution('plan', input),
|
|
1903
|
-
compact: (input = {}) =>
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2060
|
+
compact: (input = {}) => sessionRuntime.compactRequests?.request(input) ??
|
|
2061
|
+
execution('compact', {
|
|
2062
|
+
prompt: input.prompt ?? DEFAULT_COMPACTION_FOCUS,
|
|
2063
|
+
...input
|
|
2064
|
+
}),
|
|
1907
2065
|
cancel: async (executionId) => active.get(`${sessionId}:${executionId}`)?.abort(),
|
|
1908
2066
|
startSubagentTurn: (turn) => execution(turn.role === 'explore' ? 'plan' : 'run', {
|
|
1909
2067
|
prompt: turn.prompt,
|
|
@@ -1954,7 +2112,7 @@ function projectSessionRequestMessages(messages, entries, supportsImages, codeMo
|
|
|
1954
2112
|
supportsImages
|
|
1955
2113
|
}).map((message) => projectCodeModeSearchResult(autoPermission && message.toolSearchTools
|
|
1956
2114
|
? { ...message, toolSearchTools: message.toolSearchTools.map(projectPermissionTool) }
|
|
1957
|
-
: message, codeMode));
|
|
2115
|
+
: projectSmartCompactionMessage(message), codeMode));
|
|
1958
2116
|
}
|
|
1959
2117
|
function projectAppendedModelMessage(input) {
|
|
1960
2118
|
if (input.visibility === 'transcript')
|