@myagentroam/node 0.11.42 → 0.11.43

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 (29) hide show
  1. package/dist/connector.js +9 -2
  2. package/dist/hosting/hosted-conversation-node-service.js +1 -2
  3. package/dist/marctl-guidance.js +3 -1
  4. package/dist/runner/abstract-runner.js +2 -2
  5. package/dist/runner/claude/managed-run-controller.js +1 -1
  6. package/dist/runner/claude/native-transcript.js +1 -1
  7. package/dist/runner/claude/tool-normalizer.js +21 -12
  8. package/dist/runner/codex/conversation-parser.js +17 -9
  9. package/dist/runner/codex/managed-run-controller.js +1 -1
  10. package/dist/runner/codex/tool-normalizer.js +17 -9
  11. package/dist/runner/execution-presentation.js +0 -3
  12. package/dist/runner/mar-agent/agent-network-extension.js +2 -2
  13. package/dist/runner/mar-agent/conversation-normalizer.js +259 -43
  14. package/dist/runner/mar-agent/managed-run-controller.js +2 -1
  15. package/dist/runner/mar-agent/runtime-model-factory.js +2 -4
  16. package/dist/runner/mar-agent-runner.js +32 -8
  17. package/dist/runner/normalization.js +8 -0
  18. package/dist/runner/opencode/tool-normalizer.js +8 -4
  19. package/dist/runtime-state.js +0 -1
  20. package/dist/service/agent-network-bindings.js +4 -2
  21. package/dist/service/agent-network-invoke.js +5 -2
  22. package/dist/service/agent-network-observations.js +15 -13
  23. package/dist/service/agent-network-service.js +39 -15
  24. package/dist/service/goal-continuation-coordinator.js +5 -0
  25. package/dist/service/runner-service.js +7 -3
  26. package/dist/session/runtime/conversation-projection.js +3 -1
  27. package/dist/session/session-lifecycle-service.js +7 -4
  28. package/dist/session/session-message-service.js +4 -2
  29. package/package.json +4 -4
