@myagentroam/agent 0.9.158 → 0.9.160

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 (130) hide show
  1. package/dist/agent-core/extensions/agents.d.ts +69 -0
  2. package/dist/agent-core/extensions/agents.js +160 -0
  3. package/dist/agent-core/extensions/contracts.d.ts +260 -0
  4. package/dist/agent-core/extensions/contracts.js +1 -0
  5. package/dist/agent-core/extensions/data.d.ts +84 -0
  6. package/dist/agent-core/extensions/data.js +83 -0
  7. package/dist/agent-core/extensions/environment.d.ts +27 -0
  8. package/dist/agent-core/extensions/environment.js +52 -0
  9. package/dist/agent-core/extensions/files.d.ts +64 -0
  10. package/dist/agent-core/extensions/files.js +26 -0
  11. package/dist/agent-core/extensions/hooks.d.ts +32 -0
  12. package/dist/agent-core/extensions/hooks.js +135 -0
  13. package/dist/agent-core/extensions/manager.d.ts +39 -0
  14. package/dist/agent-core/extensions/manager.js +458 -0
  15. package/dist/agent-core/extensions/mcp.d.ts +99 -0
  16. package/dist/agent-core/extensions/mcp.js +433 -0
  17. package/dist/agent-core/extensions/model-request.d.ts +60 -0
  18. package/dist/agent-core/extensions/model-request.js +167 -0
  19. package/dist/agent-core/extensions/model-stream.d.ts +9 -0
  20. package/dist/agent-core/extensions/model-stream.js +101 -0
  21. package/dist/agent-core/extensions/models.d.ts +43 -0
  22. package/dist/agent-core/extensions/models.js +1 -0
  23. package/dist/agent-core/extensions/network.d.ts +32 -0
  24. package/dist/agent-core/extensions/network.js +97 -0
  25. package/dist/agent-core/extensions/operation.d.ts +29 -0
  26. package/dist/agent-core/extensions/operation.js +98 -0
  27. package/dist/agent-core/extensions/processes.d.ts +52 -0
  28. package/dist/agent-core/extensions/processes.js +283 -0
  29. package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
  30. package/dist/agent-core/extensions/prompt-composition.js +106 -0
  31. package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
  32. package/dist/agent-core/extensions/prompt-input.js +46 -0
  33. package/dist/agent-core/extensions/resources.d.ts +27 -0
  34. package/dist/agent-core/extensions/resources.js +221 -0
  35. package/dist/agent-core/extensions/services.d.ts +31 -0
  36. package/dist/agent-core/extensions/services.js +61 -0
  37. package/dist/agent-core/extensions/session-input.d.ts +151 -0
  38. package/dist/agent-core/extensions/session-input.js +351 -0
  39. package/dist/agent-core/extensions/session-messages.d.ts +41 -0
  40. package/dist/agent-core/extensions/session-messages.js +76 -0
  41. package/dist/agent-core/extensions/session.d.ts +68 -0
  42. package/dist/agent-core/extensions/session.js +593 -0
  43. package/dist/agent-core/extensions/settings.d.ts +59 -0
  44. package/dist/agent-core/extensions/settings.js +115 -0
  45. package/dist/agent-core/extensions/store.d.ts +10 -0
  46. package/dist/agent-core/extensions/store.js +131 -0
  47. package/dist/agent-core/extensions/tools.d.ts +61 -0
  48. package/dist/agent-core/extensions/tools.js +266 -0
  49. package/dist/agent-core/host/contracts.d.ts +25 -0
  50. package/dist/agent-core/host/local-agent-host.d.ts +10 -1
  51. package/dist/agent-core/host/local-agent-host.js +132 -20
  52. package/dist/agent-core/host/local-file-access.d.ts +30 -0
  53. package/dist/agent-core/host/local-file-access.js +107 -0
  54. package/dist/agent-core/host/local-network-access.d.ts +6 -0
  55. package/dist/agent-core/host/local-network-access.js +97 -0
  56. package/dist/agent-core/host/local-process-executor.d.ts +5 -1
  57. package/dist/agent-core/host/local-session-environment.d.ts +18 -0
  58. package/dist/agent-core/host/local-session-environment.js +102 -0
  59. package/dist/agent-core/model/anthropic-messages.js +73 -23
  60. package/dist/agent-core/model/contracts.d.ts +21 -0
  61. package/dist/agent-core/model/openai-responses.d.ts +3 -1
  62. package/dist/agent-core/model/openai-responses.js +248 -97
  63. package/dist/agent-core/model/text-blocks.d.ts +7 -0
  64. package/dist/agent-core/model/text-blocks.js +40 -0
  65. package/dist/agent-core/prompts/index.d.ts +6 -2
  66. package/dist/agent-core/prompts/index.js +38 -24
  67. package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
  68. package/dist/agent-core/runtime/context/normalize.js +21 -1
  69. package/dist/agent-core/runtime/context/projection.js +35 -1
  70. package/dist/agent-core/runtime/context/request-budget.js +5 -2
  71. package/dist/agent-core/runtime/context/restore.d.ts +5 -0
  72. package/dist/agent-core/runtime/context/restore.js +47 -4
  73. package/dist/agent-core/runtime/context/usage.js +2 -3
  74. package/dist/agent-core/runtime/contracts.d.ts +2 -0
  75. package/dist/agent-core/runtime/execution.js +5 -2
  76. package/dist/agent-core/sdk/agent.js +177 -40
  77. package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
  78. package/dist/agent-core/sdk/extension-agents.js +51 -0
  79. package/dist/agent-core/sdk/extension-models.d.ts +13 -0
  80. package/dist/agent-core/sdk/extension-models.js +128 -0
  81. package/dist/agent-core/sdk/extension-session.d.ts +30 -0
  82. package/dist/agent-core/sdk/extension-session.js +103 -0
  83. package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
  84. package/dist/agent-core/sdk/extension-tools.js +126 -0
  85. package/dist/agent-core/sdk/session.d.ts +26 -2
  86. package/dist/agent-core/sdk/session.js +1215 -506
  87. package/dist/agent-core/sdk/types.d.ts +3 -0
  88. package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
  89. package/dist/agent-core/session/jsonl-store.d.ts +28 -8
  90. package/dist/agent-core/session/jsonl-store.js +124 -84
  91. package/dist/agent-core/session/types.d.ts +4 -1
  92. package/dist/agent-core/storage/file-lock.d.ts +4 -0
  93. package/dist/agent-core/storage/file-lock.js +83 -0
  94. package/dist/agent-core/subagent/contracts.d.ts +11 -1
  95. package/dist/agent-core/subagent/scheduler.d.ts +8 -0
  96. package/dist/agent-core/subagent/session-controller.d.ts +22 -1
  97. package/dist/agent-core/subagent/session-controller.js +355 -98
  98. package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
  99. package/dist/agent-core/tools/code-mode/cells.js +5 -15
  100. package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
  101. package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
  102. package/dist/agent-core/tools/code-mode/index.js +1 -2
  103. package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
  104. package/dist/agent-core/tools/code-mode/observations.js +16 -5
  105. package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
  106. package/dist/agent-core/tools/execution.d.ts +11 -2
  107. package/dist/agent-core/tools/execution.js +58 -21
  108. package/dist/agent-core/tools/input.d.ts +2 -0
  109. package/dist/agent-core/tools/input.js +36 -0
  110. package/dist/agent-core/tools/local/apply-patch.js +21 -17
  111. package/dist/agent-core/tools/local/exec.d.ts +4 -4
  112. package/dist/agent-core/tools/local/exec.js +234 -62
  113. package/dist/agent-core/tools/local/registry.d.ts +6 -0
  114. package/dist/agent-core/tools/local/registry.js +60 -9
  115. package/dist/agent-core/tools/local/view-image.js +108 -2
  116. package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
  117. package/dist/agent-core/tools/mcp/catalog.js +4 -4
  118. package/dist/agent-core/tools/mcp/history.js +2 -2
  119. package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
  120. package/dist/agent-core/tools/mcp/tool-search.js +2 -2
  121. package/dist/agent-core/tools/output-projection.d.ts +3 -1
  122. package/dist/agent-core/tools/output-projection.js +11 -2
  123. package/dist/agent-core/tools/runtime-context.d.ts +1 -0
  124. package/dist/agent-core/tools/runtime.d.ts +3 -1
  125. package/dist/agent-core/tools/runtime.js +22 -7
  126. package/dist/agent-core/tools/scheduler.d.ts +2 -0
  127. package/dist/agent-core/tools/scheduler.js +43 -6
  128. package/dist/index.d.ts +21 -0
  129. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
  130. package/package.json +1 -1
