@myagentroam/node 0.11.44 → 0.11.46

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 (49) hide show
  1. package/dist/connector.js +4 -4
  2. package/dist/database.js +7 -5
  3. package/dist/marctl-guidance.js +2 -2
  4. package/dist/migrations/v023.js +25 -0
  5. package/dist/runner/abstract-runner.js +1 -1
  6. package/dist/runner/claude/managed-run-controller.js +4 -11
  7. package/dist/runner/claude/native-transcript.js +1 -1
  8. package/dist/runner/claude/tool-normalizer.js +16 -11
  9. package/dist/runner/claude-code-runner.js +1 -1
  10. package/dist/runner/codex/conversation-parser.js +29 -38
  11. package/dist/runner/codex/managed-run-controller.js +5 -5
  12. package/dist/runner/codex/native-transcript.js +5 -3
  13. package/dist/runner/codex/tool-normalizer.js +13 -17
  14. package/dist/runner/codex-runner.js +10 -5
  15. package/dist/runner/execution-presentation.js +8 -51
  16. package/dist/runner/mar-agent/agent-network-extension.js +80 -24
  17. package/dist/runner/mar-agent/agent-operation-presentation.js +167 -0
  18. package/dist/runner/mar-agent/conversation-normalizer.js +118 -107
  19. package/dist/runner/mar-agent/history-projection.js +1 -1
  20. package/dist/runner/mar-agent/hosted-conversation-runtime.js +1 -1
  21. package/dist/runner/mar-agent/managed-run-controller.js +2 -2
  22. package/dist/runner/mar-agent-runner.js +5 -5
  23. package/dist/runner/normalization.js +5 -0
  24. package/dist/runner/opencode/managed-run-controller.js +2 -2
  25. package/dist/runner/opencode/tool-normalizer.js +10 -10
  26. package/dist/runner/opencode-runner.js +5 -5
  27. package/dist/runner-profiles.js +1 -1
  28. package/dist/runtime-state.js +7 -7
  29. package/dist/service/agent-network-bindings.js +10 -6
  30. package/dist/service/agent-network-service.js +2 -1
  31. package/dist/service/continuous-run-coordinator.js +1 -1
  32. package/dist/service/external-session-resume-service.js +1 -1
  33. package/dist/service/goal-continuation-coordinator.js +3 -3
  34. package/dist/service/native-session-watch-service.js +6 -3
  35. package/dist/service/planned-restart-handoff.js +24 -5
  36. package/dist/service/restart-session-restorer.js +2 -2
  37. package/dist/service/session-recent-activity.js +1 -1
  38. package/dist/session/native-session-projection-service.js +8 -8
  39. package/dist/session/runtime/conversation-projection.js +10 -3
  40. package/dist/session/session-catalog-service.js +2 -2
  41. package/dist/session/session-command-service.js +1 -0
  42. package/dist/session/session-context-service.js +1 -1
  43. package/dist/session/session-lifecycle-service.js +12 -10
  44. package/dist/session/session-message-service.js +4 -3
  45. package/dist/workspace/git.js +44 -1
  46. package/dist/workspace/workspace-change-service.js +39 -1
  47. package/dist/workspace/workspace-session-service.js +1 -1
  48. package/dist/workspace/workspace-workbench-service.js +8 -0
  49. package/package.json +4 -4