@@ -7,7 +7,41 @@ export function normalizeMarAgentUsage(usage) {
7
7
  import { boundedConversationItemId, compactRunnerText, compactRunnerValue } from '../normalization.js';
8
8
  import { runnerErrorProjection } from '../runner-error.js';
9
9
  import { toolPresentationSchema } from '@myagentroam/protocol';
10
- import { boundExecutionPresentation, publicOutputSnapshot } from '../execution-presentation.js';
10
+ import { boundExecutionPresentation } from '../execution-presentation.js';
11
+ import { publicOutputSnapshot } from '../normalization.js';
12
+ function boundCodeOutputSnapshots(sources) {
13
+ for (const channel of ['outputPreview', 'stdout', 'stderr']) {
14
+ const text = (entry) => channel === 'outputPreview' ? entry.outputPreview : entry.displayOutput[channel];
15
+ let excess = [...sources.values()].reduce((length, entry) => length + text(entry).length, 0) - 10_000;
16
+ for (const [id, entry] of sources) {
17
+ if (excess <= 0)
18
+ break;
19
+ const before = text(entry);
20
+ if (!before)
21
+ continue;
22
+ let removed = Math.min(excess, before.length);
23
+ if (/^[\uDC00-\uDFFF]/u.test(before.slice(removed)))
24
+ removed++;
25
+ const tail = before.slice(removed);
26
+ if (channel === 'outputPreview') {
27
+ sources.set(id, { ...entry, outputPreview: tail, truncated: true });
28
+ }
29
+ else {
30
+ const dropped = channel === 'stdout' ? 'stdoutDroppedBytes' : 'stderrDroppedBytes';
31
+ sources.set(id, {
32
+ ...entry,
33
+ truncated: true,
34
+ displayOutput: {
35
+ ...entry.displayOutput,
36
+ [channel]: tail,
37
+ [dropped]: entry.displayOutput[dropped] + Buffer.byteLength(before.slice(0, removed))
38
+ }
39
+ });
40
+ }
41
+ excess -= removed;
42
+ }
43
+ }
44
+ }
11
45
  export class MarAgentConversationNormalizer {
12
46
  input(entry) {
13
47
  return {
@@ -32,6 +66,7 @@ export class MarAgentConversationNormalizer {
32
66
  #codeModeSourceCallIds = new Set();
33
67
  #codeModeOutputCalls = new Map();
34
68
  #codeModeOutputSnapshots = new Map();
69
+ #codeModeOutputArchive = new Map();
35
70
  #runningCodeModeCells = new Map();
36
71
  #codeModeCellWaiters = new Map();
37
72
  messageItemId(nativeItemId) {
@@ -234,6 +269,8 @@ export class MarAgentConversationNormalizer {
234
269
  }
235
270
  #toolOutput(event) {
236
271
  const { callId, toolName, executionId, turnId, parentCallId, output } = event;
272
+ if (output.mode === 'delta')
273
+ return this.#toolOutputDelta(event);
237
274
  const inputOutput = [output.stdout, output.stderr].filter(Boolean).join('\n');
238
275
  const displayOutput = execDisplayOutput(output.displayOutput) ?? {
239
276
  stdout: output.stdout,
@@ -248,9 +285,10 @@ export class MarAgentConversationNormalizer {
248
285
  const snapshot = {
249
286
  outputPreview: bounded.text,
250
287
  truncated: bounded.truncated || inputOutput.length > 10_000,
251
- displayOutput
288
+ displayOutput,
289
+ processId: output.processId
252
290
  };
253
- if (parentCallId !== undefined)
291
+ if (parentCallId !== undefined && (toolName === 'exec' || toolName === 'exec_wait'))
254
292
  return this.#codeModeToolOutput(callId, parentCallId, toolName, executionId, turnId, output.processId, snapshot);
255
293
  const state = this.#tools.get(callId);
256
294
  if (state === undefined ||
@@ -263,10 +301,15 @@ export class MarAgentConversationNormalizer {
263
301
  if (typeof input?.processId === 'string' && input.processId !== output.processId)
264
302
  return [];
265
303
  if (state.commandOrigin === undefined) {
266
- if (toolName !== 'exec_wait')
304
+ if (toolName !== 'exec_wait' && toolName !== 'code_exec_wait')
267
305
  return [];
268
306
  return [
269
- genericToolItem(state.itemId, toolName, 'IN_PROGRESS', state.input, snapshot.outputPreview, null, snapshot.truncated, { processId: output.processId, displayOutput: snapshot.displayOutput })
307
+ genericToolItem(state.itemId, toolName, 'IN_PROGRESS', state.input, snapshot.outputPreview, null, snapshot.truncated, {
308
+ processId: output.processId,
309
+ displayOutput: snapshot.displayOutput,
310
+ outputHeadTruncated: snapshot.displayOutput.stdoutDroppedBytes > 0 ||
311
+ snapshot.displayOutput.stderrDroppedBytes > 0
312
+ })
270
313
  ];
271
314
  }
272
315
  const command = this.#commands.get(state.commandOrigin);
@@ -279,6 +322,93 @@ export class MarAgentConversationNormalizer {
279
322
  command.truncated = snapshot.truncated;
280
323
  return [commandItem(command, 'IN_PROGRESS')];
281
324
  }
325
+ #toolOutputDelta(event) {
326
+ const { callId, toolName, executionId, turnId, parentCallId, output } = event;
327
+ let target = this.#tools.get(callId);
328
+ if (parentCallId !== undefined) {
329
+ const child = this.#codeModeOutputCalls.get(callId);
330
+ const parent = this.#tools.get(parentCallId);
331
+ if (!child ||
332
+ child.parentCallId !== parentCallId ||
333
+ child.toolName !== toolName ||
334
+ child.executionId !== executionId ||
335
+ (child.turnId && turnId && child.turnId !== turnId) ||
336
+ !parent ||
337
+ parent.executionId !== executionId ||
338
+ (parent.turnId && turnId && parent.turnId !== turnId))
339
+ return [];
340
+ const input = record(child.input);
341
+ if (typeof input?.processId === 'string' && input.processId !== output.processId)
342
+ return [];
343
+ const display = execDisplayOutput(output.displayOutput);
344
+ if (display) {
345
+ const snapshots = this.#codeModeOutputSnapshots.get(parentCallId) ?? new Map();
346
+ const text = [display.stdout, display.stderr].filter(Boolean).join('\n');
347
+ const bounded = publicOutputSnapshot(text, display.stdoutDroppedBytes > 0 || display.stderrDroppedBytes > 0);
348
+ snapshots.set(callId, {
349
+ outputPreview: bounded.text,
350
+ truncated: bounded.truncated,
351
+ displayOutput: display,
352
+ processId: output.processId
353
+ });
354
+ this.#codeModeOutputSnapshots.set(parentCallId, snapshots);
355
+ }
356
+ target = parent.active
357
+ ? parent
358
+ : this.#runningCodeModeCells.get(parentCallId)?.executionId === executionId
359
+ ? this.#activeCodeModeWaiter(this.#runningCodeModeCells.get(parentCallId))
360
+ : undefined;
361
+ }
362
+ else {
363
+ if (!target ||
364
+ !target.active ||
365
+ target.toolName !== toolName ||
366
+ target.executionId !== executionId ||
367
+ (target.turnId && turnId && target.turnId !== turnId))
368
+ return [];
369
+ const input = record(target.input);
370
+ if (typeof input?.processId === 'string' && input.processId !== output.processId)
371
+ return [];
372
+ }
373
+ if (!target)
374
+ return [];
375
+ const command = target.commandOrigin === undefined ? undefined : this.#commands.get(target.commandOrigin);
376
+ const item = command
377
+ ? commandItem(command, 'IN_PROGRESS')
378
+ : genericToolItem(target.itemId, target.toolName, 'IN_PROGRESS', target.input, null, null);
379
+ const original = record(record(item.payload.toolPresentation)?.execution);
380
+ const blocks = [
381
+ ...(output.stdout ? [{ channel: 'STDOUT', text: output.stdout }] : []),
382
+ ...(output.stderr ? [{ channel: 'STDERR', text: output.stderr }] : [])
383
+ ];
384
+ const text = [output.stdout, output.stderr].filter(Boolean).join('\n');
385
+ const boundedText = publicOutputSnapshot(text, output.truncated);
386
+ const bounded = boundExecutionPresentation({
387
+ kind: parentCallId !== undefined
388
+ ? target.toolName === 'code_exec'
389
+ ? 'CODE'
390
+ : 'CONTROL'
391
+ : toolName === 'exec'
392
+ ? 'SHELL'
393
+ : 'CONTROL',
394
+ ...(typeof original?.toolName === 'string' ? { toolName: original.toolName } : {}),
395
+ output: blocks,
396
+ ...(output.truncated ? { truncation: { output: 'HEAD' } } : {})
397
+ });
398
+ return [
399
+ {
400
+ ...item,
401
+ append: 'output',
402
+ merge: true,
403
+ payload: {
404
+ ...item.payload,
405
+ outputPreview: boundedText.text,
406
+ output: boundedText,
407
+ toolPresentation: bounded.presentation
408
+ }
409
+ }
410
+ ];
411
+ }
282
412
  #codeModeToolOutput(callId, parentCallId, toolName, executionId, turnId, processId, snapshot) {
283
413
  const parent = this.#tools.get(parentCallId);
284
414
  const child = this.#codeModeOutputCalls.get(callId);
@@ -305,13 +435,30 @@ export class MarAgentConversationNormalizer {
305
435
  const snapshots = this.#codeModeOutputSnapshots.get(parentCallId) ?? new Map();
306
436
  snapshots.set(callId, snapshot);
307
437
  this.#codeModeOutputSnapshots.set(parentCallId, snapshots);
308
- const combined = [...snapshots.values()]
438
+ const sources = new Map(this.#codeModeOutputArchive.get(parentCallId));
439
+ for (const active of snapshots.values())
440
+ sources.set(active.processId, active);
441
+ const entries = [...sources.values()];
442
+ const combined = entries
309
443
  .map((entry) => entry.outputPreview)
310
444
  .filter(Boolean)
311
- .join('\n');
312
- const bounded = publicOutputSnapshot(combined, [...snapshots.values()].some((entry) => entry.truncated));
445
+ .join('');
446
+ const bounded = publicOutputSnapshot(combined, entries.some((entry) => entry.truncated));
313
447
  return [
314
- genericToolItem(target.itemId, target.toolName, 'IN_PROGRESS', target.input, bounded.text, null, bounded.truncated)
448
+ genericToolItem(target.itemId, target.toolName, 'IN_PROGRESS', target.input, bounded.text, null, bounded.truncated, {
449
+ outputHeadTruncated: entries.some((entry) => entry.displayOutput.stdoutDroppedBytes > 0 ||
450
+ entry.displayOutput.stderrDroppedBytes > 0),
451
+ displayOutput: {
452
+ stdout: entries
453
+ .map((entry) => entry.displayOutput.stdout)
454
+ .filter(Boolean)
455
+ .join(''),
456
+ stderr: entries
457
+ .map((entry) => entry.displayOutput.stderr)
458
+ .filter(Boolean)
459
+ .join('')
460
+ }
461
+ })
315
462
  ];
316
463
  }
317
464
  #trackCodeModeOutputCall(event) {
@@ -343,7 +490,16 @@ export class MarAgentConversationNormalizer {
343
490
  const call = this.#codeModeOutputCalls.get(callId);
344
491
  if (call?.parentCallId === parentCallId) {
345
492
  this.#codeModeOutputCalls.delete(callId);
346
- this.#codeModeOutputSnapshots.get(parentCallId)?.delete(callId);
493
+ const snapshots = this.#codeModeOutputSnapshots.get(parentCallId);
494
+ const completed = snapshots?.get(callId);
495
+ snapshots?.delete(callId);
496
+ if (completed) {
497
+ const archive = this.#codeModeOutputArchive.get(parentCallId) ??
498
+ new Map();
499
+ archive.set(completed.processId, completed);
500
+ boundCodeOutputSnapshots(archive);
501
+ this.#codeModeOutputArchive.set(parentCallId, archive);
502
+ }
347
503
  }
348
504
  }
349
505
  #trackCodeModeWait(event) {
@@ -415,6 +571,7 @@ export class MarAgentConversationNormalizer {
415
571
  }
416
572
  #clearCodeModeOutputSnapshots(parentCallId) {
417
573
  this.#codeModeOutputSnapshots.delete(parentCallId);
574
+ this.#codeModeOutputArchive.delete(parentCallId);
418
575
  for (const [callId, state] of this.#codeModeOutputCalls)
419
576
  if (state.parentCallId === parentCallId)
420
577
  this.#codeModeOutputCalls.delete(callId);
@@ -822,28 +979,43 @@ function validCompactAnchor(value) {
822
979
  return value !== undefined && value.length > 0 && value.length <= 128 ? value : undefined;
823
980
  }
824
981
  function codeObservationOutput(data, summary) {
982
+ const blocks = [];
983
+ const add = (channel, text) => {
984
+ if (typeof text !== 'string' || text.length === 0)
985
+ return;
986
+ const existing = blocks.find((block) => block.channel === channel);
987
+ if (existing)
988
+ existing.text += `\n${text}`;
989
+ else
990
+ blocks.push({ channel, text });
991
+ };
992
+ const streams = (value) => {
993
+ const display = record(value?.displayOutput) ?? value;
994
+ if (typeof display?.stdout !== 'string' && typeof display?.stderr !== 'string')
995
+ return false;
996
+ add('STDOUT', display?.stdout);
997
+ add('STDERR', display?.stderr);
998
+ return true;
999
+ };
1000
+ if (streams(data))
1001
+ return blocks;
825
1002
  if (Array.isArray(data?.observations)) {
826
- return data.observations
827
- .flatMap((entry) => {
1003
+ for (const entry of data.observations) {
828
1004
  const observation = record(entry);
829
- if (observation?.type === 'value' && typeof observation.value === 'string')
830
- return [observation.value];
1005
+ if (observation?.type === 'value')
1006
+ add('TEXT', observation.value);
831
1007
  const result = record(observation?.result);
832
1008
  const source = record(observation?.source);
833
- if (observation?.type === 'tool_result' &&
834
- (source?.toolName === 'exec' || source?.toolName === 'exec_wait')) {
835
- const resultData = record(result?.data);
836
- const display = record(resultData?.displayOutput) ?? resultData;
837
- if (typeof display?.stdout === 'string' || typeof display?.stderr === 'string')
838
- return [display?.stdout, display?.stderr].filter((value) => typeof value === 'string' && value.length > 0);
839
- }
840
- return observation?.type === 'tool_result' && typeof result?.content === 'string'
841
- ? [result.content]
842
- : [];
843
- })
844
- .join('\n');
1009
+ if (observation?.type !== 'tool_result')
1010
+ continue;
1011
+ if ((source?.toolName === 'exec' || source?.toolName === 'exec_wait') &&
1012
+ streams(record(result?.data)))
1013
+ continue;
1014
+ add('TEXT', result?.content);
1015
+ }
1016
+ return blocks.length ? blocks : [{ channel: 'TEXT', text: '' }];
845
1017
  }
846
- return summary ?? undefined;
1018
+ return summary === null ? undefined : [{ channel: 'TEXT', text: summary }];
847
1019
  }
848
1020
  function codeExecutionError(data) {
849
1021
  const error = record(data?.error);
@@ -876,7 +1048,14 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
876
1048
  preview.length > 10_000
877
1049
  }, status, 'Code execution', {
878
1050
  kind: 'CODE',
879
- ...(source.length > 10_000 ? { truncation: { input: true } } : {}),
1051
+ ...(source.length > 10_000 || resultData?.outputHeadTruncated === true
1052
+ ? {
1053
+ truncation: {
1054
+ ...(source.length > 10_000 ? { input: true } : {}),
1055
+ ...(resultData?.outputHeadTruncated === true ? { output: 'HEAD' } : {})
1056
+ }
1057
+ }
1058
+ : {}),
880
1059
  ...(errorCode === null
881
1060
  ? resultError === undefined
882
1061
  ? {}
@@ -887,8 +1066,8 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
887
1066
  message: compactRunnerText(outputSummary ?? '', 1000)
888
1067
  }
889
1068
  }),
890
- ...(codeOutput === undefined ? {} : { output: [{ channel: 'TEXT', text: codeOutput }] })
891
- });
1069
+ ...(codeOutput === undefined ? {} : { output: codeOutput })
1070
+ }, toolName);
892
1071
  }
893
1072
  if (toolName === 'code_exec_wait' || toolName === 'exec_wait') {
894
1073
  const inputRecord = record(input);
@@ -920,7 +1099,7 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
920
1099
  .join('\n');
921
1100
  return commandItem({
922
1101
  itemId: id,
923
- command: toolName,
1102
+ command: '',
924
1103
  cwd: null,
925
1104
  outputPreview: status === 'IN_PROGRESS' && outputSummary !== null
926
1105
  ? outputSummary
@@ -938,6 +1117,14 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
938
1117
  resultSummary.length > 10_000
939
1118
  }, status, toolName === 'code_exec_wait' ? 'Code execution wait' : 'Command wait', {
940
1119
  kind: 'CONTROL',
1120
+ toolName,
1121
+ action: compactRunnerText(action, 128),
1122
+ ...(typeof inputRecord?.processId === 'string' || typeof inputRecord?.cellId === 'string'
1123
+ ? {
1124
+ targetId: compactRunnerText(String(inputRecord.processId ?? inputRecord.cellId), 256)
1125
+ }
1126
+ : {}),
1127
+ ...executionSource(resultData?.executionSource),
941
1128
  ...(display === undefined
942
1129
  ? {}
943
1130
  : {
@@ -947,10 +1134,9 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
947
1134
  ]
948
1135
  }),
949
1136
  ...(display === undefined && legacyBlocks.length > 0 ? { output: legacyBlocks } : {}),
950
- ...(codeOutput === undefined
951
- ? {}
952
- : { output: [{ channel: 'TEXT', text: codeOutput }] }),
1137
+ ...(codeOutput === undefined ? {} : { output: codeOutput }),
953
1138
  ...((typeof inputRecord?.data === 'string' && inputRecord.data.length > 10_000) ||
1139
+ resultData?.outputHeadTruncated === true ||
954
1140
  (display !== undefined && (display.stdoutDroppedBytes || display.stderrDroppedBytes)) ||
955
1141
  (display === undefined && legacyBlocks.length > 0 && truncated)
956
1142
  ? {
@@ -958,8 +1144,9 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
958
1144
  ...(typeof inputRecord?.data === 'string' && inputRecord.data.length > 10_000
959
1145
  ? { input: true }
960
1146
  : {}),
961
- ...(display !== undefined &&
962
- (display.stdoutDroppedBytes || display.stderrDroppedBytes)
1147
+ ...(resultData?.outputHeadTruncated === true ||
1148
+ (display !== undefined &&
1149
+ (display.stdoutDroppedBytes || display.stderrDroppedBytes))
963
1150
  ? { output: 'HEAD' }
964
1151
  : display === undefined && legacyBlocks.length > 0 && truncated
965
1152
  ? { output: 'SOURCE' }
@@ -993,6 +1180,7 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
993
1180
  title: compactRunnerText(toolName, 500) || 'unknown',
994
1181
  inputSummary: inputSummary.text || null,
995
1182
  outputSummary: outputSummary === null ? null : compactRunnerText(outputSummary, 10_000),
1183
+ ...(outputSummary === null ? {} : { output: publicOutputSnapshot(outputSummary, truncated) }),
996
1184
  progressLabel: status === 'IN_PROGRESS' ? 'Running' : null,
997
1185
  errorCode: errorCode === null ? null : compactRunnerText(errorCode, 80),
998
1186
  truncated: truncated || inputSummary.truncated || (outputSummary?.length ?? 0) > 10_000,
@@ -1064,7 +1252,24 @@ function execDisplayOutput(value) {
1064
1252
  stderrDroppedBytes: data.stderrDroppedBytes
1065
1253
  };
1066
1254
  }
1067
- function commandItem(command, status, title, execution) {
1255
+ function executionSource(value) {
1256
+ const source = record(value);
1257
+ if (!source ||
1258
+ (source.kind !== 'SHELL' && source.kind !== 'CODE') ||
1259
+ typeof source.text !== 'string')
1260
+ return {};
1261
+ return {
1262
+ source: {
1263
+ kind: source.kind,
1264
+ text: compactRunnerText(source.text, 10_000),
1265
+ ...(typeof source.cwd === 'string' ? { cwd: compactRunnerText(source.cwd, 10_000) } : {}),
1266
+ truncated: source.truncated === true ||
1267
+ source.text.length > 10_000 ||
1268
+ (typeof source.cwd === 'string' && source.cwd.length > 10_000)
1269
+ }
1270
+ };
1271
+ }
1272
+ function commandItem(command, status, title, execution, toolName = 'exec') {
1068
1273
  const display = command.displayOutput;
1069
1274
  const detail = execution ?? {
1070
1275
  kind: 'SHELL',
@@ -1089,7 +1294,7 @@ function commandItem(command, status, title, execution) {
1089
1294
  ]
1090
1295
  })
1091
1296
  };
1092
- const bounded = boundExecutionPresentation(detail);
1297
+ const bounded = boundExecutionPresentation({ toolName, ...detail });
1093
1298
  return {
1094
1299
  itemId: command.itemId,
1095
1300
  kind: 'command_execution',
@@ -1099,7 +1304,13 @@ function commandItem(command, status, title, execution) {
1099
1304
  command: command.command,
1100
1305
  cwd: command.cwd,
1101
1306
  outputPreview: command.outputPreview,
1102
- outputRef: null,
1307
+ output: {
1308
+ text: publicOutputSnapshot(bounded.presentation.execution.output
1309
+ ?.map((block) => block.text)
1310
+ .filter(Boolean)
1311
+ .join('\n') ?? command.outputPreview).text,
1312
+ truncated: command.truncated || bounded.truncated
1313
+ },
1103
1314
  exitCode: command.exitCode,
1104
1315
  durationMs: null,
1105
1316
  truncated: command.truncated ||
@@ -1130,7 +1341,6 @@ function patchChanges(value, extensions) {
1130
1341
  additions: entry.additions,
1131
1342
  deletions: entry.deletions,
1132
1343
  changeId: entry.changeId,
1133
- diffAvailable: entry.diff.status === 'available',
1134
1344
  diff: entry.diff.status === 'available'
1135
1345
  ? { ...entry.diff, source: 'TOOL_CHANGE' }
1136
1346
  : {
@@ -1164,7 +1374,7 @@ function patchChanges(value, extensions) {
1164
1374
  });
1165
1375
  }
1166
1376
  function change(path, operation, additions, deletions) {
1167
- return { path, change: operation, additions, deletions, diffAvailable: false };
1377
+ return { path, change: operation, additions, deletions };
1168
1378
  }
1169
1379
  function execResult(value, summary, eventTruncated = false) {
1170
1380
  const data = record(value);
@@ -1248,9 +1458,15 @@ function marAgentSubagentProjection(toolName, input, output, knownTitle, knownDe
1248
1458
  ...(title.length === 0 ? {} : { title }),
1249
1459
  ...subagentPayload(details),
1250
1460
  ...(typeof details.latestMessage?.text === 'string'
1251
- ? { outputSummary: boundedToolSummary(details.latestMessage.text) }
1461
+ ? {
1462
+ outputSummary: boundedToolSummary(details.latestMessage.text),
1463
+ output: publicOutputSnapshot(details.latestMessage.text, details.latestMessage.truncated === true)
1464
+ }
1252
1465
  : typeof outputData?.errorMessage === 'string'
1253
- ? { outputSummary: compactRunnerText(outputData.errorMessage, 10_000) }
1466
+ ? {
1467
+ outputSummary: compactRunnerText(outputData.errorMessage, 10_000),
1468
+ output: publicOutputSnapshot(outputData.errorMessage)
1469
+ }
1254
1470
  : {}),
1255
1471
  ...(typeof outputData?.errorCode === 'string'
1256
1472
  ? { errorCode: compactRunnerText(outputData.errorCode, 80) }
@@ -737,7 +737,8 @@ export class MarAgentManagedRunController {
737
737
  status: item.status,
738
738
  payload: attachments.length === 0 ? item.payload : { ...item.payload, attachments },
739
739
  ...(item.permissionStop === undefined ? {} : { permissionStop: item.permissionStop }),
740
- ...(item.merge === undefined ? {} : { merge: item.merge })
740
+ ...(item.merge === undefined ? {} : { merge: item.merge }),
741
+ ...(item.append === undefined ? {} : { append: item.append })
741
742
  });
742
743
  }
743
744
  }
@@ -87,10 +87,8 @@ export class MarAgentRuntimeModelFactory {
87
87
  : { responsesConfigurationUpdates: configuration.responsesConfigurationUpdates }),
88
88
  enabled: configuration.enabled
89
89
  };
90
- const { apiKey, credential } = configuration;
91
- if (apiKey !== undefined)
92
- return { ...model, apiKey: new SecretValue(apiKey) };
93
- if (credential?.type === 'INLINE_API_KEY')
90
+ const { credential } = configuration;
91
+ if (credential.type === 'INLINE_API_KEY')
94
92
  return {
95
93
  ...model,
96
94
  apiKey: new SecretValue(credential.value)
@@ -1,4 +1,4 @@
1
- import { marAgentNodeModelSummarySchema, parseComposerTags, resolveMarAgentDefaultReasoningEffort, serializeComposerTag } from '@myagentroam/protocol';
1
+ import { marAgentNodeModelSummarySchema, marAgentRuntimeModelConfigurationSchema, parseComposerTags, resolveMarAgentDefaultReasoningEffort, serializeComposerTag } from '@myagentroam/protocol';
2
2
  import { nodeLog } from '../operational.js';
3
3
  import { runnerErrorProjection } from './runner-error.js';
4
4
  import { AgentError, openAgentSessionCatalog } from '@myagentroam/agent';
@@ -7,7 +7,7 @@ import { resolve } from 'node:path';
7
7
  import { AbstractRunner } from './abstract-runner.js';
8
8
  import { marAgentGeneratedImageSources, marAgentGeneratedImagesDirectory } from './mar-agent/generated-images.js';
9
9
  import { projectMarAgentHistory } from './mar-agent/history-projection.js';
10
- import { captureMarAgentExecutionInput } from './mar-agent/execution-input.js';
10
+ import { captureMarAgentExecutionInput, marAgentEnvelopeInput } from './mar-agent/execution-input.js';
11
11
  export class MarAgentRunner extends AbstractRunner {
12
12
  homeDirectory;
13
13
  name = 'mar-agent';
@@ -31,6 +31,7 @@ export class MarAgentRunner extends AbstractRunner {
31
31
  return { runner: this.name, runnerInput: captureMarAgentExecutionInput(input) };
32
32
  }
33
33
  discoveryScope = 'ALL_WORKSPACES';
34
+ // Display and usage hints only; request admission/defaults use captured resources.
34
35
  #models = [];
35
36
  #releaseSessionResources;
36
37
  #generatedImageRegistrar;
@@ -45,6 +46,7 @@ export class MarAgentRunner extends AbstractRunner {
45
46
  return parsed.success ? [parsed.data] : [];
46
47
  })
47
48
  : [];
49
+ return this.profileForValidation();
48
50
  }
49
51
  setSessionResourceReleaser(release) {
50
52
  this.#releaseSessionResources = release;
@@ -114,8 +116,9 @@ export class MarAgentRunner extends AbstractRunner {
114
116
  available(capabilities) {
115
117
  return capabilities.marAgent?.available === true;
116
118
  }
117
- profileForValidation() {
118
- const models = this.#models.filter((model) => model.enabled && model.conversationModel !== false);
119
+ profileForValidation(catalog = this
120
+ .#models) {
121
+ const models = catalog.filter((model) => model.enabled && model.conversationModel !== false);
119
122
  return {
120
123
  runner: 'mar-agent',
121
124
  models: models.map((model, index) => ({
@@ -202,10 +205,29 @@ export class MarAgentRunner extends AbstractRunner {
202
205
  .map((model) => model.id));
203
206
  return this.profileForValidation().models.filter((model) => eligible.has(model.id));
204
207
  }
205
- assertChildModel(modelId) {
206
- if (!this.childModels().some((model) => model.id === modelId))
208
+ assertChildModel(modelId, context) {
209
+ if (!this.executionModels(context).some((model) => model.id === modelId && model.enabled && model.conversationModel && model.subagentModel))
207
210
  throw new Error('MAR_AGENT_SUBAGENT_MODEL_UNAVAILABLE');
208
211
  }
212
+ executionModels(context, purpose = 'admission') {
213
+ if (context.runner !== this.name)
214
+ return [];
215
+ const input = marAgentEnvelopeInput({ runnerInput: context.runnerInput });
216
+ const candidates = purpose === 'defaults' && Array.isArray(input?.models) && input.models.length > 0
217
+ ? input.models
218
+ : input?.model !== undefined
219
+ ? [input.model]
220
+ : Array.isArray(input?.models)
221
+ ? input.models
222
+ : [];
223
+ return candidates.flatMap((value) => {
224
+ const runtime = marAgentRuntimeModelConfigurationSchema.safeParse(value);
225
+ if (runtime.success)
226
+ return [runtime.data];
227
+ const summary = marAgentNodeModelSummarySchema.safeParse(value);
228
+ return summary.success ? [summary.data] : [];
229
+ });
230
+ }
209
231
  supportsConfiguration(configuration) {
210
232
  if (configuration.runner !== this.name)
211
233
  return false;
@@ -217,8 +239,10 @@ export class MarAgentRunner extends AbstractRunner {
217
239
  : model.supportedEfforts.includes(configuration.effort)) &&
218
240
  profile.accessOptions.some((candidate) => candidate.id === configuration.access));
219
241
  }
220
- defaultConfiguration() {
221
- const profile = this.profileForValidation();
242
+ defaultConfiguration(_workspace, _environment, executionContext) {
243
+ const profile = this.profileForValidation(executionContext === undefined
244
+ ? this.#models
245
+ : this.executionModels(executionContext, 'defaults'));
222
246
  const model = profile.models[0];
223
247
  return {
224
248
  runner: 'mar-agent',
@@ -1,6 +1,14 @@
1
1
  export function compactRunnerText(value, limit) {
2
2
  return value.length <= limit ? value : `${value.slice(0, Math.max(0, limit - 1))}…`;
3
3
  }
4
+ export function publicOutputSnapshot(value, truncated = false) {
5
+ const text = typeof value === 'string' ? value : compactRunnerValue(value, Number.MAX_SAFE_INTEGER).text;
6
+ let bounded = text.slice(-10_000);
7
+ const first = bounded.charCodeAt(0);
8
+ if (text.length > 10_000 && first >= 0xdc00 && first <= 0xdfff)
9
+ bounded = bounded.slice(1);
10
+ return { text: bounded, truncated: truncated || text.length > 10_000 };
11
+ }
4
12
  export function compactRunnerValue(value, limit) {
5
13
  if (value === undefined || value === null)
6
14
  return { text: '', truncated: false };
@@ -1,5 +1,5 @@
1
- import { compactRunnerText, compactRunnerValue } from '../normalization.js';
2
- import { publicOutputSnapshot, shellExecution } from '../execution-presentation.js';
1
+ import { compactRunnerText, compactRunnerValue, publicOutputSnapshot } from '../normalization.js';
2
+ import { shellExecution } from '../execution-presentation.js';
3
3
  export function normalizeOpenCodeTool(toolName, state) {
4
4
  const status = state?.status === 'completed'
5
5
  ? 'COMPLETED'
@@ -14,6 +14,7 @@ export function normalizeOpenCodeTool(toolName, state) {
14
14
  return {
15
15
  kind: 'command_execution',
16
16
  payload: {
17
+ toolName: publicToolName,
17
18
  command: compactRunnerText(typeof input?.command === 'string'
18
19
  ? input.command
19
20
  : typeof input?.cmd === 'string'
@@ -21,7 +22,6 @@ export function normalizeOpenCodeTool(toolName, state) {
21
22
  : publicToolName, 10_000),
22
23
  cwd: typeof input?.cwd === 'string' ? compactRunnerText(input.cwd, 4_096) : null,
23
24
  outputPreview: typeof state?.output === 'string' ? compactRunnerText(state.output, 10_000) : '',
24
- outputRef: null,
25
25
  exitCode: typeof state?.exitCode === 'number' ? state.exitCode : null,
26
26
  durationMs: null,
27
27
  truncated: typeof state?.output === 'string' && state.output.length > 10_000,
@@ -53,6 +53,7 @@ export function normalizeOpenCodeTool(toolName, state) {
53
53
  }
54
54
  }
55
55
  : {}),
56
+ ...(typeof state?.output === 'string' ? { output: publicOutputSnapshot(state.output) } : {}),
56
57
  ...subagent
57
58
  }
58
59
  };
@@ -75,7 +76,10 @@ function openCodeSubagentProjection(toolName, input, output) {
75
76
  ...(title.length === 0 ? {} : { title }),
76
77
  ...(nativeResult === undefined
77
78
  ? {}
78
- : { outputSummary: compactRunnerText(nativeResult.result, 10_000) || null })
79
+ : {
80
+ outputSummary: compactRunnerText(nativeResult.result, 10_000) || null,
81
+ output: publicOutputSnapshot(nativeResult.result)
82
+ })
79
83
  };
80
84
  }
81
85
  function openCodeTaskResult(value) {
@@ -1046,7 +1046,6 @@ export class NodeRuntimeState {
1046
1046
  command: input.command,
1047
1047
  cwd: input.cwd,
1048
1048
  outputPreview: input.outputPreview.slice(0, 10_000),
1049
- outputRef: null,
1050
1049
  exitCode: input.failed ? 1 : 0,
1051
1050
  durationMs: 0,
1052
1051
  truncated: false