@myagentroam/node 0.11.39 → 0.11.40

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 (51) hide show
  1. package/dist/connector.js +176 -21
  2. package/dist/database.js +35 -11
  3. package/dist/main.js +12 -1
  4. package/dist/marctl-guidance.js +18 -27
  5. package/dist/migrations/v022.js +58 -0
  6. package/dist/runner/abstract-runner.js +14 -0
  7. package/dist/runner/claude/tool-normalizer.js +6 -1
  8. package/dist/runner/codex/conversation-parser.js +23 -4
  9. package/dist/runner/codex/managed-run-controller.js +4 -2
  10. package/dist/runner/codex/tool-normalizer.js +9 -0
  11. package/dist/runner/execution-presentation.js +56 -0
  12. package/dist/runner/mar-agent/agent-network-extension.js +168 -0
  13. package/dist/runner/mar-agent/agent-network-wait-output.js +72 -0
  14. package/dist/runner/mar-agent/conversation-normalizer.js +233 -119
  15. package/dist/runner/mar-agent/managed-run-controller.js +5 -0
  16. package/dist/runner/mar-agent/marctl-skill.js +3 -3
  17. package/dist/runner/mar-agent/runtime-model-factory.js +1 -0
  18. package/dist/runner/mar-agent/session-runtime.js +34 -1
  19. package/dist/runner/mar-agent-runner.js +36 -0
  20. package/dist/runner/opencode/tool-normalizer.js +6 -1
  21. package/dist/runtime-state.js +57 -0
  22. package/dist/service/agent-network-bindings.js +483 -0
  23. package/dist/service/agent-network-goal.js +19 -0
  24. package/dist/service/agent-network-invoke.js +179 -0
  25. package/dist/service/agent-network-observations.js +377 -0
  26. package/dist/service/agent-network-service.js +315 -0
  27. package/dist/service/agent-network-wait.js +130 -0
  28. package/dist/service/composer-message-input.js +9 -2
  29. package/dist/service/continuous-run-coordinator.js +24 -1
  30. package/dist/service/goal-continuation-coordinator.js +12 -2
  31. package/dist/service/marctl-caller.js +80 -0
  32. package/dist/service/marctl-execution-credential-control.js +65 -0
  33. package/dist/service/model-usage-service.js +9 -0
  34. package/dist/service/node-control-message-service.js +10 -0
  35. package/dist/service/node-request-service.js +52 -7
  36. package/dist/service/node-upgrade-coordinator.js +79 -8
  37. package/dist/service/node-upgrade-credential-control.js +57 -0
  38. package/dist/service/node-upgrade-reporter.js +108 -0
  39. package/dist/service/run-event-service.js +8 -0
  40. package/dist/service/run-workbench-service.js +11 -0
  41. package/dist/service/supervised-node-child.js +153 -0
  42. package/dist/service/supervisor-upgrade-handoff.js +104 -0
  43. package/dist/session/native-session-projection-service.js +69 -6
  44. package/dist/session/operation-admission.js +7 -0
  45. package/dist/session/runtime/conversation-projection.js +25 -3
  46. package/dist/session/session-catalog-service.js +156 -32
  47. package/dist/session/session-lifecycle-service.js +68 -42
  48. package/dist/session/session-message-service.js +16 -6
  49. package/dist/supervisor.js +209 -20
  50. package/dist/terminal.js +41 -21
  51. package/package.json +4 -4
