@myagentroam/agent 0.9.171 → 0.9.173

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/agent-core/extensions/compaction.d.ts +16 -0
  2. package/dist/agent-core/extensions/compaction.js +42 -1
  3. package/dist/agent-core/model/anthropic-messages.js +40 -6
  4. package/dist/agent-core/model/attempt-diagnostics.d.ts +3 -0
  5. package/dist/agent-core/model/attempt-diagnostics.js +9 -0
  6. package/dist/agent-core/model/contracts.d.ts +9 -1
  7. package/dist/agent-core/model/openai-responses.js +4 -11
  8. package/dist/agent-core/prompts/resources.d.ts +0 -1
  9. package/dist/agent-core/prompts/resources.js +4 -14
  10. package/dist/agent-core/runtime/compaction/history.d.ts +3 -2
  11. package/dist/agent-core/runtime/compaction/history.js +42 -12
  12. package/dist/agent-core/runtime/compaction/operation.d.ts +5 -0
  13. package/dist/agent-core/runtime/compaction/operation.js +22 -2
  14. package/dist/agent-core/runtime/context/projection.js +1 -0
  15. package/dist/agent-core/runtime/context/request-budget.js +1 -1
  16. package/dist/agent-core/runtime/context/restore.d.ts +2 -2
  17. package/dist/agent-core/runtime/context/restore.js +24 -8
  18. package/dist/agent-core/runtime/context/world-state.d.ts +2 -5
  19. package/dist/agent-core/runtime/context/world-state.js +14 -14
  20. package/dist/agent-core/sdk/session.js +82 -49
  21. package/dist/agent-core/session/context-gc-replacements.js +14 -3
  22. package/dist/agent-core/session/jsonl-store.d.ts +9 -4
  23. package/dist/agent-core/session/jsonl-store.js +48 -1
  24. package/dist/agent-core/tools/code-mode/observations.js +19 -1
  25. package/dist/agent-core/tools/local/exec.d.ts +3 -1
  26. package/dist/agent-core/tools/local/exec.js +21 -0
  27. package/dist/agent-core/tools/local/registry.js +2 -2
  28. package/dist/index.d.ts +1 -1
  29. package/dist/shared/model-text.d.ts +2 -1
  30. package/dist/shared/model-text.js +1 -0
  31. package/package.json +1 -1
@@ -1,11 +1,14 @@
1
- import { retainedSessionResourcesUpdate } from '../../prompts/resources.js';
2
1
  import { compactSummaryFromCheckpoint } from '../../session/jsonl-store.js';
3
2
  export function insertContextualWorldState(messages, entries) {
4
3
  const result = [...messages];
5
4
  let offset = 0;
6
5
  for (const entry of entries) {
7
6
  const insertionIndex = Math.max(0, Math.min(entry.index + offset, result.length));
8
- result.splice(insertionIndex, 0, { role: 'user', content: entry.content });
7
+ result.splice(insertionIndex, 0, {
8
+ role: 'user',
9
+ content: entry.content,
10
+ contextKind: 'contextual'
11
+ });
9
12
  offset++;
10
13
  }
11
14
  return result;
@@ -87,24 +90,18 @@ export async function persistContextualWorldState(store, sessionId, state) {
87
90
  entries: state.entries
88
91
  });
89
92
  }
90
- export function compactHistoryMayContainRetainedResources(records) {
91
- return records.some((record) => compactSummaryFromCheckpoint(record)?.includes('# Retained Session resources'));
92
- }
93
93
  export function updateContextualWorldState(state, index, current) {
94
94
  const entries = [...state.entries];
95
95
  if (!state.initialized || state.instructionSnapshot !== current.instructionSnapshot) {
96
96
  if (current.instructionContent !== undefined)
97
97
  entries.push({ index, kind: 'instructions', content: current.instructionContent });
98
98
  }
99
- const resourceContent = retainedSessionResourcesUpdate(current.resourceSnapshot, state.initialized ? state.resourceSnapshot : undefined, current.previousMayContainResources);
100
- if (resourceContent !== undefined)
101
- entries.push({ index, kind: 'resources', content: resourceContent });
102
99
  return {
103
100
  generation: state.generation,
104
101
  mcpSnapshot: state.mcpSnapshot,
105
102
  initialized: true,
106
103
  instructionSnapshot: current.instructionSnapshot,
107
- resourceSnapshot: current.resourceSnapshot,
104
+ resourceSnapshot: state.resourceSnapshot,
108
105
  entries
109
106
  };
110
107
  }