package/dist/connector.js CHANGED
@@ -443,7 +443,7 @@ export class NodeConnector {
443
443
  emitSession: (session) => this.emitSessionWorkbenchEvent({ session }),
444
444
  emitPage: (session, page) => this.emitWorkbenchEvent('conversation', {
445
445
  sessionId: session.id,
446
- snapshot: this.conversationSegmentService.projectInitial(session, page, 10)
446
+ snapshot: this.conversationSegmentService.projectInitial(session, page, 3)
447
447
  }),
448
448
  nodeId: () => this.config?.nodeId ?? 'runtime-node',
449
449
  nodeGeneration: () => this.nodeGeneration,
@@ -854,7 +854,7 @@ export class NodeConnector {
854
854
  prompt: item.content,
855
855
  modelText: prepared.input,
856
856
  clientMessageId: item.clientMessageId,
857
- model: prepared.model,
857
+ modelId: prepared.modelId,
858
858
  effort: prepared.effort,
859
859
  access: prepared.access,
860
860
  collaborationMode: prepared.collaborationMode,
@@ -968,7 +968,7 @@ export class NodeConnector {
968
968
  runner: session.runner,
969
969
  externalSessionId: nativeSessionId,
970
970
  workspaceId: session.workspaceId,
971
- ...(session.model === null ? {} : { model: session.model }),
971
+ ...(session.modelId === null ? {} : { modelId: session.modelId }),
972
972
  ...(session.effort === null ? {} : { effort: session.effort }),
973
973
  ...(session.access === null ? {} : { access: session.access }),
974
974
  ...(session.customTitle === null ? {} : { customTitle: session.customTitle }),
@@ -1508,7 +1508,7 @@ export class NodeConnector {
1508
1508
  workspaceId: task.workspaceId,
1509
1509
  ownerUserId: task.ownerUserId,
1510
1510
  runner: task.runner,
1511
- model: configuredSession.model
1511
+ modelId: configuredSession.modelId
1512
1512
  });
1513
1513
  return {
1514
1514
  secretEnvironment: context.secretEnvironment,
package/dist/database.js CHANGED
@@ -4,6 +4,7 @@ import { NODE_SCHEMA_MIGRATION_V19 } from './migrations/v019.js';
4
4
  import { NODE_SCHEMA_MIGRATION_V20 } from './migrations/v020.js';
5
5
  import { NODE_SCHEMA_MIGRATION_V21 } from './migrations/v021.js';
6
6
  import { NODE_SCHEMA_MIGRATION_V22 } from './migrations/v022.js';
7
+ import { NODE_SCHEMA_MIGRATION_V23 } from './migrations/v023.js';
7
8
  import { normalizeSessionPrompt } from './runner/personal-instructions.js';
8
9
  import { randomUUID } from 'node:crypto';
9
10
  import { closeSync, existsSync, mkdirSync, openSync, rmSync } from 'node:fs';
@@ -27,7 +28,8 @@ export const NODE_SCHEMA_MIGRATIONS = [
27
28
  NODE_SCHEMA_MIGRATION_V19,
28
29
  NODE_SCHEMA_MIGRATION_V20,
29
30
  NODE_SCHEMA_MIGRATION_V21,
30
- NODE_SCHEMA_MIGRATION_V22
31
+ NODE_SCHEMA_MIGRATION_V22,
32
+ NODE_SCHEMA_MIGRATION_V23
31
33
  ];
32
34
  const LEGACY_NODE_SCHEMA_V12 = NODE_SCHEMA_VERSION_V13 - 1;
33
35
  const OBSOLETE_NODE_SCHEMA_V16 = 16;
@@ -314,7 +316,7 @@ export class NodeDatabase {
314
316
  pinOrder = 0;
315
317
  else if (input.pinned === false)
316
318
  pinOrder = null;
317
- const model = input.model === undefined ? (current?.model ?? null) : input.model;
319
+ const model = input.modelId === undefined ? (current?.modelId ?? null) : input.modelId;
318
320
  const effort = input.effort === undefined ? (current?.effort ?? null) : input.effort;
319
321
  const access = input.access === undefined ? (current?.access ?? null) : input.access;
320
322
  const parentSessionId = current === undefined || input.initializeParent === true
@@ -326,7 +328,7 @@ export class NodeDatabase {
326
328
  sessionPrompt,
327
329
  runnerTitle,
328
330
  runnerTitlePending,
329
- model,
331
+ modelId: model,
330
332
  effort,
331
333
  access
332
334
  });
@@ -367,7 +369,7 @@ export class NodeDatabase {
367
369
  runnerTitlePending,
368
370
  pinnedAt: pinOrder === null ? null : 1,
369
371
  pinOrder,
370
- model,
372
+ modelId: model,
371
373
  effort,
372
374
  access
373
375
  };
@@ -666,7 +668,7 @@ function sessionMetadata(row) {
666
668
  sessionPrompt: value('sessionPrompt'),
667
669
  runnerTitle: value('runnerTitle'),
668
670
  runnerTitlePending: raw['runnerTitlePending'] === true,
669
- model: value('model'),
671
+ modelId: value('modelId'),
670
672
  effort: value('effort'),
671
673
  access: value('access')
672
674
  };
@@ -10,7 +10,7 @@ const marctlSafety = 'Acceptance is not completion. If a request result is unkno
10
10
  export const marctlManagementInstructions = [
11
11
  '## MAR Workspace management',
12
12
  'Use native agent_* tools for Agent discovery, creation, communication, observation, waiting and stopping. Do not switch to marctl when a native operation fails.',
13
- 'Use marctl agents config <id> --model <id> --effort <effort> to change future model settings; agents rename <id> --title <title> changes a title.',
13
+ 'Use marctl agents config <id> --model-id <id> --effort <effort> to change future model settings; agents rename <id> --title <title> changes a title.',
14
14
  'Use agents prompt get/set/clear for persistent instructions; set accepts -m or --message-file. Use agents schedule list/get/create/update for scheduled tasks; update requires --expected-revision.',
15
15
  'marctl nodes lists authorized Nodes; marctl ssh executes remote commands and marctl scp transfers files. Use marctl help agents, nodes, ssh or scp for options.',
16
16
  marctlSafety
@@ -19,7 +19,7 @@ export const marctlDescription = 'Use marctl agents to create, send to, wait for
19
19
  export const marctlInstructions = [
20
20
  '## MAR Workspace CLI',
21
21
  'Run marctl from the shell. Commands use the authenticated Node and Workspace; marctl help agents, nodes, ssh or scp shows options.',
22
- 'Use agents start --title <title> -m <text> [--runner <runner>] [--model <id>] [--effort <effort>] [--goal <objective>]. An Agent must supply a nonblank title; a manual terminal may omit it. The default Runner is the current Session Runner; a terminal without a current Session must specify --runner or an explicit --parent. Start returns without waiting.',
22
+ 'Use agents start --title <title> -m <text> [--runner <runner>] [--model-id <id>] [--effort <effort>] [--goal <objective>]. An Agent must supply a nonblank title; a manual terminal may omit it. The default Runner is the current Session Runner; a terminal without a current Session must specify --runner or an explicit --parent. Start returns without waiting.',
23
23
  'Use agents send <id> -m <text> [--steer|--queue], agents wait [<id>] [--wait-for message|completion] [--timeout <duration>], agents stop <id> --execution <execution-id>, agents list [--scope children|descendants|workspace|profile], and agents view <id> [--history]. Wait defaults to direct-child completion for 30 seconds; --timeout 0ms takes a snapshot. Stop clears the target Session Goal.',
24
24
  'Prefer agents send <id> --steer -m <text> for timely communication or course correction. Use --queue only for deliberately deferred input: it is consumed after current work and earlier queued inputs finish, and with an active Goal it is not consumed until that Goal exits. Acceptance is not a guarantee of immediate processing.',
25
25
  'Start and send accept repeated --image <local-path> for PNG, JPEG, GIF or WebP; limits are five images, 5 MiB each and 10 MiB total. Send can update a Goal alone with --goal <objective> or --clear-goal; omitted Goal leaves it unchanged. Decide a direct child approval with send <id> --approval <approval-id> --decision approve|reject.',
@@ -0,0 +1,25 @@
1
+ import { NODE_SCHEMA_MIGRATION_V22 } from './v022.js';
2
+ const defaultPaths = ['codex', 'claude-code', 'opencode', 'mar-agent'].map((runner) => `$.runnerDefaults."${runner}"`);
3
+ const rename = (column, path) => `json_remove(CASE WHEN json_type(${column}, '${path}.modelId') IS NULL
4
+ THEN json_set(${column}, '${path}.modelId', json_extract(${column}, '${path}.model'))
5
+ ELSE ${column} END, '${path}.model')`;
6
+ export const NODE_SCHEMA_MIGRATION_V23 = {
7
+ version: 23,
8
+ sql: [
9
+ `UPDATE agent_sessions SET metadata = ${rename('metadata', '$')}
10
+ WHERE json_type(metadata, '$.model') IS NOT NULL;`,
11
+ ...defaultPaths.map((path) => `UPDATE node_settings SET settings = ${rename('settings', path)}
12
+ WHERE json_type(settings, '${path}.model') IS NOT NULL;`)
13
+ ].join('\n'),
14
+ preflight: (database) => NODE_SCHEMA_MIGRATION_V22.assert(database),
15
+ assert(database) {
16
+ NODE_SCHEMA_MIGRATION_V22.assert(database);
17
+ if (database
18
+ .prepare("SELECT 1 FROM agent_sessions WHERE json_type(metadata, '$.model') IS NOT NULL")
19
+ .get() !== undefined ||
20
+ defaultPaths.some((path) => database
21
+ .prepare(`SELECT 1 FROM node_settings WHERE json_type(settings, '${path}.model') IS NOT NULL`)
22
+ .get() !== undefined))
23
+ throw new Error('NODE_DATABASE_SCHEMA_INVALID:modelId');
24
+ }
25
+ };
@@ -30,7 +30,7 @@ export class AbstractRunner {
30
30
  supportsConfiguration(configuration, workspace, environment) {
31
31
  void [workspace, environment];
32
32
  const profile = this.profileForValidation();
33
- const model = profile.models.find((candidate) => candidate.id === configuration.model);
33
+ const model = profile.models.find((candidate) => candidate.id === configuration.modelId);
34
34
  return (configuration.runner === this.name &&
35
35
  model !== undefined &&
36
36
  model.supportedEfforts.includes(configuration.effort) &&
@@ -62,7 +62,7 @@ export class ClaudeManagedRunController {
62
62
  input,
63
63
  cwd: session.cwd,
64
64
  externalSessionId: session.externalSessionId,
65
- model: session.model,
65
+ model: session.modelId,
66
66
  effort: session.effort,
67
67
  access: session.access,
68
68
  secretEnvironment
@@ -462,26 +462,19 @@ export class ClaudeManagedRunController {
462
462
  const tool = toolInputs.get(result.toolUseId);
463
463
  if (tool === undefined)
464
464
  continue;
465
- const normalized = normalizeClaudeTool(tool.name, tool.input, result.output, result.failed, tool.inputTruncated || result.truncated, knownSubagents, result.claudeResult, result.truncated);
465
+ const normalized = normalizeClaudeTool(tool.name, tool.input, result.output, result.failed, tool.inputTruncated || result.truncated, knownSubagents, result.claudeResult, result.truncated, tool.inputTruncated, result.truncated ? 'HEAD' : undefined);
466
466
  this.host.emitItem(runId, {
467
467
  itemId: boundedConversationItemId('claude-tool', result.toolUseId),
468
468
  kind,
469
469
  status: result.failed ? 'FAILED' : 'COMPLETED',
470
470
  payload: kind === 'command_execution'
471
- ? {
472
- outputPreview: result.output,
473
- exitCode: null,
474
- durationMs: null,
475
- truncated: result.truncated
476
- }
471
+ ? normalized.payload
477
472
  : kind === 'file_change'
478
473
  ? {}
479
474
  : {
480
475
  ...normalized.payload,
481
- outputSummary: result.output,
482
476
  progressLabel: null,
483
- errorCode: result.failed ? 'TOOL_EXECUTION_FAILED' : null,
484
- truncated: result.truncated
477
+ errorCode: result.failed ? 'TOOL_EXECUTION_FAILED' : null
485
478
  },
486
479
  merge: true
487
480
  });
@@ -339,7 +339,7 @@ export const claudeNativeTranscriptAdapter = {
339
339
  return {
340
340
  userText: (text) => publicGoalHistoryText(stripClaudePlanTag(text)),
341
341
  planText: claudePlanText,
342
- normalizeTool: (entry) => normalizeClaudeTool(entry.toolName, entry.input, entry.outputSummary, entry.failed, entry.inputTruncated || entry.outputTruncated, titles, claudeNativeToolResultMetadata(entry.subagentResult), entry.outputTruncated),
342
+ normalizeTool: (entry) => normalizeClaudeTool(entry.toolName, entry.input, entry.outputSummary, entry.failed, entry.inputTruncated || entry.outputTruncated, titles, claudeNativeToolResultMetadata(entry.subagentResult), entry.outputTruncated, entry.inputTruncated, entry.outputTruncated ? 'HEAD' : undefined),
343
343
  userInputQuestions: (entry) => entry.toolName === 'AskUserQuestion' && isPlainRecord(entry.input)
344
344
  ? claudeUserInputQuestions(entry.input)
345
345
  : []
@@ -1,5 +1,5 @@
1
1
  import { claudeContentBlocks } from './message-parsers.js';
2
- import { compactRunnerText, compactRunnerValue, publicOutputSnapshot } from '../normalization.js';
2
+ import { compactRunnerText, compactRunnerValue, publicOutputSnapshot, publicToolOutput } from '../normalization.js';
3
3
  import { shellExecution } from '../execution-presentation.js';
4
4
  import { RunnerUsageTracker, usageToken, usageTokenSum } from '../../service/runner-usage-tracker.js';
5
5
  export class ClaudeUsageNormalizer {
@@ -99,11 +99,9 @@ export function extractClaudeToolResults(message) {
99
99
  };
100
100
  });
101
101
  }
102
- export function normalizeClaudeTool(toolName, input, output, failed, truncated, knownSubagents = new Map(), resultMetadata, sourceOutputTruncated = output === null ? false : truncated) {
102
+ export function normalizeClaudeTool(toolName, input, output, failed, truncated, knownSubagents = new Map(), resultMetadata, sourceOutputTruncated = output === null ? false : truncated, sourceInputTruncated = output === null ? truncated : false, outputDirection) {
103
103
  const values = record(input);
104
104
  const normalizedToolName = compactRunnerText(toolName, 128) || 'unknown';
105
- const normalizedOutput = output === null ? null : compactRunnerText(output, 10_000);
106
- const outputTruncated = sourceOutputTruncated || (output?.length ?? 0) > 10_000;
107
105
  const subagent = claudeSubagentProjection(toolName, input, resultMetadata ?? output, knownSubagents);
108
106
  if (isCommandTool(toolName))
109
107
  return {
@@ -112,14 +110,15 @@ export function normalizeClaudeTool(toolName, input, output, failed, truncated,
112
110
  toolName: normalizedToolName,
113
111
  command: compactRunnerText(typeof values?.command === 'string' ? values.command : normalizedToolName, 10_000),
114
112
  cwd: null,
115
- outputPreview: normalizedOutput ?? '',
116
113
  exitCode: null,
117
114
  durationMs: null,
118
- truncated: truncated || outputTruncated,
119
115
  ...(output === null || output === undefined
120
116
  ? {}
121
- : { output: publicOutputSnapshot(output, outputTruncated) }),
122
- toolPresentation: shellExecution(output ?? undefined, outputTruncated)
117
+ : {
118
+ output: publicToolOutput(output, outputDirection ?? (sourceOutputTruncated ? 'SOURCE' : undefined), 'COMBINED')
119
+ }),
120
+ toolPresentation: shellExecution(sourceInputTruncated ||
121
+ (typeof values?.command === 'string' && values.command.length > 10_000))
123
122
  }
124
123
  };
125
124
  if (isFileTool(toolName)) {
@@ -154,11 +153,17 @@ export function normalizeClaudeTool(toolName, input, output, failed, truncated,
154
153
  toolName: normalizedToolName,
155
154
  title: compactRunnerText(toolName, 500) || normalizedToolName,
156
155
  inputSummary: compactRunnerValue(input, 10_000).text || null,
157
- outputSummary: normalizedOutput,
158
- ...(output === null ? {} : { output: publicOutputSnapshot(output, outputTruncated) }),
156
+ outputSummary: null,
157
+ ...(output === null
158
+ ? {}
159
+ : {
160
+ output: publicToolOutput(output, outputDirection ?? (sourceOutputTruncated ? 'SOURCE' : undefined))
161
+ }),
159
162
  progressLabel: null,
160
163
  errorCode: failed ? 'TOOL_EXECUTION_FAILED' : null,
161
- truncated: truncated || outputTruncated,
164
+ ...(sourceInputTruncated || compactRunnerValue(input, 10_000).truncated
165
+ ? { inputTruncated: true }
166
+ : {}),
162
167
  ...(toolName === 'Read'
163
168
  ? {
164
169
  toolPresentation: {
@@ -49,7 +49,7 @@ export class ClaudeCodeRunner extends AbstractRunner {
49
49
  defaultConfiguration() {
50
50
  return {
51
51
  runner: this.name,
52
- model: claudeDefaultModel(this.environment),
52
+ modelId: claudeDefaultModel(this.environment),
53
53
  effort: 'medium',
54
54
  access: 'default'
55
55
  };
@@ -132,7 +132,7 @@ import { conversationImageAttachmentSchema } from '@myagentroam/protocol';
132
132
  import { parseCodexComposerImageMarkers } from './composer-image-markers.js';
133
133
  import { normalizeCodexTool } from './tool-normalizer.js';
134
134
  import { shellExecution } from '../execution-presentation.js';
135
- import { publicOutputSnapshot } from '../normalization.js';
135
+ import { publicToolOutput } from '../normalization.js';
136
136
  import { publicGoalHistoryText } from '../../service/goal-runtime.js';
137
137
  const MAX_COMPOSER_ATTACHMENT_BYTES = 5 * 1024 * 1024;
138
138
  const MAX_COMPOSER_ATTACHMENTS_BYTES = 10 * 1024 * 1024;
@@ -551,13 +551,9 @@ export function mergeCodexCustomToolItems(invocation, result) {
551
551
  completedAt: result.completedAt,
552
552
  payload: {
553
553
  ...invocationPayload,
554
- outputPreview: resultPayload.outputPreview ??
555
- resultPayload.outputSummary ??
556
- invocationPayload.outputPreview,
557
554
  ...(resultPayload.output === undefined ? {} : { output: resultPayload.output }),
558
555
  exitCode: resultPayload.exitCode ?? invocationPayload.exitCode,
559
556
  durationMs: resultPayload.durationMs ?? invocationPayload.durationMs,
560
- truncated: invocationPayload.truncated === true || resultPayload.truncated === true,
561
557
  ...(resultPayload.attachments === undefined
562
558
  ? {}
563
559
  : { attachments: resultPayload.attachments })
@@ -576,7 +572,6 @@ export function mergeCodexCustomToolItems(invocation, result) {
576
572
  outputSummary: resultPayload.outputSummary ?? invocationPayload.outputSummary,
577
573
  ...(resultPayload.output === undefined ? {} : { output: resultPayload.output }),
578
574
  errorCode: resultPayload.errorCode ?? invocationPayload.errorCode,
579
- truncated: invocationPayload.truncated === true || resultPayload.truncated === true,
580
575
  ...(resultPayload.attachments === undefined ? {} : { attachments: resultPayload.attachments })
581
576
  }
582
577
  };
@@ -720,7 +715,6 @@ export function codexOfficialItem(session, nativeTurnId, value, turnSequence, it
720
715
  ? compactRunnerText(value.command, 10_000)
721
716
  : '(No command provided)',
722
717
  cwd: typeof value.cwd === 'string' ? compactRunnerText(value.cwd, 4_096) : null,
723
- outputPreview: publicOutputSnapshot(output).text,
724
718
  exitCode: typeof value.exitCode === 'number' && Number.isInteger(value.exitCode)
725
719
  ? value.exitCode
726
720
  : null,
@@ -729,21 +723,16 @@ export function codexOfficialItem(session, nativeTurnId, value, turnSequence, it
729
723
  value.durationMs >= 0
730
724
  ? value.durationMs
731
725
  : null,
732
- truncated: output.length > 10_000,
733
- ...(base.status !== 'IN_PROGRESS'
734
- ? {
735
- output: typeof value.aggregatedOutput === 'string'
736
- ? publicOutputSnapshot(output)
737
- : undefined
738
- }
726
+ ...(typeof value.aggregatedOutput === 'string'
727
+ ? { output: publicToolOutput(output, undefined, 'COMBINED') }
739
728
  : {}),
740
- toolPresentation: shellExecution(base.status === 'IN_PROGRESS' || typeof value.aggregatedOutput !== 'string'
741
- ? undefined
742
- : output)
729
+ toolPresentation: shellExecution((typeof value.command === 'string' && value.command.length > 10_000) ||
730
+ (typeof value.cwd === 'string' && value.cwd.length > 4_096))
743
731
  }
744
732
  };
745
733
  }
746
- if (value.type === 'mcpToolCall')
734
+ if (value.type === 'mcpToolCall') {
735
+ const input = compactRunnerValue(value.arguments, 10_000);
747
736
  return {
748
737
  ...base,
749
738
  kind: 'tool_call',
@@ -756,12 +745,14 @@ export function codexOfficialItem(session, nativeTurnId, value, turnSequence, it
756
745
  .join('.'),
757
746
  namespace: typeof value.server === 'string' ? value.server : '',
758
747
  toolName: typeof value.tool === 'string' ? value.tool : '',
759
- inputSummary: codexCompactJson(value.arguments),
760
- outputSummary: codexCompactJson(value.result),
761
- ...(value.result == null ? {} : { output: publicOutputSnapshot(value.result) }),
748
+ inputSummary: input.text,
749
+ ...(input.truncated ? { inputTruncated: true } : {}),
750
+ outputSummary: null,
751
+ ...(value.result == null ? {} : { output: publicToolOutput(value.result) }),
762
752
  ...(value.error === undefined ? {} : { errorCode: codexCompactJson(value.error) })
763
753
  }
764
754
  };
755
+ }
765
756
  if (value.type === 'webSearch')
766
757
  return {
767
758
  ...base,
@@ -804,15 +795,16 @@ export function codexOfficialItem(session, nativeTurnId, value, turnSequence, it
804
795
  ? value.name
805
796
  : 'tool';
806
797
  const outputValue = value.contentItems ?? value.output;
807
- const output = outputValue == null ? undefined : publicOutputSnapshot(codexToolOutput(outputValue));
798
+ const output = outputValue == null ? undefined : codexToolOutput(outputValue);
799
+ const input = compactRunnerValue(value.arguments ?? value.input, 10_000);
808
800
  const normalized = normalizeCodexTool({
809
801
  name,
810
802
  namespace: typeof value.namespace === 'string' ? value.namespace : 'codex',
811
803
  rawInput: value.arguments ?? value.input,
812
- inputSummary: codexCompactJson(value.arguments ?? value.input),
813
- outputSummary: output?.text ?? null,
814
- outputTruncated: output?.truncated ?? false,
815
- truncated: output?.truncated ?? false,
804
+ inputSummary: input.text || null,
805
+ inputTruncated: input.truncated,
806
+ outputSummary: output === undefined ? null : typeof output === 'string' ? output : JSON.stringify(output),
807
+ outputTruncated: false,
816
808
  outputOnly: value.type === 'custom_tool_call_output' ||
817
809
  value.type === 'function_call_output' ||
818
810
  value.type === 'customToolCallOutput' ||
@@ -906,7 +898,6 @@ export function codexLiveConversationItem(value, completed, imageAttachments = [
906
898
  ? compactRunnerText(value.command, 10_000)
907
899
  : '(No command provided)',
908
900
  cwd: typeof value.cwd === 'string' ? compactRunnerText(value.cwd, 4_096) : null,
909
- outputPreview: publicOutputSnapshot(output).text,
910
901
  exitCode: typeof value.exitCode === 'number' && Number.isInteger(value.exitCode)
911
902
  ? value.exitCode
912
903
  : null,
@@ -915,21 +906,20 @@ export function codexLiveConversationItem(value, completed, imageAttachments = [
915
906
  value.durationMs >= 0
916
907
  ? value.durationMs
917
908
  : null,
918
- truncated: output.length > 10_000,
919
909
  ...(completed
920
910
  ? {
921
911
  output: typeof value.aggregatedOutput === 'string'
922
- ? publicOutputSnapshot(output)
912
+ ? publicToolOutput(output, undefined, 'COMBINED')
923
913
  : undefined
924
914
  }
925
915
  : {}),
926
- toolPresentation: shellExecution(!completed || typeof value.aggregatedOutput !== 'string' ? undefined : output)
916
+ toolPresentation: shellExecution((typeof value.command === 'string' && value.command.length > 10_000) ||
917
+ (typeof value.cwd === 'string' && value.cwd.length > 4_096))
927
918
  }
928
919
  };
929
920
  }
930
921
  if (value.type === 'mcpToolCall') {
931
922
  const input = compactRunnerValue(value.arguments, 10_000);
932
- const output = compactRunnerValue(value.result, 10_000);
933
923
  const error = compactRunnerValue(value.error, 80);
934
924
  const server = typeof value.server === 'string' ? value.server : 'mcp';
935
925
  const tool = typeof value.tool === 'string' ? value.tool : 'tool';
@@ -942,11 +932,11 @@ export function codexLiveConversationItem(value, completed, imageAttachments = [
942
932
  toolName: compactRunnerText(tool, 128),
943
933
  title: compactRunnerText(`${server}.${tool}`, 500),
944
934
  inputSummary: input.text || null,
945
- outputSummary: output.text || null,
946
- ...(value.result == null ? {} : { output: publicOutputSnapshot(value.result) }),
935
+ outputSummary: null,
936
+ ...(value.result == null ? {} : { output: publicToolOutput(value.result) }),
947
937
  progressLabel: null,
948
938
  errorCode: error.text || null,
949
- truncated: input.truncated || output.truncated || error.truncated
939
+ ...(input.truncated ? { inputTruncated: true } : {})
950
940
  }
951
941
  };
952
942
  }
@@ -1021,7 +1011,7 @@ export function codexLiveConversationItem(value, completed, imageAttachments = [
1021
1011
  : 'tool';
1022
1012
  const input = compactRunnerValue(value.arguments ?? value.input, 10_000);
1023
1013
  const outputValue = value.contentItems ?? value.output;
1024
- const output = outputValue == null ? undefined : publicOutputSnapshot(codexToolOutput(outputValue));
1014
+ const output = outputValue == null ? undefined : codexToolOutput(outputValue);
1025
1015
  const outputOnly = value.type === 'custom_tool_call_output' ||
1026
1016
  value.type === 'function_call_output' ||
1027
1017
  value.type === 'customToolCallOutput' ||
@@ -1031,9 +1021,10 @@ export function codexLiveConversationItem(value, completed, imageAttachments = [
1031
1021
  namespace: typeof value.namespace === 'string' ? value.namespace : 'codex',
1032
1022
  rawInput: value.arguments ?? value.input,
1033
1023
  inputSummary: input.text || null,
1034
- outputSummary: output?.text ?? null,
1035
- truncated: input.truncated || output?.truncated === true,
1036
- outputTruncated: output?.truncated ?? false,
1024
+ inputTruncated: input.truncated,
1025
+ outputSummary: output === undefined ? null : typeof output === 'string' ? output : JSON.stringify(output),
1026
+ truncated: input.truncated,
1027
+ outputTruncated: false,
1037
1028
  outputOnly,
1038
1029
  attachments: imageAttachments
1039
1030
  });
@@ -1,7 +1,7 @@
1
1
  import { NODE_MCP } from '../../mcp/naming.js';
2
2
  import { nodeExecutionEnvironment } from '../../node-execution-environment.js';
3
3
  import { extractId } from './conversation-parser.js';
4
- import { publicOutputSnapshot } from '../normalization.js';
4
+ import { publicToolOutput } from '../normalization.js';
5
5
  import { normalizeCodexApproval } from './tool-normalizer.js';
6
6
  import { parseSecretEnvironment } from '../../mcp/secret-environment.js';
7
7
  import { safeErrorCode } from '../../util/safe-error.js';
@@ -280,14 +280,14 @@ export class CodexManagedRunController {
280
280
  if (notification.method === 'item/commandExecution/outputDelta' &&
281
281
  typeof params.itemId === 'string' &&
282
282
  typeof params.delta === 'string') {
283
- const output = publicOutputSnapshot(params.delta);
283
+ const output = publicToolOutput(params.delta, undefined, 'COMBINED');
284
284
  this.emitItem(runId, {
285
285
  itemId: boundedConversationItemId('codex', params.itemId),
286
286
  kind: 'command_execution',
287
287
  status: 'IN_PROGRESS',
288
- payload: { outputPreview: output.text, output },
288
+ payload: { output },
289
289
  merge: true,
290
- append: 'output'
290
+ ...(output.truncation === 'HEAD' ? {} : { append: 'output' })
291
291
  });
292
292
  return;
293
293
  }
@@ -400,7 +400,7 @@ export class CodexManagedRunController {
400
400
  usage = new CodexUsageNormalizer(runId, (value) => this.host.emit(runId, 'run.usage', {
401
401
  ...value,
402
402
  sessionId: this.runner.execution.runs.get(runId)?.threadId
403
- }), active ? (this.host.session(active.sessionId)?.model ?? undefined) : undefined);
403
+ }), active ? (this.host.session(active.sessionId)?.modelId ?? undefined) : undefined);
404
404
  this.usage.set(runId, usage);
405
405
  }
406
406
  return usage;
@@ -1,7 +1,7 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { basename, join } from 'node:path';
3
3
  import { forEachJsonlRecord } from '../../native-jsonl-reader.js';
4
- import { compactRunnerValue as compactValue } from '../normalization.js';
4
+ import { compactRunnerValue as compactValue, publicOutputSnapshot } from '../normalization.js';
5
5
  import { stripGoalStartPrompt, publicGoalHistoryText } from '../../service/goal-runtime.js';
6
6
  import { parseCodexComposerImageMarkers } from './composer-image-markers.js';
7
7
  import { codexTranscriptSourceItemId } from './conversation-parser.js';
@@ -167,7 +167,7 @@ function codexToolResultsOf(record, createdAt) {
167
167
  const toolUseId = firstString(payload, ['call_id', 'id']);
168
168
  if (toolUseId === undefined)
169
169
  return [];
170
- const output = compactValue(codexToolOutput(payload['output'] ?? payload['contentItems']), 10_000);
170
+ const output = publicOutputSnapshot(codexToolOutput(payload['output'] ?? payload['contentItems']));
171
171
  const imageAttachments = codexImagePlaceholders(payload['output'] ?? payload['contentItems']);
172
172
  return [
173
173
  {
@@ -566,7 +566,9 @@ export const codexNativeTranscriptAdapter = {
566
566
  rawInput: entry.input,
567
567
  inputSummary: entry.inputSummary,
568
568
  outputSummary: entry.outputSummary,
569
- truncated: entry.inputTruncated || entry.outputTruncated,
569
+ inputTruncated: entry.inputTruncated,
570
+ outputTruncated: entry.outputTruncated,
571
+ ...(entry.outputTruncated ? { outputDirection: 'HEAD' } : {}),
570
572
  ...(entry.imageAttachments === undefined ? {} : { attachments: entry.imageAttachments })
571
573
  }),
572
574
  userInputQuestions: () => []
@@ -1,4 +1,4 @@
1
- import { compactRunnerText, publicOutputSnapshot } from '../normalization.js';
1
+ import { compactRunnerText, publicToolOutput } from '../normalization.js';
2
2
  import { shellExecution } from '../execution-presentation.js';
3
3
  export function normalizeCodexApproval(method) {
4
4
  return method === 'item/commandExecution/requestApproval'
@@ -10,20 +10,18 @@ export function normalizeCodexTool(input) {
10
10
  const rawInput = input.rawInput;
11
11
  const attachments = input.attachments ?? [];
12
12
  const outputTruncated = input.outputTruncated ?? (input.outputSummary != null && input.truncated === true);
13
+ const outputDirection = input.outputDirection ?? (outputTruncated ? 'SOURCE' : undefined);
13
14
  const subagent = codexSubagentProjection(input.namespace, name, rawInput, input.outputSummary);
14
15
  if (isCommandTool(name)) {
15
- const outputPreview = compactRunnerText(input.outputSummary ?? '', 10_000);
16
16
  if (input.outputOnly === true)
17
17
  return {
18
18
  kind: 'command_execution',
19
19
  payload: {
20
20
  toolName: name,
21
- outputPreview,
22
- truncated: input.truncated === true || (input.outputSummary?.length ?? 0) > 10_000,
23
21
  ...(input.outputSummary == null
24
22
  ? {}
25
- : { output: publicOutputSnapshot(input.outputSummary, outputTruncated) }),
26
- toolPresentation: shellExecution(input.outputSummary ?? undefined, outputTruncated),
23
+ : { output: publicToolOutput(input.outputSummary, outputDirection, 'COMBINED') }),
24
+ toolPresentation: shellExecution(),
27
25
  ...(attachments.length === 0 ? {} : { attachments })
28
26
  }
29
27
  };
@@ -41,16 +39,14 @@ export function normalizeCodexTool(input) {
41
39
  toolName: name,
42
40
  command: compactRunnerText(command, 10_000),
43
41
  cwd: typeof values?.cwd === 'string' ? compactRunnerText(values.cwd, 4_096) : null,
44
- outputPreview,
45
42
  exitCode: null,
46
43
  durationMs: null,
47
- toolPresentation: shellExecution(input.outputSummary ?? undefined, outputTruncated),
44
+ toolPresentation: shellExecution(input.inputTruncated === true ||
45
+ command.length > 10_000 ||
46
+ (typeof values?.cwd === 'string' && values.cwd.length > 4_096)),
48
47
  ...(input.outputSummary == null
49
48
  ? {}
50
- : { output: publicOutputSnapshot(input.outputSummary, outputTruncated) }),
51
- truncated: input.truncated === true ||
52
- command.length > 10_000 ||
53
- (input.outputSummary?.length ?? 0) > 10_000,
49
+ : { output: publicToolOutput(input.outputSummary, outputDirection, 'COMBINED') }),
54
50
  ...(attachments.length === 0 ? {} : { attachments })
55
51
  }
56
52
  };
@@ -59,10 +55,10 @@ export function normalizeCodexTool(input) {
59
55
  return {
60
56
  kind: 'tool_call',
61
57
  payload: {
62
- outputSummary: input.outputSummary == null ? null : compactRunnerText(input.outputSummary, 10_000),
58
+ outputSummary: null,
63
59
  ...(input.outputSummary == null
64
60
  ? {}
65
- : { output: publicOutputSnapshot(input.outputSummary, outputTruncated) }),
61
+ : { output: publicToolOutput(input.outputSummary, outputDirection) }),
66
62
  ...subagent,
67
63
  ...(attachments.length === 0 ? {} : { attachments })
68
64
  }
@@ -74,13 +70,13 @@ export function normalizeCodexTool(input) {
74
70
  namespace: compactRunnerText(input.namespace ?? 'codex', 128),
75
71
  toolName: name,
76
72
  inputSummary: input.inputSummary ?? null,
77
- outputSummary: input.outputSummary == null ? null : compactRunnerText(input.outputSummary, 10_000),
73
+ outputSummary: null,
78
74
  ...(input.outputSummary == null
79
75
  ? {}
80
- : { output: publicOutputSnapshot(input.outputSummary, outputTruncated) }),
76
+ : { output: publicToolOutput(input.outputSummary, outputDirection) }),
81
77
  progressLabel: null,
82
78
  errorCode: null,
83
- truncated: input.truncated === true,
79
+ ...(input.inputTruncated ? { inputTruncated: true } : {}),
84
80
  ...subagent,
85
81
  ...(attachments.length === 0 ? {} : { attachments })
86
82
  }