@@ -7,6 +7,7 @@ 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
11
  export class MarAgentConversationNormalizer {
11
12
  input(entry) {
12
13
  return {
@@ -25,12 +26,10 @@ export class MarAgentConversationNormalizer {
25
26
  }
26
27
  #tools = new Map();
27
28
  #commands = new Map();
28
- #processOrigins = new Map();
29
29
  #subagentTitles = new Map();
30
30
  #subagentDetails = new Map();
31
31
  #codeModeObservationIds = new Set();
32
32
  #codeModeSourceCallIds = new Set();
33
- #toolOutputSnapshots = new Map();
34
33
  #codeModeOutputCalls = new Map();
35
34
  #codeModeOutputSnapshots = new Map();
36
35
  #runningCodeModeCells = new Map();
@@ -69,7 +68,8 @@ export class MarAgentConversationNormalizer {
69
68
  const payload = {
70
69
  text: event.type === 'message.completed' ? event.text : '',
71
70
  format: 'markdown',
72
- model: typeof modelName === 'string' ? modelName : null
71
+ model: typeof modelName === 'string' ? modelName : null,
72
+ phase: event.phase
73
73
  };
74
74
  return [
75
75
  {
@@ -168,7 +168,6 @@ export class MarAgentConversationNormalizer {
168
168
  #toolStarted(event) {
169
169
  const ownItemId = itemId('tool', event.callId);
170
170
  this.#clearRetainedCodeModeCellsForOtherExecutions(event.executionId);
171
- this.#toolOutputSnapshots.delete(event.callId);
172
171
  this.#codeModeOutputCalls.delete(event.callId);
173
172
  this.#clearCodeModeOutputSnapshots(event.callId);
174
173
  if (event.toolName === 'exec' || event.toolName === 'exec_wait') {
@@ -182,13 +181,17 @@ export class MarAgentConversationNormalizer {
182
181
  if (action === 'start' && typeof input.command === 'string') {
183
182
  const command = {
184
183
  itemId: ownItemId,
185
- turnKey: event.turnId ?? event.executionId,
186
184
  command: compactRunnerText(input.command, 10_000),
187
185
  cwd: typeof input.cwd === 'string' ? compactRunnerText(input.cwd, 4_096) : null,
188
186
  status: 'IN_PROGRESS',
189
187
  outputPreview: '',
190
188
  exitCode: null,
191
- truncated: false
189
+ truncated: event.truncated === true ||
190
+ input.command.length > 10_000 ||
191
+ (typeof input.cwd === 'string' && input.cwd.length > 4096),
192
+ inputTruncated: event.truncated === true ||
193
+ input.command.length > 10_000 ||
194
+ (typeof input.cwd === 'string' && input.cwd.length > 4096)
192
195
  };
193
196
  this.#commands.set(event.callId, command);
194
197
  this.#tools.set(event.callId, {
@@ -203,49 +206,6 @@ export class MarAgentConversationNormalizer {
203
206
  });
204
207
  return [commandItem(command, 'IN_PROGRESS')];
205
208
  }
206
- if ((action === 'poll' || action === 'write' || action === 'cancel') &&
207
- typeof input.processId === 'string') {
208
- const origin = this.#processOrigins.get(input.processId);
209
- if (origin !== undefined) {
210
- const previous = this.#commands.get(origin);
211
- const sameTurn = previous?.turnKey === (event.turnId ?? event.executionId);
212
- if (previous !== undefined && !sameTurn) {
213
- const command = {
214
- itemId: ownItemId,
215
- turnKey: event.turnId ?? event.executionId,
216
- command: previous.command,
217
- cwd: previous.cwd,
218
- status: 'IN_PROGRESS',
219
- outputPreview: '',
220
- exitCode: null,
221
- truncated: false
222
- };
223
- this.#commands.set(event.callId, command);
224
- this.#tools.set(event.callId, {
225
- toolName: event.toolName,
226
- input: event.input,
227
- itemId: ownItemId,
228
- structured: true,
229
- executionId: event.executionId,
230
- ...(event.turnId === undefined ? {} : { turnId: event.turnId }),
231
- active: true,
232
- commandOrigin: event.callId
233
- });
234
- return [];
235
- }
236
- this.#tools.set(event.callId, {
237
- toolName: event.toolName,
238
- input: event.input,
239
- itemId: ownItemId,
240
- structured: true,
241
- executionId: event.executionId,
242
- ...(event.turnId === undefined ? {} : { turnId: event.turnId }),
243
- active: true,
244
- commandOrigin: origin
245
- });
246
- return [];
247
- }
248
- }
249
209
  }
250
210
  const agentId = isMarAgentControlTool(event.toolName)
251
211
  ? marAgentAgentId(event.input)
@@ -275,9 +235,20 @@ export class MarAgentConversationNormalizer {
275
235
  #toolOutput(event) {
276
236
  const { callId, toolName, executionId, turnId, parentCallId, output } = event;
277
237
  const inputOutput = [output.stdout, output.stderr].filter(Boolean).join('\n');
238
+ const displayOutput = execDisplayOutput(output.displayOutput) ?? {
239
+ stdout: output.stdout,
240
+ stderr: output.stderr,
241
+ stdoutDroppedBytes: 0,
242
+ stderrDroppedBytes: 0
243
+ };
244
+ const displayed = [displayOutput.stdout, displayOutput.stderr].filter(Boolean).join('\n');
245
+ const bounded = publicOutputSnapshot(displayed, output.truncated ||
246
+ displayOutput.stdoutDroppedBytes > 0 ||
247
+ displayOutput.stderrDroppedBytes > 0);
278
248
  const snapshot = {
279
- outputPreview: compactRunnerText(inputOutput, 10_000),
280
- truncated: output.truncated || inputOutput.length > 10_000
249
+ outputPreview: bounded.text,
250
+ truncated: bounded.truncated || inputOutput.length > 10_000,
251
+ displayOutput
281
252
  };
282
253
  if (parentCallId !== undefined)
283
254
  return this.#codeModeToolOutput(callId, parentCallId, toolName, executionId, turnId, output.processId, snapshot);
@@ -295,19 +266,18 @@ export class MarAgentConversationNormalizer {
295
266
  if (toolName !== 'exec_wait')
296
267
  return [];
297
268
  return [
298
- genericToolItem(state.itemId, toolName, 'IN_PROGRESS', state.input, snapshot.outputPreview, null, snapshot.truncated)
269
+ genericToolItem(state.itemId, toolName, 'IN_PROGRESS', state.input, snapshot.outputPreview, null, snapshot.truncated, { processId: output.processId, displayOutput: snapshot.displayOutput })
299
270
  ];
300
271
  }
301
272
  const command = this.#commands.get(state.commandOrigin);
302
273
  if (command === undefined || command.status !== 'IN_PROGRESS')
303
274
  return [];
304
- this.#toolOutputSnapshots.set(callId, {
305
- commandOrigin: state.commandOrigin,
306
- ...snapshot
307
- });
308
- return [
309
- commandItem(commandWithOutputSnapshots(command, [...this.#toolOutputSnapshots.values()].filter((entry) => entry.commandOrigin === state.commandOrigin)), 'IN_PROGRESS')
310
- ];
275
+ const display = snapshot.displayOutput;
276
+ if (display !== undefined)
277
+ command.displayOutput = display;
278
+ command.outputPreview = snapshot.outputPreview;
279
+ command.truncated = snapshot.truncated;
280
+ return [commandItem(command, 'IN_PROGRESS')];
311
281
  }
312
282
  #codeModeToolOutput(callId, parentCallId, toolName, executionId, turnId, processId, snapshot) {
313
283
  const parent = this.#tools.get(parentCallId);
@@ -339,9 +309,9 @@ export class MarAgentConversationNormalizer {
339
309
  .map((entry) => entry.outputPreview)
340
310
  .filter(Boolean)
341
311
  .join('\n');
342
- const truncated = [...snapshots.values()].some((entry) => entry.truncated) || combined.length > 10_000;
312
+ const bounded = publicOutputSnapshot(combined, [...snapshots.values()].some((entry) => entry.truncated));
343
313
  return [
344
- genericToolItem(target.itemId, target.toolName, 'IN_PROGRESS', target.input, compactRunnerText(combined, 10_000), null, truncated)
314
+ genericToolItem(target.itemId, target.toolName, 'IN_PROGRESS', target.input, bounded.text, null, bounded.truncated)
345
315
  ];
346
316
  }
347
317
  #trackCodeModeOutputCall(event) {
@@ -409,7 +379,6 @@ export class MarAgentConversationNormalizer {
409
379
  const state = this.#tools.get(event.callId);
410
380
  if (state !== undefined)
411
381
  state.active = false;
412
- this.#toolOutputSnapshots.delete(event.callId);
413
382
  const data = record(event.data);
414
383
  const cellId = typeof data?.cellId === 'string' ? data.cellId : undefined;
415
384
  const status = data?.status;
@@ -468,7 +437,6 @@ export class MarAgentConversationNormalizer {
468
437
  for (const [callId, state] of this.#tools)
469
438
  if (state.executionId !== executionId) {
470
439
  state.active = false;
471
- this.#toolOutputSnapshots.delete(callId);
472
440
  }
473
441
  for (const [callId, state] of this.#codeModeOutputCalls)
474
442
  if (state.executionId !== executionId)
@@ -499,21 +467,19 @@ export class MarAgentConversationNormalizer {
499
467
  }
500
468
  #toolCompleted(event) {
501
469
  const state = this.#tools.get(event.callId);
502
- if ((event.toolName === 'exec' || event.toolName === 'exec_wait') &&
503
- state?.commandOrigin !== undefined) {
470
+ if (event.toolName === 'exec' && state?.commandOrigin !== undefined) {
504
471
  const command = this.#commands.get(state.commandOrigin);
505
472
  const result = execResult(event.data, event.summary, event.truncated === true);
506
473
  if (command !== undefined && result !== undefined) {
507
- if (result.processId !== undefined)
508
- this.#processOrigins.set(result.processId, state.commandOrigin);
509
474
  applyCommandResult(command, result);
510
- const status = result.status === 'running'
511
- ? 'IN_PROGRESS'
512
- : result.status === 'completed' && result.exitCode === 0
513
- ? 'COMPLETED'
514
- : 'FAILED';
515
- command.status = status;
516
- return [commandItem(command, status)];
475
+ const data = record(event.data);
476
+ const display = execDisplayOutput(data?.displayOutput);
477
+ if (display !== undefined)
478
+ command.displayOutput = display;
479
+ else
480
+ delete command.displayOutput;
481
+ command.status = 'COMPLETED';
482
+ return [commandItem(command, 'COMPLETED')];
517
483
  }
518
484
  }
519
485
  if (event.toolName === 'apply_patch' &&
@@ -559,16 +525,17 @@ export class MarAgentConversationNormalizer {
559
525
  }
560
526
  #toolFailed(event) {
561
527
  const state = this.#tools.get(event.callId);
562
- if ((event.toolName === 'exec' || event.toolName === 'exec_wait') &&
563
- state?.commandOrigin !== undefined) {
528
+ if (event.toolName === 'exec' && state?.commandOrigin !== undefined) {
564
529
  const command = this.#commands.get(state.commandOrigin);
565
- const input = record(state.input);
566
- if (command !== undefined &&
567
- command.status === 'IN_PROGRESS' &&
568
- (state.itemId !== command.itemId ||
569
- input?.action === 'start' ||
570
- (input?.action == null && typeof input?.command === 'string'))) {
530
+ if (command !== undefined) {
531
+ const error = {
532
+ code: compactRunnerText(event.code, 80) || 'UNKNOWN',
533
+ message: compactRunnerText(event.message ?? event.code, 1000)
534
+ };
571
535
  command.status = 'FAILED';
536
+ command.error = error;
537
+ delete command.displayOutput;
538
+ command.outputPreview = '';
572
539
  return [commandItem(command, 'FAILED')];
573
540
  }
574
541
  }
@@ -854,24 +821,89 @@ function normalizeContextCompaction(event, anchorItemId) {
854
821
  function validCompactAnchor(value) {
855
822
  return value !== undefined && value.length > 0 && value.length <= 128 ? value : undefined;
856
823
  }
824
+ function codeObservationOutput(data, summary) {
825
+ if (Array.isArray(data?.observations)) {
826
+ return data.observations
827
+ .flatMap((entry) => {
828
+ const observation = record(entry);
829
+ if (observation?.type === 'value' && typeof observation.value === 'string')
830
+ return [observation.value];
831
+ const result = record(observation?.result);
832
+ 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');
845
+ }
846
+ return summary ?? undefined;
847
+ }
848
+ function codeExecutionError(data) {
849
+ const error = record(data?.error);
850
+ if (typeof error?.code !== 'string' || !error.code || typeof error.message !== 'string')
851
+ return undefined;
852
+ return {
853
+ code: compactRunnerText(error.code, 80),
854
+ message: compactRunnerText(error.message, 1000)
855
+ };
856
+ }
857
857
  function genericToolItem(id, toolName, status, input, outputSummary = null, errorCode = null, truncated = false, output, subagentTitle, knownSubagentDetails, sleepStartedAt) {
858
858
  if (toolName === 'code_exec') {
859
859
  const code = typeof input === 'string' ? input : record(input)?.code;
860
860
  const source = typeof code === 'string' ? code : compactRunnerValue(input, 10_000).text || toolName;
861
- const output = errorCode === null
861
+ const resultData = record(output);
862
+ const codeOutput = codeObservationOutput(resultData, outputSummary);
863
+ const resultError = codeExecutionError(resultData);
864
+ const preview = errorCode === null
862
865
  ? (outputSummary ?? '')
863
866
  : [errorCode, outputSummary].filter(Boolean).join(': ');
864
867
  return commandItem({
865
868
  itemId: id,
866
869
  command: compactRunnerText(source, 10_000),
867
870
  cwd: null,
868
- outputPreview: compactRunnerText(output, 10_000),
871
+ outputPreview: compactRunnerText(preview, 10_000),
869
872
  exitCode: null,
870
- truncated: truncated || source.length > 10_000 || output.length > 10_000
871
- }, status, 'Code execution');
873
+ truncated: truncated ||
874
+ resultData?.truncated === true ||
875
+ source.length > 10_000 ||
876
+ preview.length > 10_000
877
+ }, status, 'Code execution', {
878
+ kind: 'CODE',
879
+ ...(source.length > 10_000 ? { truncation: { input: true } } : {}),
880
+ ...(errorCode === null
881
+ ? resultError === undefined
882
+ ? {}
883
+ : { error: resultError }
884
+ : {
885
+ error: {
886
+ code: compactRunnerText(errorCode, 80),
887
+ message: compactRunnerText(outputSummary ?? '', 1000)
888
+ }
889
+ }),
890
+ ...(codeOutput === undefined ? {} : { output: [{ channel: 'TEXT', text: codeOutput }] })
891
+ });
872
892
  }
873
893
  if (toolName === 'code_exec_wait' || toolName === 'exec_wait') {
874
894
  const inputRecord = record(input);
895
+ const resultData = record(output);
896
+ const display = execDisplayOutput(resultData?.displayOutput);
897
+ const legacyBlocks = [
898
+ ...(typeof resultData?.stdout === 'string'
899
+ ? [{ channel: 'STDOUT', text: resultData.stdout }]
900
+ : []),
901
+ ...(typeof resultData?.stderr === 'string'
902
+ ? [{ channel: 'STDERR', text: resultData.stderr }]
903
+ : [])
904
+ ];
905
+ const codeOutput = toolName === 'code_exec_wait' ? codeObservationOutput(resultData, outputSummary) : undefined;
906
+ const resultError = toolName === 'code_exec_wait' ? codeExecutionError(resultData) : undefined;
875
907
  const action = typeof inputRecord?.action === 'string' ? inputRecord.action : 'poll';
876
908
  const inputSummary = compactRunnerValue(input, 10_000);
877
909
  const result = output === undefined ? null : compactRunnerValue(output, 10_000);
@@ -888,15 +920,64 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
888
920
  .join('\n');
889
921
  return commandItem({
890
922
  itemId: id,
891
- command: toolName,
923
+ command: `${toolName} (${compactRunnerText(action, 128)})`,
892
924
  cwd: null,
893
- outputPreview: details,
894
- exitCode: null,
925
+ outputPreview: status === 'IN_PROGRESS' && outputSummary !== null
926
+ ? outputSummary
927
+ : legacyBlocks.length > 0
928
+ ? legacyBlocks
929
+ .map((block) => block.text)
930
+ .filter(Boolean)
931
+ .join('\n')
932
+ : details,
933
+ exitCode: typeof resultData?.exitCode === 'number' ? resultData.exitCode : null,
895
934
  truncated: truncated ||
935
+ resultData?.truncated === true ||
896
936
  inputSummary.truncated ||
897
937
  result?.truncated === true ||
898
938
  resultSummary.length > 10_000
899
- }, status, toolName === 'code_exec_wait' ? 'Code execution wait' : 'Command wait');
939
+ }, status, toolName === 'code_exec_wait' ? 'Code execution wait' : 'Command wait', {
940
+ kind: 'CONTROL',
941
+ ...(display === undefined
942
+ ? {}
943
+ : {
944
+ output: [
945
+ { channel: 'STDOUT', text: display.stdout },
946
+ { channel: 'STDERR', text: display.stderr }
947
+ ]
948
+ }),
949
+ ...(display === undefined && legacyBlocks.length > 0 ? { output: legacyBlocks } : {}),
950
+ ...(codeOutput === undefined
951
+ ? {}
952
+ : { output: [{ channel: 'TEXT', text: codeOutput }] }),
953
+ ...((typeof inputRecord?.data === 'string' && inputRecord.data.length > 10_000) ||
954
+ (display !== undefined && (display.stdoutDroppedBytes || display.stderrDroppedBytes)) ||
955
+ (display === undefined && legacyBlocks.length > 0 && truncated)
956
+ ? {
957
+ truncation: {
958
+ ...(typeof inputRecord?.data === 'string' && inputRecord.data.length > 10_000
959
+ ? { input: true }
960
+ : {}),
961
+ ...(display !== undefined &&
962
+ (display.stdoutDroppedBytes || display.stderrDroppedBytes)
963
+ ? { output: 'HEAD' }
964
+ : display === undefined && legacyBlocks.length > 0 && truncated
965
+ ? { output: 'SOURCE' }
966
+ : {})
967
+ }
968
+ }
969
+ : {}),
970
+ ...(errorCode === null
971
+ ? resultError === undefined
972
+ ? {}
973
+ : { error: resultError }
974
+ : {
975
+ error: {
976
+ code: compactRunnerText(errorCode, 80),
977
+ message: compactRunnerText(outputSummary ?? '', 1000)
978
+ }
979
+ })
980
+ });
900
981
  }
901
982
  const subagent = marAgentSubagentProjection(toolName, input, output, subagentTitle, knownSubagentDetails);
902
983
  const toolCategory = isImageGenerationTool(toolName) ? 'IMAGE_GENERATION' : undefined;
@@ -965,7 +1046,50 @@ function projectToolPresentation(toolName, input, output, startedAt) {
965
1046
  const parsed = toolPresentationSchema.safeParse(candidate);
966
1047
  return parsed.success ? parsed.data : { kind };
967
1048
  }
968
- function commandItem(command, status, title) {
1049
+ function execDisplayOutput(value) {
1050
+ const data = record(value);
1051
+ if (typeof data?.stdout !== 'string' ||
1052
+ typeof data.stderr !== 'string' ||
1053
+ typeof data.stdoutDroppedBytes !== 'number' ||
1054
+ !Number.isSafeInteger(data.stdoutDroppedBytes) ||
1055
+ data.stdoutDroppedBytes < 0 ||
1056
+ typeof data.stderrDroppedBytes !== 'number' ||
1057
+ !Number.isSafeInteger(data.stderrDroppedBytes) ||
1058
+ data.stderrDroppedBytes < 0)
1059
+ return undefined;
1060
+ return {
1061
+ stdout: data.stdout,
1062
+ stderr: data.stderr,
1063
+ stdoutDroppedBytes: data.stdoutDroppedBytes,
1064
+ stderrDroppedBytes: data.stderrDroppedBytes
1065
+ };
1066
+ }
1067
+ function commandItem(command, status, title, execution) {
1068
+ const display = command.displayOutput;
1069
+ const detail = execution ?? {
1070
+ kind: 'SHELL',
1071
+ ...(command.error === undefined ? {} : { error: command.error }),
1072
+ ...(!command.inputTruncated &&
1073
+ !(display && (display.stdoutDroppedBytes > 0 || display.stderrDroppedBytes > 0))
1074
+ ? {}
1075
+ : {
1076
+ truncation: {
1077
+ ...(command.inputTruncated ? { input: true } : {}),
1078
+ ...(display && (display.stdoutDroppedBytes > 0 || display.stderrDroppedBytes > 0)
1079
+ ? { output: 'HEAD' }
1080
+ : {})
1081
+ }
1082
+ }),
1083
+ ...(display === undefined
1084
+ ? {}
1085
+ : {
1086
+ output: [
1087
+ { channel: 'STDOUT', text: display.stdout },
1088
+ { channel: 'STDERR', text: display.stderr }
1089
+ ]
1090
+ })
1091
+ };
1092
+ const bounded = boundExecutionPresentation(detail);
969
1093
  return {
970
1094
  itemId: command.itemId,
971
1095
  kind: 'command_execution',
@@ -978,37 +1102,21 @@ function commandItem(command, status, title) {
978
1102
  outputRef: null,
979
1103
  exitCode: command.exitCode,
980
1104
  durationMs: null,
981
- truncated: command.truncated
1105
+ truncated: command.truncated ||
1106
+ command.inputTruncated === true ||
1107
+ bounded.truncated ||
1108
+ (display !== undefined &&
1109
+ (display.stdoutDroppedBytes > 0 || display.stderrDroppedBytes > 0)),
1110
+ toolPresentation: bounded.presentation
982
1111
  },
983
1112
  merge: true
984
1113
  };
985
1114
  }
986
- function commandWithOutputSnapshots(command, snapshots) {
987
- const outputPreview = [
988
- command.outputPreview,
989
- ...snapshots.map((snapshot) => snapshot.outputPreview)
990
- ]
991
- .filter(Boolean)
992
- .join('\n');
993
- return {
994
- ...command,
995
- outputPreview: compactRunnerText(outputPreview, 10_000),
996
- truncated: command.truncated ||
997
- snapshots.some((snapshot) => snapshot.truncated) ||
998
- outputPreview.length > 10_000
999
- };
1000
- }
1001
1115
  function applyCommandResult(command, result) {
1002
1116
  const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
1003
- if (output.length > 0) {
1004
- const combined = result.cached
1005
- ? output
1006
- : [command.outputPreview, output].filter(Boolean).join('\n');
1007
- command.truncated ||= combined.length > 10_000;
1008
- command.outputPreview = compactRunnerText(combined, 10_000);
1009
- }
1010
- command.exitCode = result.exitCode ?? command.exitCode;
1011
- command.truncated ||= result.truncated;
1117
+ command.outputPreview = compactRunnerText(output, 10_000);
1118
+ command.truncated = result.truncated || output.length > 10_000;
1119
+ command.exitCode = result.exitCode ?? null;
1012
1120
  }
1013
1121
  function fileChangeItem(id, status, changes) {
1014
1122
  return { itemId: id, kind: 'file_change', status, payload: { changes }, merge: true };
@@ -1126,11 +1234,17 @@ function marAgentSubagentProjection(toolName, input, output, knownTitle, knownDe
1126
1234
  ? outputData.description
1127
1235
  : operation === 'START' && typeof inputData?.description === 'string'
1128
1236
  ? inputData.description
1129
- : ''), 500);
1237
+ : operation === 'START' && typeof inputData?.title === 'string'
1238
+ ? inputData.title
1239
+ : ''), 500);
1130
1240
  return {
1131
1241
  subagentOperation: operation,
1132
1242
  ...(operation === 'STATUS' ? { subagentWait: inputData?.waitMs !== 0 } : {}),
1133
1243
  subagentId: marAgentAgentId(input) ?? marAgentAgentId(controlOutput) ?? null,
1244
+ ...(typeof outputData?.productSessionId === 'string' &&
1245
+ outputData.productSessionId === marAgentAgentId(controlOutput)
1246
+ ? { subagentSessionId: outputData.productSessionId }
1247
+ : {}),
1134
1248
  ...(title.length === 0 ? {} : { title }),
1135
1249
  ...subagentPayload(details),
1136
1250
  ...(typeof details.latestMessage?.text === 'string'
@@ -1171,9 +1285,9 @@ function marAgentSubagentOperation(toolName) {
1171
1285
  return 'START';
1172
1286
  if (toolName === 'agent_wait')
1173
1287
  return 'STATUS';
1174
- if (toolName === 'agent_message')
1288
+ if (toolName === 'agent_send' || toolName === 'agent_message')
1175
1289
  return 'MESSAGE';
1176
- if (toolName === 'agent_cancel')
1290
+ if (toolName === 'agent_stop' || toolName === 'agent_cancel')
1177
1291
  return 'CANCEL';
1178
1292
  return undefined;
1179
1293
  }
@@ -51,6 +51,11 @@ export class MarAgentManagedRunController {
51
51
  }
52
52
  }),
53
53
  customTitle: (sessionId) => this.host.customTitle?.(sessionId) ?? undefined,
54
+ ...(options.attachNetworkContext === undefined
55
+ ? {}
56
+ : {
57
+ attachNetworkContext: options.attachNetworkContext
58
+ }),
54
59
  modelUsage: (runId, usage) => this.host.emit(runId, 'run.usage', normalizeMarAgentUsage(usage)),
55
60
  publishModelResource: options.publishModelResource,
56
61
  question: (runId, request, signal) => this.requestQuestion(runId, request, signal),
@@ -1,4 +1,4 @@
1
- import { hasMarctlContext, marctlDescription, marctlInstructions } from '../../marctl-guidance.js';
1
+ import { hasMarctlContext, marctlManagementDescription, marctlManagementInstructions } from '../../marctl-guidance.js';
2
2
  export function marctlExecutionEnvironment(environment = {}) {
3
3
  return Object.fromEntries(Object.entries(environment).filter(([key]) => key.startsWith('MAR_CTL_')));
4
4
  }
@@ -7,8 +7,8 @@ export function marctlHostSkill(environment = {}) {
7
7
  return undefined;
8
8
  return {
9
9
  name: 'marctl',
10
- description: marctlDescription,
10
+ description: marctlManagementDescription,
11
11
  requiredTools: ['exec'],
12
- load: async () => marctlInstructions
12
+ load: async () => marctlManagementInstructions
13
13
  };
14
14
  }
@@ -63,6 +63,7 @@ export class MarAgentRuntimeModelFactory {
63
63
  reasoningEfforts: configuration.reasoningEfforts,
64
64
  defaultReasoningEffort: resolveMarAgentDefaultReasoningEffort(configuration.defaultReasoningEffort, configuration.reasoningEfforts),
65
65
  conversationModel: configuration.conversationModel ?? true,
66
+ subagentModel: configuration.subagentModel ?? false,
66
67
  titleGeneration: configuration.titleGeneration,
67
68
  codeMode: configuration.codeMode,
68
69
  ...(configuration.serviceTier === undefined
@@ -2,6 +2,8 @@ import { createHash, randomUUID } from 'node:crypto';
2
2
  import { homedir } from 'node:os';
3
3
  import { access, constants } from 'node:fs/promises';
4
4
  import { LocalAgentHost } from '@myagentroam/agent/host';
5
+ import { SESSION_CONTEXT_PROVIDER } from '@myagentroam/agent/extensions';
6
+ import { agentNetworkExtension } from './agent-network-extension.js';
5
7
  import { marAgentRuntimeCompactConfigurationSchema } from '@myagentroam/protocol';
6
8
  import { nodeExecutionEnvironment } from '../../node-execution-environment.js';
7
9
  import { nodeLog } from '../../operational.js';
@@ -26,8 +28,10 @@ export async function createSessionRuntime(input, sessionKey, fingerprint, hostC
26
28
  let agent;
27
29
  let models = [];
28
30
  let releaseEnvironment;
31
+ let networkContext;
29
32
  try {
30
33
  releaseEnvironment = dependencies.retainEnvironment?.(input.secretEnvironment ?? {});
34
+ networkContext = dependencies.attachNetworkContext?.(input.sessionId);
31
35
  const mcp = mcpLease.pool;
32
36
  const localHost = new LocalAgentHost({
33
37
  publishModelResource: dependencies.publishModelResource === undefined
@@ -68,12 +72,36 @@ export async function createSessionRuntime(input, sessionKey, fingerprint, hostC
68
72
  });
69
73
  const compactState = { revision: 0 };
70
74
  updateMarAgentCompactState(compactState, input.compact, models, input.models.map((model) => model.id));
75
+ const compactSource = marAgentCompactExtensionSource(compactState);
76
+ const networkProvider = networkContext;
71
77
  agent = await dependencies.createAgent({
72
78
  mode: 'node',
73
79
  host: localHost,
74
80
  models: sdkModelConfigurations(models.filter((model) => input.models.some((candidate) => candidate.id === model.id))),
75
81
  defaultModelId: input.model.id,
76
- extensions: marAgentCompactExtensionSource(compactState),
82
+ extensions: networkProvider === undefined
83
+ ? compactSource
84
+ : {
85
+ async resolve(request) {
86
+ const snapshot = await compactSource.resolve(request);
87
+ return {
88
+ ...snapshot,
89
+ extensions: [
90
+ ...snapshot.extensions,
91
+ {
92
+ id: 'mar.node.agent-network-context',
93
+ apiVersion: '1.0',
94
+ attachSession(context) {
95
+ context.services.provide(SESSION_CONTEXT_PROVIDER, networkProvider);
96
+ }
97
+ },
98
+ ...(networkProvider.invoke === undefined
99
+ ? []
100
+ : [agentNetworkExtension(networkProvider.invoke, currentRunId)])
101
+ ]
102
+ };
103
+ }
104
+ },
77
105
  ...(input.personalInstructions ? { systemInstruction: input.personalInstructions } : {})
78
106
  });
79
107
  const customTitle = dependencies.customTitle(input.sessionId) ?? undefined;
@@ -92,6 +120,9 @@ export async function createSessionRuntime(input, sessionKey, fingerprint, hostC
92
120
  normalizer: new MarAgentConversationNormalizer(),
93
121
  mcpLease,
94
122
  releaseEnvironment,
123
+ ...(networkContext === undefined
124
+ ? {}
125
+ : { releaseNetworkContext: () => networkContext?.close() }),
95
126
  automaticTitleEnabled: input.externalSessionId === undefined,
96
127
  models,
97
128
  compactState,
@@ -103,6 +134,7 @@ export async function createSessionRuntime(input, sessionKey, fingerprint, hostC
103
134
  catch (error) {
104
135
  for (const cleanup of [
105
136
  () => agent?.dispose(),
137
+ () => networkContext?.close(),
106
138
  () => closeModelCredentialProviders(models),
107
139
  () => mcpLease.release(),
108
140
  () => releaseEnvironment?.()
@@ -155,6 +187,7 @@ export async function releaseSessionRuntime(runtime) {
155
187
  const failures = [];
156
188
  for (const cleanup of [
157
189
  () => runtime.agent.dispose(),
190
+ () => runtime.releaseNetworkContext?.(),
158
191
  () => runtime.mcpLease.release(),
159
192
  () => runtime.releaseEnvironment?.()
160
193
  ]) {