@@ -112,9 +109,8 @@ export function resetContextualWorldState(generation, index, current) {
112
109
  const entries = [];
113
110
  if (current.instructionContent !== undefined)
114
111
  entries.push({ index, kind: 'instructions', content: current.instructionContent });
115
- const resourceContent = retainedSessionResourcesUpdate(current.resourceSnapshot, undefined, current.previousMayContainResources);
116
- if (resourceContent !== undefined)
117
- entries.push({ index, kind: 'resources', content: resourceContent });
112
+ if (current.resourceSnapshot !== undefined)
113
+ entries.push({ index, kind: 'resources', content: current.resourceSnapshot });
118
114
  return {
119
115
  generation,
120
116
  initialized: true,
@@ -124,9 +120,13 @@ export function resetContextualWorldState(generation, index, current) {
124
120
  };
125
121
  }
126
122
  export function contextualInstructionInsertIndex(messages) {
127
- let index = messages.findLastIndex((message) => message.role === 'user' && message.contextKind !== 'environment');
123
+ let index = messages.findLastIndex((message) => message.role === 'user' &&
124
+ message.contextKind === undefined &&
125
+ message.metadata?.source !== 'extension');
126
+ if (index < 0)
127
+ index = messages.findLastIndex((message) => message.contextKind === 'summary');
128
128
  if (index < 0)
129
- return 0;
129
+ return messages.length;
130
130
  if (index > 0 && messages[index - 1]?.contextKind === 'environment')
131
131
  index--;
132
132
  return index;
@@ -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 { compactHistoryMayContainRetainedResources, contextualInstructionInsertIndex, insertContextualWorldState, latestContextualWorldStateGeneration, parsePersistedContextualWorldState, persistContextualWorldState, resetContextualWorldState, updateContextualWorldState } from '../runtime/context/world-state.js';
5
+ import { contextualInstructionInsertIndex, insertContextualWorldState, latestContextualWorldStateGeneration, parsePersistedContextualWorldState, persistContextualWorldState, resetContextualWorldState, updateContextualWorldState } from '../runtime/context/world-state.js';
6
6
  import { eventForPersistence, publicModelUsage } from '../runtime/event-projection.js';
7
7
  import { ExecutionLoop } from '../runtime/execution.js';
8
8
  import { closeModelAdapters, createModelTurnSession } from './model-runtime.js';
@@ -11,7 +11,7 @@ import { discoveredMcpToolNames, rebindMcpToolSearchOutputs } from '../tools/mcp
11
11
  import { currentExecutionImages } from '../tools/output-projection.js';
12
12
  import { createHash, randomUUID } from 'node:crypto';
13
13
  import { ExecutionToolSources } from './extension-tools.js';
14
- import { compactExtensionMessages, compactExtensionResult } from '../extensions/compaction.js';
14
+ import { compactExtensionMessages, compactExtensionResult, compactExtensionDiagnostic } from '../extensions/compaction.js';
15
15
  import { ExtensionRequestBridge, ExtensionRequestSnapshots } from '../extensions/model-request.js';
16
16
  import { normalizeSessionMessagesInput, sessionMessagesPage } from '../extensions/session-messages.js';
17
17
  import { transformCoreModelStream } from '../extensions/model-stream.js';
@@ -38,7 +38,7 @@ import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
38
38
  import { loadAgentInstructions, renderAgentInstructions } from '../runtime/context/instructions.js';
39
39
  import { normalizeMessagesForModel } from '../runtime/context/normalize.js';
40
40
  import { RolloutBudget } from '../runtime/rollout-budget.js';
41
- import { compactSummaryFromCheckpoint } from '../session/jsonl-store.js';
41
+ import { compactSummaryFromCheckpoint, inputMetadataFromRecord } from '../session/jsonl-store.js';
42
42
  import { SubagentSessionController } from '../subagent/session-controller.js';
43
43
  import { agentStartToolDefinitionWithModels } from '../tools/agent/agent-start.js';
44
44
  import { allowsDirectTool, allowsToolCaller, toolCallers } from '../tools/callers.js';
@@ -386,7 +386,6 @@ export function createSessionOpener(services) {
386
386
  sessionRuntime.contextualWorldState = contextualWorldState;
387
387
  }
388
388
  const compactMayContainAgentInstructions = contextRecords.some((record) => compactSummaryFromCheckpoint(record) !== undefined);
389
- const compactMayContainRetainedResources = compactHistoryMayContainRetainedResources(contextRecords);
390
389
  const restored = await restoreMessages(store, sessionId, contextRecords, {
391
390
  includeImages: mode !== 'compact'
392
391
  });
@@ -440,9 +439,14 @@ export function createSessionOpener(services) {
440
439
  executionId,
441
440
  turnId
442
441
  }, { signal: call.signal });
443
- committed(content, record.recordId);
442
+ const inputMetadata = inputMetadataFromRecord(record);
443
+ compactionUserMessages.push({
444
+ content: content.modelText ?? content.text,
445
+ metadata: inputMetadata
446
+ });
447
+ committed(content, record.recordId, inputMetadata);
444
448
  for (const text of content.modelContext ?? content.context ?? [])
445
- messages.push({ role: 'user', content: text });
449
+ messages.push({ role: 'user', content: text, contextKind: 'contextual' });
446
450
  });
