@myagentroam/agent 0.10.30 → 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/model/contracts.d.ts +1 -0
- package/dist/agent-core/model/openai-responses.js +6 -2
- 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/execution-policy.d.ts +1 -0
- package/dist/agent-core/runtime/execution-policy.js +1 -0
- package/dist/agent-core/sdk/session.js +46 -4
- package/dist/agent-core/session/jsonl-store.js +3 -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/package.json +1 -1
|
@@ -1771,6 +1771,8 @@ function strictFunctionSchema(value, optional = false) {
|
|
|
1771
1771
|
return value;
|
|
1772
1772
|
const schema = value;
|
|
1773
1773
|
const supportedSchema = Object.fromEntries(Object.entries(schema).filter(([keyword]) => keyword !== 'propertyNames' && keyword !== 'uniqueItems'));
|
|
1774
|
+
if (optional && Array.isArray(schema.enum) && !schema.enum.includes(null))
|
|
1775
|
+
supportedSchema.enum = [...schema.enum, null];
|
|
1774
1776
|
if (schema.type === 'object') {
|
|
1775
1777
|
const properties = record(schema.properties);
|
|
1776
1778
|
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
@@ -1795,11 +1797,13 @@ function strictFunctionSchema(value, optional = false) {
|
|
|
1795
1797
|
return supportedSchema;
|
|
1796
1798
|
if (typeof schema.type === 'string')
|
|
1797
1799
|
return { ...supportedSchema, type: [schema.type, 'null'] };
|
|
1798
|
-
if (Array.isArray(schema.
|
|
1800
|
+
if (Array.isArray(schema.type))
|
|
1799
1801
|
return {
|
|
1800
1802
|
...supportedSchema,
|
|
1801
|
-
|
|
1803
|
+
type: schema.type.includes('null') ? schema.type : [...schema.type, 'null']
|
|
1802
1804
|
};
|
|
1805
|
+
if (Array.isArray(schema.enum))
|
|
1806
|
+
return supportedSchema;
|
|
1803
1807
|
return { anyOf: [supportedSchema, { type: 'null' }] };
|
|
1804
1808
|
}
|
|
1805
1809
|
function responseCitations(response) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModelMessage } from '../../model/contracts.js';
|
|
2
|
+
import { type SessionRecord } from '../../session/jsonl-store.js';
|
|
3
|
+
export declare const SMART_COMPACTION_MARKER = "<mar_runtime>\n<smart_compaction scope=\"this_message\">eligible</smart_compaction>\n</mar_runtime>";
|
|
4
|
+
export interface SmartCompactionInput {
|
|
5
|
+
readonly recordId: string;
|
|
6
|
+
readonly eligible: boolean;
|
|
7
|
+
requested: boolean;
|
|
8
|
+
error?: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** A measured watermark, not the stricter native-request budget calibration. */
|
|
11
|
+
export declare function latestSmartContextTokens(records: readonly SessionRecord[], modelId: string, contextWindowTokens: number): number | undefined;
|
|
12
|
+
/** Only provider views receive the marker; durable message content stays unchanged. */
|
|
13
|
+
export declare function projectSmartCompactionMessage(message: ModelMessage): ModelMessage;
|
|
14
|
+
export declare function requestSmartCompaction(input: SmartCompactionInput | undefined, start: () => void): void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { MarAgentError } from '../../error.js';
|
|
2
|
+
import { compactSummaryFromCheckpoint } from '../../session/jsonl-store.js';
|
|
3
|
+
export const SMART_COMPACTION_MARKER = '<mar_runtime>\n<smart_compaction scope="this_message">eligible</smart_compaction>\n</mar_runtime>';
|
|
4
|
+
/** A measured watermark, not the stricter native-request budget calibration. */
|
|
5
|
+
export function latestSmartContextTokens(records, modelId, contextWindowTokens) {
|
|
6
|
+
let tokens;
|
|
7
|
+
let matchingModel = false;
|
|
8
|
+
for (const record of records) {
|
|
9
|
+
const payload = record.payload;
|
|
10
|
+
if (compactSummaryFromCheckpoint(record) !== undefined) {
|
|
11
|
+
tokens = undefined;
|
|
12
|
+
// A sliced checkpoint does not prove the input window used by later usage.
|
|
13
|
+
matchingModel = false;
|
|
14
|
+
}
|
|
15
|
+
if (record.type === 'context.gc.completed')
|
|
16
|
+
tokens = undefined;
|
|
17
|
+
if (record.type === 'execution.header' && payload.mode !== 'compact') {
|
|
18
|
+
matchingModel =
|
|
19
|
+
payload.modelId === modelId && payload.contextWindowTokens === contextWindowTokens;
|
|
20
|
+
if (!matchingModel)
|
|
21
|
+
tokens = undefined;
|
|
22
|
+
}
|
|
23
|
+
if (matchingModel && payload.type === 'usage.updated' && payload.purpose !== 'compact') {
|
|
24
|
+
const value = payload.contextInputTokens ?? payload.inputTokens;
|
|
25
|
+
if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0)
|
|
26
|
+
tokens = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return tokens;
|
|
30
|
+
}
|
|
31
|
+
/** Only provider views receive the marker; durable message content stays unchanged. */
|
|
32
|
+
export function projectSmartCompactionMessage(message) {
|
|
33
|
+
if (message.role !== 'user' ||
|
|
34
|
+
message.contextKind !== undefined ||
|
|
35
|
+
!message.metadata?.smartCompaction)
|
|
36
|
+
return message;
|
|
37
|
+
const suffix = '\n\n' + SMART_COMPACTION_MARKER;
|
|
38
|
+
return {
|
|
39
|
+
...message,
|
|
40
|
+
content: message.content + suffix,
|
|
41
|
+
...(message.textBlocks ? { textBlocks: [...message.textBlocks, { text: suffix }] } : {})
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function requestSmartCompaction(input, start) {
|
|
45
|
+
if (!input?.eligible)
|
|
46
|
+
throw new MarAgentError('MAR_AGENT_SMART_COMPACT_DENIED', 'The current user input is not eligible for Smart Compaction.');
|
|
47
|
+
if ('error' in input)
|
|
48
|
+
throw input.error;
|
|
49
|
+
if (input.requested)
|
|
50
|
+
return;
|
|
51
|
+
input.requested = true;
|
|
52
|
+
try {
|
|
53
|
+
start();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
input.error = error;
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -41,6 +41,7 @@ import { ContextGcRuntime } from '../runtime/context/gc-runtime.js';
|
|
|
41
41
|
import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
|
|
42
42
|
import { loadAgentInstructions, renderAgentInstructions } from '../runtime/context/instructions.js';
|
|
43
43
|
import { normalizeMessagesForModel } from '../runtime/context/normalize.js';
|
|
44
|
+
import { latestSmartContextTokens, projectSmartCompactionMessage, requestSmartCompaction } from '../runtime/compaction/smart.js';
|
|
44
45
|
import { compactSummaryFromCheckpoint, inputMetadataFromRecord } from '../session/jsonl-store.js';
|
|
45
46
|
import { SubagentSessionController } from '../subagent/session-controller.js';
|
|
46
47
|
import { agentStartToolDefinitionWithModels } from '../tools/agent/agent-start.js';
|
|
@@ -417,6 +418,7 @@ export function createSessionOpener(services) {
|
|
|
417
418
|
pendingAppends.length = 0;
|
|
418
419
|
immediateTarget = messages;
|
|
419
420
|
let recordedEnvironment = restored.environmentState;
|
|
421
|
+
let smartInput;
|
|
420
422
|
let compactContext;
|
|
421
423
|
let startCompact;
|
|
422
424
|
const writeInputRecord = async (content, type, metadata, call, committed) => {
|
|
@@ -425,6 +427,16 @@ export function createSessionOpener(services) {
|
|
|
425
427
|
throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
|
|
426
428
|
await withRecordProjection(async () => {
|
|
427
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;
|
|
428
440
|
const images = content.images?.length
|
|
429
441
|
? await store.persistInputImages(sessionId, content.images)
|
|
430
442
|
: [];
|
|
@@ -433,6 +445,7 @@ export function createSessionOpener(services) {
|
|
|
433
445
|
payload: {
|
|
434
446
|
...metadata,
|
|
435
447
|
prompt: content.text,
|
|
448
|
+
...(smartCompaction ? { smartCompaction: true } : {}),
|
|
436
449
|
...(content.modelText === undefined ? {} : { modelText: content.modelText }),
|
|
437
450
|
...(content.context === undefined ? {} : { context: content.context }),
|
|
438
451
|
...(content.modelContext === undefined
|
|
@@ -444,6 +457,11 @@ export function createSessionOpener(services) {
|
|
|
444
457
|
turnId
|
|
445
458
|
}, { signal: call.signal });
|
|
446
459
|
const inputMetadata = inputMetadataFromRecord(record);
|
|
460
|
+
smartInput = {
|
|
461
|
+
recordId: record.recordId,
|
|
462
|
+
eligible: smartCompaction,
|
|
463
|
+
requested: false
|
|
464
|
+
};
|
|
447
465
|
compactionUserMessages.push({
|
|
448
466
|
content: content.modelText ?? content.text,
|
|
449
467
|
metadata: inputMetadata
|
|
@@ -549,6 +567,7 @@ export function createSessionOpener(services) {
|
|
|
549
567
|
const selected = createModelTurnSession(selectedAdapter);
|
|
550
568
|
selectedTurn = selected;
|
|
551
569
|
let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
|
|
570
|
+
let smartContextTokens = latestSmartContextTokens(contextRecords, selectedModel.id, selectedModel.contextWindowTokens);
|
|
552
571
|
const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
|
|
553
572
|
const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
|
|
554
573
|
const useEffortUpdates = usesResponsesConfigurationUpdates(selectedModel);
|
|
@@ -887,8 +906,24 @@ export function createSessionOpener(services) {
|
|
|
887
906
|
const parentFrame = subagents === undefined || mode === 'compact'
|
|
888
907
|
? undefined
|
|
889
908
|
: subagents.captureParentFrame(executionId);
|
|
909
|
+
const roundSmartInput = smartInput;
|
|
890
910
|
const toolContext = {
|
|
891
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
|
+
},
|
|
892
927
|
permissionPolicy,
|
|
893
928
|
writeToolResultResource: (text) => store.writeToolResultResource(sessionId, text),
|
|
894
929
|
todo,
|
|
@@ -1185,7 +1220,7 @@ export function createSessionOpener(services) {
|
|
|
1185
1220
|
(entry.metadata?.recordId
|
|
1186
1221
|
? message.metadata?.recordId === entry.metadata.recordId
|
|
1187
1222
|
: message.content === entry.content)));
|
|
1188
|
-
const snapshotTools = structuredClone(executionTools);
|
|
1223
|
+
const snapshotTools = structuredClone(projectTools(executionTools));
|
|
1189
1224
|
const generation = contextualWorldState.generation;
|
|
1190
1225
|
const snapshotUsage = lastServerUsage;
|
|
1191
1226
|
const snapshotEffort = requestReasoningEffort ?? reasoningEffort;
|
|
@@ -1201,7 +1236,7 @@ export function createSessionOpener(services) {
|
|
|
1201
1236
|
const compactSystemPrompt = compactComposition.system;
|
|
1202
1237
|
const projectedHistory = normalizeMessagesForModel(compactHistory, {
|
|
1203
1238
|
supportsImages: false
|
|
1204
|
-
});
|
|
1239
|
+
}).map(projectSmartCompactionMessage);
|
|
1205
1240
|
const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
|
|
1206
1241
|
...(input.focus === undefined ? {} : { focus: input.focus })
|
|
1207
1242
|
});
|
|
@@ -1358,6 +1393,7 @@ export function createSessionOpener(services) {
|
|
|
1358
1393
|
await resetCompactWorldState(compactRecord.recordId, { resources: compactResources });
|
|
1359
1394
|
lastServerContextTokens = undefined;
|
|
1360
1395
|
lastServerUsage = undefined;
|
|
1396
|
+
smartContextTokens = undefined;
|
|
1361
1397
|
requestReasoningEffort = reasoningEffort;
|
|
1362
1398
|
effectiveReasoningEffort = reasoningEffort;
|
|
1363
1399
|
await emit({
|
|
@@ -1371,7 +1407,7 @@ export function createSessionOpener(services) {
|
|
|
1371
1407
|
};
|
|
1372
1408
|
startCompact = (input = {}) => {
|
|
1373
1409
|
const id = manualCompactionId ?? randomUUID();
|
|
1374
|
-
|
|
1410
|
+
const flight = compactCoordinator.start(id, input.focus, async (prepare, signal) => {
|
|
1375
1411
|
const linked = AbortSignal.any([signal, controller.signal, compactSignal]);
|
|
1376
1412
|
try {
|
|
1377
1413
|
if (mode === 'compact' && inputs.currentCompact())
|
|
@@ -1393,6 +1429,9 @@ export function createSessionOpener(services) {
|
|
|
1393
1429
|
throw error;
|
|
1394
1430
|
}
|
|
1395
1431
|
});
|
|
1432
|
+
if (smartInput)
|
|
1433
|
+
smartInput.requested = true;
|
|
1434
|
+
return flight;
|
|
1396
1435
|
};
|
|
1397
1436
|
compactContext = async (input = {}) => {
|
|
1398
1437
|
const flight = startCompact(input);
|
|
@@ -1576,10 +1615,12 @@ export function createSessionOpener(services) {
|
|
|
1576
1615
|
},
|
|
1577
1616
|
observeContextTokens: (tokens) => {
|
|
1578
1617
|
lastServerContextTokens = tokens;
|
|
1618
|
+
smartContextTokens = tokens;
|
|
1579
1619
|
},
|
|
1580
1620
|
observeUsage: (usage) => {
|
|
1581
1621
|
if (usage !== undefined) {
|
|
1582
1622
|
lastServerContextTokens = usage.contextInputTokens;
|
|
1623
|
+
smartContextTokens = usage.contextInputTokens;
|
|
1583
1624
|
lastServerUsage = { ...usage, historyMessages: [...messages] };
|
|
1584
1625
|
}
|
|
1585
1626
|
},
|
|
@@ -1622,6 +1663,7 @@ export function createSessionOpener(services) {
|
|
|
1622
1663
|
}
|
|
1623
1664
|
});
|
|
1624
1665
|
if (committed) {
|
|
1666
|
+
smartContextTokens = undefined;
|
|
1625
1667
|
selected.resetContinuation?.();
|
|
1626
1668
|
const refreshed = await restoreMessages(store, sessionId, committed.records, {
|
|
1627
1669
|
includeImages: true,
|
|
@@ -2070,7 +2112,7 @@ function projectSessionRequestMessages(messages, entries, supportsImages, codeMo
|
|
|
2070
2112
|
supportsImages
|
|
2071
2113
|
}).map((message) => projectCodeModeSearchResult(autoPermission && message.toolSearchTools
|
|
2072
2114
|
? { ...message, toolSearchTools: message.toolSearchTools.map(projectPermissionTool) }
|
|
2073
|
-
: message, codeMode));
|
|
2115
|
+
: projectSmartCompactionMessage(message), codeMode));
|
|
2074
2116
|
}
|
|
2075
2117
|
function projectAppendedModelMessage(input) {
|
|
2076
2118
|
if (input.visibility === 'transcript')
|
|
@@ -2527,7 +2527,8 @@ export function parseModelInputMetadata(value) {
|
|
|
2527
2527
|
...(typeof metadata.extensionId === 'string' && metadata.extensionId.length <= 128
|
|
2528
2528
|
? { extensionId: metadata.extensionId }
|
|
2529
2529
|
: {}),
|
|
2530
|
-
...(metadata.truncated === true ? { truncated: true } : {})
|
|
2530
|
+
...(metadata.truncated === true ? { truncated: true } : {}),
|
|
2531
|
+
...(metadata.smartCompaction === true ? { smartCompaction: true } : {})
|
|
2531
2532
|
};
|
|
2532
2533
|
}
|
|
2533
2534
|
export function inputMetadataFromRecord(record) {
|
|
@@ -2540,6 +2541,7 @@ export function inputMetadataFromRecord(record) {
|
|
|
2540
2541
|
? 'parent_agent'
|
|
2541
2542
|
: 'user',
|
|
2542
2543
|
recordId: record.recordId,
|
|
2544
|
+
...(payload.smartCompaction === true ? { smartCompaction: true } : {}),
|
|
2543
2545
|
...(typeof source?.extensionId === 'string' && source.extensionId.length <= 128
|
|
2544
2546
|
? { extensionId: source.extensionId }
|
|
2545
2547
|
: {})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ClientToolDefinition } from '../../model/contracts.js';
|
|
2
|
+
import type { ToolRuntimeContext } from '../runtime-context.js';
|
|
3
|
+
export declare const smartCompactToolDefinition: ClientToolDefinition;
|
|
4
|
+
export declare function executeSmartCompact(arguments_: unknown, context: ToolRuntimeContext): Promise<{
|
|
5
|
+
content: string;
|
|
6
|
+
data: {
|
|
7
|
+
status: string;
|
|
8
|
+
};
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MarAgentError } from '../../error.js';
|
|
2
|
+
export const smartCompactToolDefinition = {
|
|
3
|
+
name: 'smart_compact',
|
|
4
|
+
allowedCallers: ['direct', 'programmatic'],
|
|
5
|
+
parallelSafety: 'safe',
|
|
6
|
+
description: 'Request background context compaction only when the current real user message ends with <mar_runtime><smart_compaction scope="this_message">eligible</smart_compaction></mar_runtime> (on separate lines). The scope applies only to the containing user message; a historical marker does not authorize the current input. Call only when prior work is substantially complete, the request begins a sustained new work phase, and prior detailed context has low continuation value while key state can be retained in a summary. Do not call merely because the conversation is long, for a one-off question, or while prior evidence is still needed. Prefer calling alongside the first batch of necessary read/search/exec tools rather than creating a separate rollout. Acceptance does not wait for generation or installation.',
|
|
7
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false }
|
|
8
|
+
};
|
|
9
|
+
export function executeSmartCompact(arguments_, context) {
|
|
10
|
+
if (!arguments_ ||
|
|
11
|
+
typeof arguments_ !== 'object' ||
|
|
12
|
+
Array.isArray(arguments_) ||
|
|
13
|
+
Object.keys(arguments_).length)
|
|
14
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Smart Compaction input must be an empty object.');
|
|
15
|
+
if (!context.requestSmartCompact)
|
|
16
|
+
throw new MarAgentError('MAR_AGENT_SMART_COMPACT_DENIED', 'The current user input is not eligible for Smart Compaction.');
|
|
17
|
+
context.requestSmartCompact();
|
|
18
|
+
const data = { status: 'accepted' };
|
|
19
|
+
return Promise.resolve({ content: JSON.stringify(data), data });
|
|
20
|
+
}
|
|
@@ -12,6 +12,7 @@ import { executeTodoRead, todoReadToolDefinition } from './agent/todo-read.js';
|
|
|
12
12
|
import { executeTodoWrite, todoWriteToolDefinition } from './agent/todo-write.js';
|
|
13
13
|
import { executeToolSearch, toolSearchToolDefinition } from './mcp/tool-search.js';
|
|
14
14
|
import { viewImageToolDefinition } from './local/view-image.js';
|
|
15
|
+
import { smartCompactToolDefinition, executeSmartCompact } from './agent/smart-compact.js';
|
|
15
16
|
export const BUILTIN_TOOL_DEFINITIONS = [
|
|
16
17
|
skillToolDefinition,
|
|
17
18
|
toolSearchToolDefinition,
|
|
@@ -28,10 +29,12 @@ export const BUILTIN_TOOL_DEFINITIONS = [
|
|
|
28
29
|
agentStartToolDefinition,
|
|
29
30
|
agentWaitToolDefinition,
|
|
30
31
|
agentMessageToolDefinition,
|
|
31
|
-
agentCancelToolDefinition
|
|
32
|
+
agentCancelToolDefinition,
|
|
33
|
+
smartCompactToolDefinition
|
|
32
34
|
];
|
|
33
35
|
export const AGENT_TOOL_HANDLERS = new Map([
|
|
34
36
|
['skill', executeSkill],
|
|
37
|
+
['smart_compact', executeSmartCompact],
|
|
35
38
|
['tool_search', executeToolSearch],
|
|
36
39
|
['question', executeQuestion],
|
|
37
40
|
['todo_read', (_arguments, context) => executeTodoRead(context)],
|