@@ -7,7 +7,9 @@ import WebSocket from 'ws';
7
7
  import { isRetryableUpstreamOverloadCode, number, parseSse, record, text, observeModelStreamEvent, modelStreamEventObservedAt } from './sse.js';
8
8
  import { connectSocks5Target, fetchModelResponse, isRetryableModelHttpStatus, isRetryableModelTransportError, modelRetryMaxAttempts, isRetryableModelRequestError, modelRetryDelay, waitForModelRetry } from './http.js';
9
9
  import { acquireModelCredential, appendModelCredentialHeaders } from './runtime-credential.js';
10
+ import { resolveModelTextBlocks } from './text-blocks.js';
10
11
  const WEBSOCKET_IDLE_TIMEOUT_MS = 300_000;
12
+ const WEBSOCKET_SEND_FRAME_BYTES = 1024 * 1024;
11
13
  export const OPENAI_RESPONSES_EARLY_EOF_REASON = 'OPENAI_RESPONSES_EARLY_EOF';
12
14
  export const OPENAI_RESPONSES_WEBSOCKET_CONNECTION_LIMIT_REASON = 'OPENAI_RESPONSES_WEBSOCKET_CONNECTION_LIMIT';
13
15
  const X_CODEX_TURN_STATE = 'x-codex-turn-state';