447
451
  };
448
452
  const appendMailboxMessages = async () => inputs.drain(async (entry, request, call) => {
@@ -476,12 +480,13 @@ export function createSessionOpener(services) {
476
480
  : {
477
481
  source: extensionInputSource(call, request.input.provenance, request.input.modelAttribution),
478
482
  ...(parent?.delivery ? { messageId: parent.delivery.messageId } : {})
479
- }, call, (actual, recordId) => {
483
+ }, call, (actual, recordId, metadata) => {
480
484
  outcome = { changed: true, executionId, recordId };
481
485
  recordSessionInputCommit(entry, outcome);
482
486
  messages.push({
483
487
  role: 'user',
484
488
  content: actual.modelText ?? actual.text,
489
+ metadata,
485
490
  ...(actual.images?.length
486
491
  ? {
487
492
  images: actual.images.map(({ mimeType, dataBase64 }) => ({
@@ -739,21 +744,26 @@ export function createSessionOpener(services) {
739
744
  subagents: subagents?.listSessionResources() ?? []
740
745
  };
741
746
  };
742
- let currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
747
+ const resetCompactWorldState = async (generation) => {
748
+ agentInstructionInsertIndex = contextualInstructionInsertIndex(messages);
749
+ contextualWorldState = resetContextualWorldState(generation, agentInstructionInsertIndex, {
750
+ instructionSnapshot: currentInstructionSnapshot,
751
+ instructionContent: renderAgentInstructions(instructionEntries, true),
752
+ resourceSnapshot: retainedSessionResourcesSnapshot(await loadRetainedSessionResources())
753
+ });
754
+ sessionRuntime.contextualWorldState = contextualWorldState;
755
+ await persistContextualWorldState(store, sessionId, contextualWorldState);
756
+ };
757
+ // A missing sidecar is rebuilt only for the latest Compact, never for a GC boundary.
758
+ if (!contextualWorldState.initialized &&
759
+ contextRecords.some((record) => record.recordId === contextualWorldStateGeneration &&
760
+ compactSummaryFromCheckpoint(record) !== undefined))
761
+ await resetCompactWorldState(contextualWorldStateGeneration);
743
762
  const previousContextualWorldState = contextualWorldState;
744
763
  contextualWorldState = updateContextualWorldState(contextualWorldState, agentInstructionInsertIndex, {
745
764
  instructionSnapshot: currentInstructionSnapshot,
746
765
  instructionContent: renderAgentInstructions(instructionEntries, compactMayContainAgentInstructions ||
747
- contextualWorldState.instructionSnapshot !== undefined),
748
- resourceSnapshot: currentResourceSnapshot,
749
- previousMayContainResources: (!contextualWorldState.initialized && compactMayContainRetainedResources) ||
750
- (host.listSessionResources !== undefined &&
751
- currentResourceSnapshot === undefined &&
752
- messages
753
- .slice(contextualWorldState.entries.findLast((entry) => entry.kind === 'resources')
754
- ?.index ?? 0)
755
- .some((message) => message.role === 'tool_call' &&
756
- ['exec', 'code_exec', 'agent_start'].includes(message.name ?? '')))
766
+ contextualWorldState.instructionSnapshot !== undefined)
757
767
  });
758
768
  sessionRuntime.contextualWorldState = contextualWorldState;
759
769
  if (!isDeepStrictEqual(previousContextualWorldState, contextualWorldState))
@@ -958,14 +968,17 @@ export function createSessionOpener(services) {
958
968
  ? {}
959
969
  : { inputSource: internal.inputSource }),
960
970
  ...(input.clientMessageId ? { clientMessageId: input.clientMessageId } : {})
961
- }, call, (actual, recordId) => {
971
+ }, call, (actual, recordId, metadata) => {
962
972
  entered = true;
963
- compactionUserMessages.push(actual.text);
964
973
  modelInputImages = actual.images?.map(({ mimeType, dataBase64 }) => ({
965
974
  mimeType,
966
975
  dataBase64
967
976
  }));
968
- messages.push({ role: 'user', content: actual.modelText ?? actual.text });
977
+ messages.push({
978
+ role: 'user',
979
+ content: actual.modelText ?? actual.text,
980
+ metadata
981
+ });
969
982
  currentExecutionUserIndex = messages.length - 1;
970
983
  internal.inputCommitted?.(recordId);
971
984
  internal.delivery?.applied();
@@ -1019,6 +1032,8 @@ export function createSessionOpener(services) {
1019
1032
  return undefined;
1020
1033
  const summaryTokenBudget = Math.min(10_000, selectedModel.maxOutputTokens, Math.max(1, Math.floor(selectedModel.contextWindowTokens / 4)));
1021
1034
  const highDensityCompaction = selectedModel.highDensityCompaction;
1035
+ let diagnosticReports = 0;
1036
+ let acceptsDiagnostics = true;
1022
1037
  try {
1023
1038
  const result = await compaction.generate((signal) => operation((call) => coreExtensions.hooks.invoke('compact.generate', Object.freeze({
1024
1039
  operationId: call.operationId,
@@ -1026,15 +1041,33 @@ export function createSessionOpener(services) {
1026
1041
  messages: compactExtensionMessages(history),
1027
1042
  ...(focus === undefined ? {} : { focus }),
1028
1043
  summaryTokenBudget,
1029
- getPrompt: () => buildCompactionMessages('', highDensityCompaction)[1].content
1044
+ getPrompt: () => buildCompactionMessages('', highDensityCompaction)[1].content,
1045
+ reportDiagnostic: async (value) => {
1046
+ if (!acceptsDiagnostics || ++diagnosticReports > 8)
1047
+ return;
1048
+ try {
1049
+ const diagnostic = compactExtensionDiagnostic(value);
1050
+ if (diagnostic)
1051
+ await compaction.observeExtension(diagnostic);
1052
+ }
1053
+ catch {
1054
+ // Diagnostic failures must not change compaction or fallback.
1055
+ }
1056
+ }
1030
1057
  }), Object.freeze({ ...call, signal }), async () => undefined)));
1031
1058
  compaction.signal.throwIfAborted();
1032
- return compactExtensionResult(result, summaryTokenBudget);
1059
+ const handled = compactExtensionResult(result, summaryTokenBudget);
1060
+ await compaction.finishExtension(handled ? 'HANDLED' : result === undefined ? 'DECLINED' : 'INVALID_RESULT');
1061
+ return handled;
1033
1062
  }
1034
1063
  catch {
1064
+ await compaction.finishExtension('FAILED');
1035
1065
  compaction.signal.throwIfAborted();
1036
1066
  return undefined;
1037
1067
  }
1068
+ finally {
1069
+ acceptsDiagnostics = false;
1070
+ }
1038
1071
  };
1039
1072
  let manualExtension;
1040
1073
  if (mode === 'compact' && coreExtensions.hooks.hasInterceptors('compact.generate')) {
@@ -1099,7 +1132,7 @@ export function createSessionOpener(services) {
1099
1132
  return false;
1100
1133
  let compactSummary = '';
1101
1134
  const current = messages.at(-1);
1102
- const retainCurrent = current.role === 'user' && current.contextKind !== 'environment';
1135
+ const retainCurrent = current.role === 'user' && current.contextKind === undefined;
1103
1136
  const compactHistory = retainCurrent ? messages.slice(0, -1) : [...messages];
1104
1137
  const appendBoundary = pendingAppends.length;
1105
1138
  const compactSystemPrompt = await operation((call) => coreExtensions.compose(promptComposition(executionTools, 'run'), call));
@@ -1188,11 +1221,21 @@ export function createSessionOpener(services) {
1188
1221
  if (!compactSummary)
1189
1222
  throw new MarAgentError('MAR_AGENT_COMPACT_FAILED', 'Automatic compact failed.');
1190
1223
  rolloutBudget?.recordUsage(latestUsage);
1191
- let replacementHistory = buildCompactedHistory(compactSummary, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent ? current.content : undefined);
1224
+ let replacementHistory = buildCompactedHistory(compactSummary, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent
1225
+ ? {
1226
+ content: current.content,
1227
+ ...(current.metadata ? { metadata: current.metadata } : {})
1228
+ }
1229
+ : undefined);
1192
1230
  signal.throwIfAborted();
1193
1231
  const compactRecord = await writeCompactionRecord(compactSummary, true, signal, async (actual, options) => {
1194
1232
  compactSummary = actual;
1195
- replacementHistory = buildCompactedHistory(actual, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent ? current.content : undefined);
1233
+ replacementHistory = buildCompactedHistory(actual, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent
1234
+ ? {
1235
+ content: current.content,
1236
+ ...(current.metadata ? { metadata: current.metadata } : {})
1237
+ }
1238
+ : undefined);
1196
1239
  const retainedModelContext = pendingAppends
1197
1240
  .slice(appendBoundary)
1198
1241
  .map((entry) => entry.message);
@@ -1218,18 +1261,12 @@ export function createSessionOpener(services) {
1218
1261
  projectedExecutionBudgetStage = undefined;
1219
1262
  recordedEnvironment = executionEnvironment;
1220
1263
  compactionUserMessages = replacementHistory
1221
- .filter((message) => message.kind === 'user_input')
1222
- .map((message) => message.content);
1223
- agentInstructionInsertIndex = Math.max(0, messages.length - 1);
1224
- currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
1225
- contextualWorldState = resetContextualWorldState(compactRecord.recordId, agentInstructionInsertIndex, {
1226
- instructionSnapshot: currentInstructionSnapshot,
1227
- instructionContent: renderAgentInstructions(instructionEntries, true),
1228
- resourceSnapshot: currentResourceSnapshot,
1229
- previousMayContainResources: compactSummary.includes('# Retained Session resources')
1230
- });
1231
- sessionRuntime.contextualWorldState = contextualWorldState;
1232
- await persistContextualWorldState(store, sessionId, contextualWorldState);
1264
+ .filter((message) => message.kind !== 'summary')
1265
+ .map((message) => ({
1266
+ content: message.content,
1267
+ ...(message.metadata ? { metadata: { ...message.metadata } } : {})
1268
+ }));
1269
+ await resetCompactWorldState(compactRecord.recordId);
1233
1270
  lastServerContextTokens = undefined;
1234
1271
  lastServerUsage = undefined;
1235
1272
  requestReasoningEffort = reasoningEffort;
@@ -1399,17 +1436,10 @@ export function createSessionOpener(services) {
1399
1436
  projectedExecutionBudgetStage = undefined;
1400
1437
  recordedEnvironment = refreshed.environmentState;
1401
1438
  compactionUserMessages = [...refreshed.userMessages];
1402
- agentInstructionInsertIndex =
1403
- refreshed.currentTurnUserIndex ?? Math.max(0, messages.length - 1);
1404
- if (agentInstructionInsertIndex &&
1405
- messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
1406
- agentInstructionInsertIndex--;
1407
- currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
1439
+ agentInstructionInsertIndex = contextualInstructionInsertIndex(messages);
1408
1440
  contextualWorldState = resetContextualWorldState(latestContextualWorldStateGeneration(committed.records), agentInstructionInsertIndex, {
1409
1441
  instructionSnapshot: currentInstructionSnapshot,
1410
- instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined)),
1411
- resourceSnapshot: currentResourceSnapshot,
1412
- previousMayContainResources: compactHistoryMayContainRetainedResources(committed.records)
1442
+ instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined))
1413
1443
  });
1414
1444
  sessionRuntime.contextualWorldState = contextualWorldState;
1415
1445
  await persistContextualWorldState(store, sessionId, contextualWorldState);
@@ -1439,8 +1469,8 @@ export function createSessionOpener(services) {
1439
1469
  manualCompaction.signal.throwIfAborted();
1440
1470
  await manualCompaction.dispose();
1441
1471
  }
1442
- if (mode === 'compact')
1443
- await writeCompactionRecord(compactSummary, false, compactSignal, async (actual, options) => {
1472
+ if (mode === 'compact') {
1473
+ const compactRecord = await writeCompactionRecord(compactSummary, false, compactSignal, async (actual, options) => {
1444
1474
  const retainedModelContext = pendingAppends
1445
1475
  .slice(manualAppendBoundary)
1446
1476
  .map((entry) => entry.message);
@@ -1460,6 +1490,8 @@ export function createSessionOpener(services) {
1460
1490
  selected.resetContinuation?.();
1461
1491
  return record;
1462
1492
  });
1493
+ await resetCompactWorldState(compactRecord.recordId);
1494
+ }
1463
1495
  if (mode === 'compact' && manualCompactionId !== undefined)
1464
1496
  await emit({ type: 'context.compacted', compactionId: manualCompactionId });
1465
1497
  };
@@ -1861,6 +1893,7 @@ function projectAppendedModelMessage(input) {
1861
1893
  return undefined;
1862
1894
  return {
1863
1895
  role: 'user',
1896
+ contextKind: 'contextual',
1864
1897
  content: input.content,
1865
1898
  ...(input.textBlocks === undefined
1866
1899
  ? {}
@@ -222,16 +222,27 @@ export function parseContextGcExecResult(value) {
222
222
  parsed.status !== 'completed' &&
223
223
  parsed.status !== 'cancelled') ||
224
224
  typeof parsed.stdout !== 'string' ||
225
- typeof parsed.stderr !== 'string')
225
+ (parsed.stderr !== undefined && typeof parsed.stderr !== 'string') ||
226
+ (parsed.truncated === true &&
227
+ (parsed.stdoutBytes === undefined || parsed.stderrBytes === undefined)))
226
228
  return undefined;
227
- const metadata = parseContextGcExecMetadata(parsed);
229
+ const stderr = parsed.stderr === undefined ? '' : parsed.stderr;
230
+ // Native exec bodies omit only empty/default fields and exactly derivable byte counts.
231
+ const defaults = {
232
+ truncated: false,
233
+ stdoutBytes: Buffer.byteLength(parsed.stdout),
234
+ stderrBytes: Buffer.byteLength(stderr),
235
+ stdoutDroppedBytes: 0,
236
+ stderrDroppedBytes: 0
237
+ };
238
+ const metadata = parseContextGcExecMetadata({ ...defaults, ...parsed });
228
239
  return metadata
229
240
  ? {
230
241
  source: 'STRUCTURED',
231
242
  ...metadata,
232
243
  status: parsed.status,
233
244
  stdout: parsed.stdout,
234
- stderr: parsed.stderr
245
+ stderr
235
246
  }
236
247
  : undefined;
237
248
  }
@@ -1,5 +1,5 @@
1
1
  import { type CoreCheckpoint, type CoreJsonValue } from '../extensions/data.js';
2
- import type { ModelMessage, ModelUsage } from '../model/contracts.js';
2
+ import type { ModelInputMetadata, ModelMessage, ModelUsage } from '../model/contracts.js';
3
3
  import type { EnvironmentContextState } from '../runtime/context/contracts.js';
4
4
  import { type CodeModeStateEntry } from '../tools/code-mode/state.js';
5
5
  import { type TodoSnapshot } from '../tools/agent/todo.js';
@@ -29,10 +29,13 @@ export interface SessionExecutionState {
29
29
  todo: TodoSnapshot;
30
30
  contextRecords: SessionRecord[];
31
31
  }
32
- export interface CompactHistoryMessage {
33
- role: 'user';
32
+ export interface CompactUserInput {
34
33
  content: string;
35
- kind: 'user_input' | 'summary';
34
+ metadata?: ModelInputMetadata;
35
+ }
36
+ export interface CompactHistoryMessage extends CompactUserInput {
37
+ role: 'user';
38
+ kind: 'user_input' | 'extension_input' | 'summary';
36
39
  }
37
40
  export interface SessionInputImage {
38
41
  readonly name: string;
@@ -247,3 +250,5 @@ export declare class JsonlSessionStore {
247
250
  }
248
251
  export declare function compactSummaryFromCheckpoint(record: SessionRecord | undefined): string | undefined;
249
252
  export declare function replacementHistoryFromCheckpoint(record: SessionRecord): CompactHistoryMessage[] | undefined;
253
+ export declare function parseModelInputMetadata(value: unknown): ModelInputMetadata | undefined;
254
+ export declare function inputMetadataFromRecord(record: SessionRecord): ModelInputMetadata;
@@ -2455,11 +2455,58 @@ export function replacementHistoryFromCheckpoint(record) {
2455
2455
  typeof message === 'object' &&
2456
2456
  message.role === 'user' &&
2457
2457
  typeof message.content === 'string' &&
2458
+ (message.metadata === undefined ||
2459
+ (parseModelInputMetadata(message.metadata) !== undefined &&
2460
+ (message.kind === 'extension_input'
2461
+ ? message.metadata.source === 'extension'
2462
+ : message.kind === 'user_input' && message.metadata.source !== 'extension'))) &&
2458
2463
  (message.kind === 'user_input' ||
2464
+ message.kind === 'extension_input' ||
2459
2465
  (message.kind === 'summary' && message.content.trim().length > 0))) ||
2460
2466
  history.filter((message) => message.kind === 'summary').length !== 1)
2461
2467
  return undefined;
2462
- return history.map(({ role, content, kind }) => ({ role, content, kind }));
2468
+ return history.map(({ role, content, kind, metadata }) => ({
2469
+ role,
2470
+ content,
2471
+ kind,
2472
+ ...(parseModelInputMetadata(metadata) === undefined
2473
+ ? {}
2474
+ : { metadata: parseModelInputMetadata(metadata) })
2475
+ }));
2476
+ }
2477
+ export function parseModelInputMetadata(value) {
2478
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
2479
+ return undefined;
2480
+ const metadata = value;
2481
+ if (metadata.source !== 'user' &&
2482
+ metadata.source !== 'parent_agent' &&
2483
+ metadata.source !== 'extension')
2484
+ return undefined;
2485
+ return {
2486
+ source: metadata.source,
2487
+ ...(typeof metadata.recordId === 'string' && metadata.recordId.length <= 128
2488
+ ? { recordId: metadata.recordId }
2489
+ : {}),
2490
+ ...(typeof metadata.extensionId === 'string' && metadata.extensionId.length <= 128
2491
+ ? { extensionId: metadata.extensionId }
2492
+ : {}),
2493
+ ...(metadata.truncated === true ? { truncated: true } : {})
2494
+ };
2495
+ }
2496
+ export function inputMetadataFromRecord(record) {
2497
+ const payload = record.payload;
2498
+ const source = payload.source;
2499
+ return {
2500
+ source: record.type === 'extension.input' || payload.inputSource === 'extension'
2501
+ ? 'extension'
2502
+ : payload.inputSource === 'parent_agent'
2503
+ ? 'parent_agent'
2504
+ : 'user',
2505
+ recordId: record.recordId,
2506
+ ...(typeof source?.extensionId === 'string' && source.extensionId.length <= 128
2507
+ ? { extensionId: source.extensionId }
2508
+ : {})
2509
+ };
2463
2510
  }
2464
2511
  function isCompactCheckpoint(record) {
2465
2512
  return compactSummaryFromCheckpoint(record) !== undefined;
@@ -145,17 +145,35 @@ export function newObservationId() {
145
145
  return randomUUID();
146
146
  }
147
147
  export function observationModelParts(message) {
148
- if (message.textBlocks !== undefined || !message.observations?.length)
148
+ if (message.textBlocks !== undefined || message.observations === undefined)
149
149
  return undefined;
150
150
  const prefix = [];
151
151
  try {
152
152
  const parsed = JSON.parse(message.content ?? '');
153
153
  if (parsed && !Array.isArray(parsed) && typeof parsed === 'object') {
154
154
  const { observations: _observations, ...metadata } = parsed;
155
+ const cellEnvelope = typeof metadata.cellId === 'string' &&
156
+ typeof metadata.status === 'string' &&
157
+ Array.isArray(_observations);
158
+ if (!message.observations.length && !cellEnvelope)
159
+ return undefined;
160
+ if (cellEnvelope) {
161
+ // Keep execution facts in storage; project only actionable lifecycle metadata.
162
+ delete metadata.toolCalls;
163
+ delete metadata.outputBytes;
164
+ if (metadata.outputDroppedBytes === 0)
165
+ delete metadata.outputDroppedBytes;
166
+ if (metadata.truncated === false)
167
+ delete metadata.truncated;
168
+ if (metadata.cached === false)
169
+ delete metadata.cached;
170
+ }
155
171
  prefix.push({ type: 'text', text: JSON.stringify(metadata) });
156
172
  }
157
173
  }
158
174
  catch { }
175
+ if (!message.observations.length && !prefix.length)
176
+ return undefined;
159
177
  return [
160
178
  ...prefix,
161
179
  ...message.observations.flatMap((value) => {
@@ -1,10 +1,12 @@
1
1
  import { type LocalProcessExecutor } from '../../host/local-process-executor.js';
2
2
  import type { ClientToolDefinition } from '../../model/contracts.js';
3
- import type { ExecResult } from '../contracts.js';
3
+ import type { ExecResult, ToolExecutionResult } from '../contracts.js';
4
4
  import { WorkspacePathResolver } from '../shared/path-resolver.js';
5
5
  import { type CoreProcessInput, type CoreProcessHandle } from '../../extensions/processes.js';
6
6
  import type { CoreOperationContext } from '../../extensions/operation.js';
7
7
  export type { ExecResult } from '../contracts.js';
8
+ /** Share the model-facing body across direct calls and original Code Mode observations. */
9
+ export declare function formatExecResult(result: ExecResult): ToolExecutionResult;
8
10
  export type ExecInput = {
9
11
  action?: 'start';
10
12
  command: string;
@@ -4,6 +4,27 @@ import { MarAgentError } from '../../error.js';
4
4
  import { directLocalProcessExecutor, signalProcessTree } from '../../host/local-process-executor.js';
5
5
  import { TOOL_EXECUTION_LIMITS } from '../execution-limits.js';
6
6
  import { validateCoreProcessInput } from '../../extensions/processes.js';
7
+ /** Share the model-facing body across direct calls and original Code Mode observations. */
8
+ export function formatExecResult(result) {
9
+ const visible = { ...result };
10
+ if (result.stderr === '')
11
+ delete visible.stderr;
12
+ if (!result.truncated) {
13
+ delete visible.truncated;
14
+ // Incremental output may be smaller than the cumulative stream counts.
15
+ if (result.stdoutBytes === Buffer.byteLength(result.stdout))
16
+ delete visible.stdoutBytes;
17
+ if (result.stderrBytes === Buffer.byteLength(result.stderr))
18
+ delete visible.stderrBytes;
19
+ }
20
+ if (result.stdoutDroppedBytes === 0)
21
+ delete visible.stdoutDroppedBytes;
22
+ if (result.stderrDroppedBytes === 0)
23
+ delete visible.stderrDroppedBytes;
24
+ if (result.cached === false)
25
+ delete visible.cached;
26
+ return { content: JSON.stringify(visible), data: result, truncated: result.truncated };
27
+ }
7
28
  export const execToolDefinition = {
8
29
  name: 'exec',
9
30
  allowedCallers: ['direct', 'programmatic'],
@@ -3,7 +3,7 @@ import { LocalFileAccess } from '../../host/local-file-access.js';
3
3
  import { ApplyPatchTool, applyPatchToolInput, applyPatchToolDefinition } from './apply-patch.js';
4
4
  import { CodeModeTool, codeModeToolDefinition, codeExecWaitToolDefinition } from '../code-mode/index.js';
5
5
  import { normalizeToolInput } from '../input.js';
6
- import { ExecTool, execToolInput, execToolDefinition, execWaitToolDefinition } from './exec.js';
6
+ import { ExecTool, execToolInput, execToolDefinition, execWaitToolDefinition, formatExecResult } from './exec.js';
7
7
  import { ReadTool, formatBatchReadOutput, readToolDefinition } from './read.js';
8
8
  import { WorkspacePathResolver } from '../shared/path-resolver.js';
9
9
  import { ViewImageTool, viewImageToolDefinition } from './view-image.js';
@@ -91,7 +91,7 @@ export class LocalToolRegistry {
91
91
  : { ownerInstanceId: context.operation.ownerInstanceId })
92
92
  })
93
93
  });
94
- return { content: JSON.stringify(result), data: result, truncated: result.truncated };
94
+ return formatExecResult(result);
95
95
  }
96
96
  ])
97
97
  ]);
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export type { CoreAgentExtensionContext, CoreSessionExtensionContext, CoreRuntim
13
13
  export type { CoreDisposer, CoreResourceRegistration, CoreResourceScope } from './agent-core/extensions/resources.js';
14
14
  export type { CoreHookOptions, CoreInterceptor } from './agent-core/extensions/hooks.js';
15
15
  export type { CoreOperationContext } from './agent-core/extensions/operation.js';
16
- export type { CoreCompactContext, CoreCompactMessage, CoreCompactResult } from './agent-core/extensions/compaction.js';
16
+ export type { CoreCompactContext, CoreCompactDiagnostic, CoreCompactMessage, CoreCompactResult } from './agent-core/extensions/compaction.js';
17
17
  export { buildCompactionInput as buildCompactEvidence } from './agent-core/runtime/compaction/history.js';
18
18
  export { countModelTokens } from './agent-core/runtime/token-budget.js';
19
19
  export type { CoreServiceKey, CoreServiceRegistry } from './agent-core/extensions/services.js';
@@ -1,5 +1,5 @@
1
1
  import { type MarAgentModelConfiguration, type MarAgentReasoningEffort } from '../agent-core/model/configuration.js';
2
- import type { ModelUsage } from '../agent-core/model/contracts.js';
2
+ import type { ModelAttemptDiagnostic, ModelUsage } from '../agent-core/model/contracts.js';
3
3
  import type { CoreCompactMessage } from '../agent-core/extensions/compaction.js';
4
4
  export interface GenerateModelTextInput {
5
5
  readonly model: MarAgentModelConfiguration;
@@ -8,6 +8,7 @@ export interface GenerateModelTextInput {
8
8
  readonly reasoningEffort?: MarAgentReasoningEffort;
9
9
  readonly outputLimit?: number;
10
10
  readonly signal: AbortSignal;
11
+ readonly onAttemptDiagnostic?: (diagnostic: ModelAttemptDiagnostic) => Promise<void> | void;
11
12
  }
12
13
  /** One tool-free request using the SDK transports. Credential providers remain caller-owned. */
13
14
  export declare function generateModelText(input: GenerateModelTextInput): Promise<{
@@ -22,6 +22,7 @@ export async function generateModelText(input) {
22
22
  allowTools: false,
23
23
  toolChoice: 'none',
24
24
  reasoningEffort: input.reasoningEffort ?? resolveModelDefaultReasoningEffort(input.model),
25
+ ...(input.onAttemptDiagnostic ? { onAttemptDiagnostic: input.onAttemptDiagnostic } : {}),
25
26
  ...(input.outputLimit === undefined ? {} : { outputLimit: input.outputLimit })
26
27
  }, input.signal)) {
27
28
  input.signal.throwIfAborted();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.9.171",
3
+ "version": "0.9.173",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",