@@ -624,6 +626,7 @@ class OpenAiResponsesTurnSession {
624
626
  #fullBody;
625
627
  #completedResponseId;
626
628
  #connectionDiagnostic;
629
+ #webSocketDiagnostic;
627
630
  constructor(adapter, state, cacheLease) {
628
631
  this.adapter = adapter;
629
632
  this.state = state;
@@ -735,9 +738,13 @@ class OpenAiResponsesTurnSession {
735
738
  }
736
739
  beginAttempt() {
737
740
  this.#connectionDiagnostic = undefined;
741
+ this.#webSocketDiagnostic = undefined;
742
+ }
743
+ captureWebSocketDiagnostic(diagnostic) {
744
+ this.#webSocketDiagnostic = diagnostic;
738
745
  }
739
746
  connectionDiagnostic() {
740
- return this.#connectionDiagnostic ?? {};
747
+ return { ...this.#connectionDiagnostic, ...this.#webSocketDiagnostic };
741
748
  }
742
749
  captureTurnState(value) {
743
750
  if (this.#turnState !== undefined || typeof value !== 'string')
@@ -855,6 +862,11 @@ function isLocalContinuationReset(error) {
855
862
  return error instanceof MarAgentError && error.code === 'MAR_AGENT_RESPONSES_CONTINUATION_RESET';
856
863
  }
857
864
  function responsesRequestBody(configuration, request, messages) {
865
+ if ((request.systemBlocks !== undefined &&
866
+ resolveModelTextBlocks(request.system, request.systemBlocks).some((block) => block.cache)) ||
867
+ messages.some((message) => message.textBlocks !== undefined &&
868
+ resolveModelTextBlocks(message.content, message.textBlocks).some((block) => block.cache)))
869
+ throw new MarAgentError('MAR_AGENT_MODEL_CAPABILITY_UNAVAILABLE', 'This model protocol does not support explicit text-block cache markers.');
858
870
  const tools = request.allowTools === false
859
871
  ? []
860
872
  : request.tools.map((tool) => tool.nativeType === 'tool_search'
@@ -881,14 +893,27 @@ function responsesRequestBody(configuration, request, messages) {
881
893
  tools.push({ type: 'web_search' });
882
894
  const toolChoice = request.toolChoice ?? 'auto';
883
895
  const input = openAiInput(messages, request.images, configuration.id, true, usesResponsesConfigurationUpdates(configuration));
896
+ if (request.systemBlocks !== undefined && (request.systemBlocks.length || request.system.length))
897
+ input.unshift({
898
+ role: 'developer',
899
+ content: resolveModelTextBlocks(request.system, request.systemBlocks).map((block) => ({
900
+ type: 'input_text',
901
+ text: block.text
902
+ }))
903
+ });
884
904
  return {
885
905
  model: configuration.modelId,
906
+ ...(request.outputLimit === undefined
907
+ ? {}
908
+ : {
909
+ max_output_tokens: Math.min(request.outputLimit, configuration.maxOutputTokens)
910
+ }),
886
911
  ...(configuration.serviceTier !== undefined
887
912
  ? { service_tier: configuration.serviceTier }
888
913
  : configuration.fastMode === true
889
914
  ? { service_tier: 'priority' }
890
915
  : {}),
891
- instructions: request.system,
916
+ instructions: request.systemBlocks === undefined ? request.system : '',
892
917
  tools,
893
918
  tool_choice: toolChoice,
894
919
  input,
@@ -933,6 +958,7 @@ function responsesContinuationPropertiesMatch(previous, current) {
933
958
  'instructions',
934
959
  'tools',
935
960
  'tool_choice',
961
+ 'max_output_tokens',
936
962
  'parallel_tool_calls',
937
963
  'reasoning',
938
964
  'store',
@@ -1010,6 +1036,10 @@ async function* parseOpenAiResponsesSse(response, signal, continuation) {
1010
1036
  }
1011
1037
  }
1012
1038
  async function* webSocketResponseEvents(input) {
1039
+ const body = input.turn.withTurnStateMetadata(input.body);
1040
+ const payload = Buffer.from(JSON.stringify({ type: 'response.create', ...body }), 'utf8');
1041
+ const diagnostic = webSocketRequestDiagnostic(body, payload.length);
1042
+ input.turn.captureWebSocketDiagnostic(diagnostic);
1013
1043
  const socket = await input.turn.webSocketForRequest({
1014
1044
  configuration: input.configuration,
1015
1045
  promptCacheKey: input.promptCacheKey,
@@ -1018,32 +1048,75 @@ async function* webSocketResponseEvents(input) {
1018
1048
  continuation: input.continuation,
1019
1049
  credentialAcquired: input.credentialAcquired
1020
1050
  });
1021
- const body = input.turn.withTurnStateMetadata(input.body);
1022
- await sendWebSocketMessage(socket, JSON.stringify({ type: 'response.create', ...body }), input.signal);
1023
- let terminal = false;
1024
- for await (const item of webSocketMessages(socket, input.signal)) {
1025
- const event = record(item);
1026
- const type = text(event.type);
1027
- input.turn.captureTurnState(turnStateFromEvent(event));
1028
- if (type !== 'response.metadata' && type !== 'codex.response.metadata')
1029
- yield item;
1030
- if (type === 'response.completed' ||
1031
- type === 'response.incomplete' ||
1032
- type === 'response.failed' ||
1033
- type === 'error') {
1034
- terminal = true;
1035
- return;
1051
+ const messages = webSocketMessages(socket, input.signal, (code) => {
1052
+ diagnostic.webSocketCloseCode = code;
1053
+ });
1054
+ try {
1055
+ diagnostic.webSocketPhase = 'SEND';
1056
+ await sendWebSocketMessage(socket, payload, input.signal, () => {
1057
+ diagnostic.sentFrameCount = (diagnostic.sentFrameCount ?? 0) + 1;
1058
+ });
1059
+ diagnostic.webSocketPhase = 'RECEIVE';
1060
+ let terminal = false;
1061
+ for await (const item of messages) {
1062
+ const event = record(item);
1063
+ const type = text(event.type);
1064
+ input.turn.captureTurnState(turnStateFromEvent(event));
1065
+ if (type !== 'response.metadata' && type !== 'codex.response.metadata')
1066
+ yield item;
1067
+ if (type === 'response.completed' ||
1068
+ type === 'response.incomplete' ||
1069
+ type === 'response.failed' ||
1070
+ type === 'error') {
1071
+ terminal = true;
1072
+ return;
1073
+ }
1036
1074
  }
1075
+ if (!terminal && !input.signal.aborted)
1076
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'OpenAI Responses WebSocket closed before a terminal event.', {
1077
+ details: {
1078
+ reason: OPENAI_RESPONSES_EARLY_EOF_REASON,
1079
+ transport: 'WEBSOCKET',
1080
+ continuation: input.continuation
1081
+ },
1082
+ retryable: true
1083
+ });
1084
+ }
1085
+ finally {
1086
+ messages.dispose();
1037
1087
  }
1038
- if (!terminal && !input.signal.aborted)
1039
- throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'OpenAI Responses WebSocket closed before a terminal event.', {
1040
- details: {
1041
- reason: OPENAI_RESPONSES_EARLY_EOF_REASON,
1042
- transport: 'WEBSOCKET',
1043
- continuation: input.continuation
1044
- },
1045
- retryable: true
1046
- });
1088
+ }
1089
+ function webSocketRequestDiagnostic(body, requestBytes) {
1090
+ let imageCount = 0;
1091
+ let imageBytes = 0;
1092
+ for (const item of body.input) {
1093
+ const entry = record(item);
1094
+ const parts = Array.isArray(entry.content)
1095
+ ? entry.content
1096
+ : Array.isArray(entry.output)
1097
+ ? entry.output
1098
+ : [];
1099
+ for (const value of parts) {
1100
+ const part = record(value);
1101
+ if (part.type !== 'input_image')
1102
+ continue;
1103
+ imageCount++;
1104
+ const url = text(part.image_url);
1105
+ if (url === undefined)
1106
+ continue;
1107
+ const marker = url.indexOf(';base64,');
1108
+ if (url.startsWith('data:') && marker >= 0)
1109
+ imageBytes += Buffer.byteLength(url.slice(marker + 8), 'base64');
1110
+ }
1111
+ }
1112
+ return {
1113
+ requestBytes,
1114
+ imageCount,
1115
+ imageBytes,
1116
+ requestFrameCount: Math.ceil(requestBytes / WEBSOCKET_SEND_FRAME_BYTES),
1117
+ sentFrameCount: 0,
1118
+ webSocketPhase: 'CONNECT'
1119
+ };
1047
1120
  }
1048
1121
  async function waitForWebSocketOpen(socket, signal) {
1049
1122
  if (signal.aborted) {
@@ -1101,29 +1174,55 @@ async function waitForWebSocketOpen(socket, signal) {
1101
1174
  socket.once('unexpected-response', unexpectedResponse);
1102
1175
  });
1103
1176
  }
1104
- async function sendWebSocketMessage(socket, value, signal) {
1105
- if (signal.aborted)
1106
- throw signal.reason;
1107
- await new Promise((resolve, reject) => {
1108
- const cleanup = () => signal.removeEventListener('abort', abort);
1109
- const abort = () => {
1110
- socket.terminate();
1111
- cleanup();
1112
- reject(signal.reason);
1113
- };
1114
- signal.addEventListener('abort', abort, { once: true });
1115
- socket.send(value, (error) => {
1116
- cleanup();
1117
- if (error)
1118
- reject(webSocketReceiveError(error));
1119
- else if (signal.aborted)
1177
+ async function sendWebSocketMessage(socket, payload, signal, frameSent) {
1178
+ for (let offset = 0; offset < payload.length; offset += WEBSOCKET_SEND_FRAME_BYTES) {
1179
+ if (signal.aborted)
1180
+ throw signal.reason;
1181
+ const end = Math.min(payload.length, offset + WEBSOCKET_SEND_FRAME_BYTES);
1182
+ await new Promise((resolve, reject) => {
1183
+ let settled = false;
1184
+ const cleanup = () => {
1185
+ settled = true;
1186
+ signal.removeEventListener('abort', abort);
1187
+ socket.off('error', error);
1188
+ socket.off('close', close);
1189
+ };
1190
+ const abort = () => {
1191
+ cleanup();
1192
+ socket.terminate();
1120
1193
  reject(signal.reason);
1121
- else
1122
- resolve();
1194
+ };
1195
+ const error = (cause) => {
1196
+ cleanup();
1197
+ reject(webSocketReceiveError(cause));
1198
+ };
1199
+ const close = () => error(new Error('WebSocket closed while sending the request.'));
1200
+ signal.addEventListener('abort', abort, { once: true });
1201
+ socket.once('error', error);
1202
+ socket.once('close', close);
1203
+ try {
1204
+ socket.send(payload.subarray(offset, end), { binary: false, fin: end === payload.length }, (cause) => {
1205
+ if (settled)
1206
+ return;
1207
+ cleanup();
1208
+ if (cause)
1209
+ reject(webSocketReceiveError(cause));
1210
+ else if (signal.aborted)
1211
+ reject(signal.reason);
1212
+ else {
1213
+ frameSent();
1214
+ resolve();
1215
+ }
1216
+ });
1217
+ }
1218
+ catch (cause) {
1219
+ cleanup();
1220
+ reject(webSocketReceiveError(cause));
1221
+ }
1123
1222
  });
1124
- });
1223
+ }
1125
1224
  }
1126
- async function* webSocketMessages(socket, signal) {
1225
+ function webSocketMessages(socket, signal, closed) {
1127
1226
  const queue = [];
1128
1227
  let resume;
1129
1228
  const push = (item) => {
@@ -1136,7 +1235,10 @@ async function* webSocketMessages(socket, signal) {
1136
1235
  value,
1137
1236
  observedAt: performance.now()
1138
1237
  });
1139
- const close = () => push({ type: 'closed' });
1238
+ const close = (code) => {
1239
+ closed(code);
1240
+ push({ type: 'closed' });
1241
+ };
1140
1242
  const error = (cause) => push({
1141
1243
  type: 'error',
1142
1244
  error: webSocketReceiveError(cause)
@@ -1153,49 +1255,57 @@ async function* webSocketMessages(socket, signal) {
1153
1255
  socket.on('close', close);
1154
1256
  socket.on('error', error);
1155
1257
  signal.addEventListener('abort', abort, { once: true });
1156
- try {
1157
- let idleDeadline = Date.now() + WEBSOCKET_IDLE_TIMEOUT_MS;
1158
- while (true) {
1159
- if (Date.now() >= idleDeadline)
1160
- throw webSocketIdleError(socket);
1161
- if (queue.length === 0)
1162
- await new Promise((resolve, reject) => {
1163
- const timer = setTimeout(() => {
1164
- resume = undefined;
1165
- reject(webSocketIdleError(socket));
1166
- }, Math.max(0, idleDeadline - Date.now()));
1167
- resume = () => {
1168
- clearTimeout(timer);
1169
- resolve();
1170
- };
1171
- });
1172
- const item = queue.shift();
1173
- if (item === undefined)
1174
- continue;
1175
- if (item.type === 'closed')
1176
- return;
1177
- if (item.type === 'error')
1178
- throw item.error;
1179
- const text = await webSocketMessageText(item.value);
1180
- if (Buffer.byteLength(text) > 16 * 1024 * 1024)
1181
- throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Model response exceeded the size limit.');
1182
- try {
1183
- const event = JSON.parse(text);
1184
- if (!isWebSocketNonProgressEvent(event))
1185
- idleDeadline = Date.now() + WEBSOCKET_IDLE_TIMEOUT_MS;
1186
- yield observeModelStreamEvent(event, item.observedAt);
1187
- }
1188
- catch (cause) {
1189
- throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Model WebSocket contained invalid JSON.', { cause });
1190
- }
1191
- }
1192
- }
1193
- finally {
1258
+ const dispose = () => {
1194
1259
  signal.removeEventListener('abort', abort);
1195
1260
  socket.off('message', message);
1196
1261
  socket.off('close', close);
1197
1262
  socket.off('error', error);
1198
- }
1263
+ };
1264
+ return {
1265
+ dispose,
1266
+ async *[Symbol.asyncIterator]() {
1267
+ try {
1268
+ let idleDeadline = Date.now() + WEBSOCKET_IDLE_TIMEOUT_MS;
1269
+ while (true) {
1270
+ if (Date.now() >= idleDeadline)
1271
+ throw webSocketIdleError(socket);
1272
+ if (queue.length === 0)
1273
+ await new Promise((resolve, reject) => {
1274
+ const timer = setTimeout(() => {
1275
+ resume = undefined;
1276
+ reject(webSocketIdleError(socket));
1277
+ }, Math.max(0, idleDeadline - Date.now()));
1278
+ resume = () => {
1279
+ clearTimeout(timer);
1280
+ resolve();
1281
+ };
1282
+ });
1283
+ const item = queue.shift();
1284
+ if (item === undefined)
1285
+ continue;
1286
+ if (item.type === 'closed')
1287
+ return;
1288
+ if (item.type === 'error')
1289
+ throw item.error;
1290
+ const text = await webSocketMessageText(item.value);
1291
+ if (Buffer.byteLength(text) > 16 * 1024 * 1024)
1292
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Model response exceeded the size limit.');
1293
+ try {
1294
+ const event = JSON.parse(text);
1295
+ if (!isWebSocketNonProgressEvent(event))
1296
+ idleDeadline = Date.now() + WEBSOCKET_IDLE_TIMEOUT_MS;
1297
+ yield observeModelStreamEvent(event, item.observedAt);
1298
+ }
1299
+ catch (cause) {
1300
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Model WebSocket contained invalid JSON.', { cause });
1301
+ }
1302
+ }
1303
+ }
1304
+ finally {
1305
+ dispose();
1306
+ }
1307
+ }
1308
+ };
1199
1309
  }
1200
1310
  function webSocketIdleError(socket) {
1201
1311
  terminateWebSocket(socket);
@@ -1597,8 +1707,8 @@ function strictFunctionSchema(value, optional = false) {
1597
1707
  return value;
1598
1708
  const schema = value;
1599
1709
  const supportedSchema = Object.fromEntries(Object.entries(schema).filter(([keyword]) => keyword !== 'propertyNames'));
1600
- if (schema.type === 'object' && schema.properties && typeof schema.properties === 'object') {
1601
- const properties = schema.properties;
1710
+ if (schema.type === 'object') {
1711
+ const properties = record(schema.properties);
1602
1712
  const required = new Set(Array.isArray(schema.required) ? schema.required : []);
1603
1713
  return {
1604
1714
  ...supportedSchema,
@@ -1771,40 +1881,81 @@ function openAiInputItem(message, images, customCallIds = new Set(), functionCal
1771
1881
  type: 'input_image',
1772
1882
  image_url: `data:${part.image.mimeType};base64,${part.image.dataBase64}`
1773
1883
  }) ??
1774
- (message.images?.length
1884
+ (message.textBlocks !== undefined
1775
1885
  ? [
1776
- { type: 'input_text', text: message.content },
1777
- ...message.images.map((image) => ({
1886
+ ...resolveModelTextBlocks(message.content, message.textBlocks).map((block) => ({
1887
+ type: 'input_text',
1888
+ text: block.text
1889
+ })),
1890
+ ...(message.images ?? []).map((image) => ({
1778
1891
  type: 'input_image',
1779
1892
  image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1780
1893
  }))
1781
1894
  ]
1782
- : message.content)
1895
+ : message.images?.length
1896
+ ? [
1897
+ { type: 'input_text', text: message.content },
1898
+ ...message.images.map((image) => ({
1899
+ type: 'input_image',
1900
+ image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1901
+ }))
1902
+ ]
1903
+ : message.content)
1783
1904
  };
1784
1905
  return {
1785
1906
  role: message.role,
1786
- content: userImages.length > 0
1907
+ content: message.textBlocks !== undefined
1787
1908
  ? [
1788
- { type: 'input_text', text: message.content },
1909
+ ...resolveModelTextBlocks(message.content, message.textBlocks).map((block) => ({
1910
+ type: message.role === 'assistant' ? 'output_text' : 'input_text',
1911
+ text: block.text
1912
+ })),
1789
1913
  ...userImages.map((image) => ({
1790
1914
  type: 'input_image',
1791
1915
  image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1792
1916
  }))
1793
1917
  ]
1794
- : message.content
1918
+ : userImages.length > 0
1919
+ ? [
1920
+ { type: 'input_text', text: message.content },
1921
+ ...userImages.map((image) => ({
1922
+ type: 'input_image',
1923
+ image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1924
+ }))
1925
+ ]
1926
+ : message.content
1795
1927
  };
1796
1928
  }
1797
1929
  function responsesFunctionTool(tool) {
1930
+ const strict = !hasOpenFunctionObject(tool.inputSchema);
1798
1931
  return tool.inputMode === 'freeform'
1799
1932
  ? { type: 'custom', name: tool.name, description: tool.description }
1800
1933
  : {
1801
1934
  type: 'function',
1802
1935
  name: tool.name,
1803
1936
  description: tool.description,
1804
- parameters: strictFunctionSchema(tool.inputSchema),
1805
- strict: true
1937
+ parameters: strict ? strictFunctionSchema(tool.inputSchema) : tool.inputSchema,
1938
+ strict
1806
1939
  };
1807
1940
  }
1941
+ function hasOpenFunctionObject(value) {
1942
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
1943
+ return false;
1944
+ const schema = record(value);
1945
+ if ((schema.type === 'object' || (Array.isArray(schema.type) && schema.type.includes('object'))) &&
1946
+ (schema.additionalProperties === true ||
1947
+ (schema.additionalProperties !== null && typeof schema.additionalProperties === 'object') ||
1948
+ (schema.properties === undefined && schema.additionalProperties !== false)))
1949
+ return true;
1950
+ const children = [
1951
+ ...Object.values(record(schema.properties)),
1952
+ ...Object.values(record(schema.$defs)),
1953
+ ...Object.values(record(schema.definitions)),
1954
+ schema.items,
1955
+ ...[schema.anyOf, schema.oneOf, schema.allOf].flatMap((items) => Array.isArray(items) ? items : [])
1956
+ ];
1957
+ return children.some(hasOpenFunctionObject);
1958
+ }
1808
1959
  function uniqueModelImages(...groups) {
1809
1960
  const seen = new Set();
1810
1961
  return groups.flatMap((group) => (group ?? []).flatMap((image) => {
@@ -0,0 +1,7 @@
1
+ import type { ModelTextBlock } from './contracts.js';
2
+ /** Preserve explicit blocks only while their complete texts still match the actual prefix. */
3
+ export declare function resolveModelTextBlocks(text: string, blocks: readonly ModelTextBlock[]): readonly ModelTextBlock[];
4
+ export declare function modelTextInput(value: string | readonly ModelTextBlock[]): {
5
+ text: string;
6
+ blocks?: readonly ModelTextBlock[];
7
+ };
@@ -0,0 +1,40 @@
1
+ import { MarAgentError } from '../error.js';
2
+ /** Preserve explicit blocks only while their complete texts still match the actual prefix. */
3
+ export function resolveModelTextBlocks(text, blocks) {
4
+ if (typeof text !== 'string' || !Array.isArray(blocks))
5
+ throw invalid();
6
+ const copied = blocks.map((block) => {
7
+ if (!block ||
8
+ typeof block !== 'object' ||
9
+ typeof block.text !== 'string' ||
10
+ (block.cache !== undefined && block.cache !== true))
11
+ throw invalid();
12
+ return { text: block.text, ...(block.cache === true ? { cache: true } : {}) };
13
+ });
14
+ let offset = 0;
15
+ const result = [];
16
+ for (const block of copied) {
17
+ if (!text.startsWith(block.text, offset))
18
+ break;
19
+ result.push(block);
20
+ offset += block.text.length;
21
+ }
22
+ if (offset < text.length)
23
+ result.push({ text: text.slice(offset) });
24
+ return result;
25
+ }
26
+ export function modelTextInput(value) {
27
+ if (typeof value === 'string')
28
+ return { text: value };
29
+ if (!Array.isArray(value))
30
+ throw invalid();
31
+ for (const block of value) {
32
+ if (!block || typeof block !== 'object' || typeof block.text !== 'string')
33
+ throw invalid();
34
+ }
35
+ const text = value.map((block) => block.text).join('');
36
+ return { text, blocks: resolveModelTextBlocks(text, value) };
37
+ }
38
+ function invalid() {
39
+ return new MarAgentError('MAR_AGENT_MODEL_TEXT_BLOCK_INVALID', 'Model text blocks are invalid.');
40
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ExecutionMode } from '../runtime/contracts.js';
2
+ import { type CorePromptPart } from '../extensions/prompt-composition.js';
2
3
  export declare const MAR_AGENT_PROMPT_VERSION = "1.60";
3
- export declare function buildSystemPrompt(input: {
4
+ interface SystemPromptInput {
4
5
  mode: ExecutionMode;
5
6
  platform: string;
6
7
  architecture?: string;
@@ -14,4 +15,7 @@ export declare function buildSystemPrompt(input: {
14
15
  directTools?: readonly string[];
15
16
  highDensityCompaction?: boolean;
16
17
  extension?: string | undefined;
17
- }): string;
18
+ }
19
+ export declare function buildSystemPrompt(input: SystemPromptInput): string;
20
+ export declare function buildSystemPromptSections(input: SystemPromptInput): readonly CorePromptPart[];
21
+ export {};
@@ -1,3 +1,4 @@
1
+ import { renderPromptSections } from '../extensions/prompt-composition.js';
1
2
  import { compactionPrompt, highDensityCompactionPrompt } from './compact.js';
2
3
  import { environmentPrompt, identityPrompt, instructionPriorityPrompt, workspaceDisciplinePrompt } from './core.js';
3
4
  import { modePrompt } from './modes.js';
@@ -6,6 +7,9 @@ import { subagentPrompt } from './subagent.js';
6
7
  import { interactionPrompt, longRunningPrompt, safetyPrompt, toolUsagePrompt, workflowPrompt } from './workflow.js';
7
8
  export const MAR_AGENT_PROMPT_VERSION = '1.60';
8
9
  export function buildSystemPrompt(input) {
10
+ return renderPromptSections(buildSystemPromptSections(input));
11
+ }
12
+ export function buildSystemPromptSections(input) {
9
13
  const toolNames = new Set(input.tools);
10
14
  const hasLongRunningCapability = [
11
15
  'exec',
@@ -15,28 +19,38 @@ export function buildSystemPrompt(input) {
15
19
  'agent_wait',
16
20
  'agent_cancel'
17
21
  ].some((name) => toolNames.has(name));
18
- return [
19
- identityPrompt(),
20
- instructionPriorityPrompt,
21
- workspaceDisciplinePrompt,
22
- workflowPrompt,
23
- interactionPrompt,
24
- safetyPrompt,
25
- outputStylePrompt,
26
- environmentPrompt(input),
27
- input.tools.length > 0 ? toolUsagePrompt(input.tools, input.directTools) : '',
28
- hasLongRunningCapability ? longRunningPrompt(input.tools) : '',
29
- modePrompt(input.mode, {
30
- questionAvailable: toolNames.has('question')
31
- }),
32
- input.mode === 'compact'
33
- ? input.highDensityCompaction
34
- ? highDensityCompactionPrompt()
35
- : compactionPrompt()
36
- : '',
37
- toolNames.has('agent_start') ? subagentPrompt : '',
38
- input.extension ? `# Caller, project, and selected Skill instructions\n${input.extension}` : ''
39
- ]
40
- .filter(Boolean)
41
- .join('\n\n');
22
+ const values = [
23
+ ['identity', identityPrompt()],
24
+ ['instruction-priority', instructionPriorityPrompt],
25
+ ['workspace-discipline', workspaceDisciplinePrompt],
26
+ ['workflow', workflowPrompt],
27
+ ['interaction', interactionPrompt],
28
+ ['safety', safetyPrompt],
29
+ ['output-style', outputStylePrompt],
30
+ ['environment-rules', environmentPrompt(input)],
31
+ ['tools', input.tools.length > 0 ? toolUsagePrompt(input.tools, input.directTools) : ''],
32
+ ['long-running', hasLongRunningCapability ? longRunningPrompt(input.tools) : ''],
33
+ [
34
+ 'mode',
35
+ modePrompt(input.mode, {
36
+ questionAvailable: toolNames.has('question')
37
+ })
38
+ ],
39
+ [
40
+ 'compaction',
41
+ input.mode === 'compact'
42
+ ? input.highDensityCompaction
43
+ ? highDensityCompactionPrompt()
44
+ : compactionPrompt()
45
+ : ''
46
+ ],
47
+ ['subagent', toolNames.has('agent_start') ? subagentPrompt : ''],
48
+ [
49
+ 'caller-instructions',
50
+ input.extension
51
+ ? `# Caller, project, and selected Skill instructions\n${input.extension}`
52
+ : ''
53
+ ]
54
+ ];
55
+ return Object.freeze(values.map(([id, text]) => Object.freeze({ id: `core:${id}`, text: text || null })));
42
56
  }
@@ -1,5 +1,6 @@
1
1
  import { openAiInput } from '../../model/openai-responses.js';
2
2
  import { countModelTokens } from '../token-budget.js';
3
+ import { observationModelText } from '../../tools/code-mode/observations.js';
3
4
  export { MODEL_TOKEN_ESTIMATOR as CONTEXT_GC_TOKEN_ESTIMATOR } from '../token-budget.js';
4
5
  /** Uses Responses lowering, including native alias and cross-model filtering. */
5
6
  export function contextGcTokenEntries(input, messages) {
@@ -17,7 +18,11 @@ function serializedEntries(input, messages) {
17
18
  : messages
18
19
  .filter((message) => message.role !== 'provider' ||
19
20
  (message.provider === 'ANTHROPIC_MESSAGES' && message.providerModelId === input.modelId))
20
- .map(({ observations: _observations, observationId: _observationId, ...message }) => message);
21
+ .map((original) => {
22
+ const { observations: _observations, observationId: _observationId, ...message } = original;
23
+ const content = original.role === 'tool' ? observationModelText(original) : undefined;
24
+ return content === undefined ? message : { ...message, content };
25
+ });
21
26
  return values.map((value, index) => {
22
27
  const item = value;
23
28
  const callId = item.call_id ?? item.callId;