@janole/ai-sdk-provider-codex-asp 0.3.2 → 0.3.4

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.
package/dist/index.cjs CHANGED
@@ -958,7 +958,7 @@ var DynamicToolsDispatcher = class {
958
958
  // package.json
959
959
  var package_default = {
960
960
  name: "@janole/ai-sdk-provider-codex-asp",
961
- version: "0.3.2"};
961
+ version: "0.3.4"};
962
962
 
963
963
  // src/package-info.ts
964
964
  var PACKAGE_NAME = package_default.name;
@@ -1003,6 +1003,8 @@ function toFinishReason(status) {
1003
1003
  return { unified: "other", raw: void 0 };
1004
1004
  }
1005
1005
  }
1006
+ var NOOP = () => [];
1007
+ var DEFAULT_MAX_TOOL_RESULT_OUTPUT_CHARS = 32768;
1006
1008
  var CodexEventMapper = class {
1007
1009
  options;
1008
1010
  streamStarted = false;
@@ -1014,9 +1016,45 @@ var CodexEventMapper = class {
1014
1016
  threadId;
1015
1017
  turnId;
1016
1018
  latestUsage;
1019
+ handlers;
1017
1020
  constructor(options) {
1018
1021
  this.options = {
1019
- emitPlanUpdates: options?.emitPlanUpdates ?? true
1022
+ emitPlanUpdates: options?.emitPlanUpdates ?? true,
1023
+ maxToolResultOutputChars: options?.maxToolResultOutputChars ?? DEFAULT_MAX_TOOL_RESULT_OUTPUT_CHARS
1024
+ };
1025
+ this.handlers = {
1026
+ "turn/started": (p) => this.handleTurnStarted(p),
1027
+ "item/started": (p) => this.handleItemStarted(p),
1028
+ "item/agentMessage/delta": (p) => this.handleAgentMessageDelta(p),
1029
+ "item/completed": (p) => this.handleItemCompleted(p),
1030
+ "item/reasoning/textDelta": (p) => this.handleReasoningDelta(p),
1031
+ "item/reasoning/summaryTextDelta": (p) => this.handleReasoningDelta(p),
1032
+ "item/plan/delta": (p) => this.handleReasoningDelta(p),
1033
+ "item/fileChange/outputDelta": (p) => this.handleReasoningDelta(p),
1034
+ "item/reasoning/summaryPartAdded": (p) => this.handleSummaryPartAdded(p),
1035
+ "turn/plan/updated": (p) => this.handlePlanUpdated(p),
1036
+ "item/commandExecution/outputDelta": (p) => this.handleCommandOutputDelta(p),
1037
+ "codex/event/mcp_tool_call_begin": (p) => this.handleMcpToolCallBegin(p),
1038
+ "codex/event/mcp_tool_call_end": (p) => this.handleMcpToolCallEnd(p),
1039
+ "item/mcpToolCall/progress": (p) => this.handleMcpToolCallProgress(p),
1040
+ "item/tool/callStarted": (p) => this.handleToolCallStarted(p),
1041
+ "item/tool/callDelta": (p) => this.handleToolCallDelta(p),
1042
+ "item/tool/callFinished": (p) => this.handleToolCallFinished(p),
1043
+ "item/tool/call": (p) => this.handleToolCall(p),
1044
+ "thread/tokenUsage/updated": (p) => this.handleTokenUsageUpdated(p),
1045
+ "turn/completed": (p) => this.handleTurnCompleted(p),
1046
+ // Intentionally ignored: wrapper/duplicate events handled by their canonical forms above.
1047
+ "codex/event/agent_reasoning": NOOP,
1048
+ "codex/event/agent_reasoning_section_break": NOOP,
1049
+ "codex/event/plan_update": NOOP,
1050
+ // Intentionally ignored: web search wrappers mirror item events.
1051
+ // We emit web-search reasoning only from item/started + item/completed.
1052
+ "codex/event/web_search_begin": NOOP,
1053
+ "codex/event/web_search_end": NOOP,
1054
+ // Intentionally ignored: full diffs (often 50-100 KB) crash/freeze frontend renderers.
1055
+ // If these need to surface, they should use a dedicated part type with lazy rendering.
1056
+ "turn/diff/updated": NOOP,
1057
+ "codex/event/turn_diff": NOOP
1020
1058
  };
1021
1059
  }
1022
1060
  setThreadId(threadId) {
@@ -1028,331 +1066,478 @@ var CodexEventMapper = class {
1028
1066
  getTurnId() {
1029
1067
  return this.turnId;
1030
1068
  }
1069
+ map(event) {
1070
+ const handler = this.handlers[event.method];
1071
+ return handler ? handler(event.params) : [];
1072
+ }
1073
+ // ── Helpers ──────────────────────────────────────────────────────────────
1074
+ withMeta(part) {
1075
+ return withProviderMetadata(part, this.threadId, this.turnId);
1076
+ }
1077
+ ensureStreamStarted(parts) {
1078
+ if (!this.streamStarted) {
1079
+ parts.push({ type: "stream-start", warnings: [] });
1080
+ this.streamStarted = true;
1081
+ }
1082
+ }
1083
+ emitReasoningDelta(parts, id, delta) {
1084
+ this.ensureStreamStarted(parts);
1085
+ if (!this.openReasoningParts.has(id)) {
1086
+ this.openReasoningParts.add(id);
1087
+ parts.push(this.withMeta({ type: "reasoning-start", id }));
1088
+ }
1089
+ if (delta) {
1090
+ parts.push(this.withMeta({ type: "reasoning-delta", id, delta }));
1091
+ }
1092
+ }
1031
1093
  nextPlanSequence(turnId) {
1032
1094
  const next = (this.planSequenceByTurnId.get(turnId) ?? 0) + 1;
1033
1095
  this.planSequenceByTurnId.set(turnId, next);
1034
1096
  return next;
1035
1097
  }
1036
- map(event) {
1098
+ applyOutputLimit(output) {
1099
+ const limit = this.options.maxToolResultOutputChars;
1100
+ if (limit <= 0) {
1101
+ return { output, droppedChars: 0 };
1102
+ }
1103
+ if (output.length <= limit) {
1104
+ return { output, droppedChars: 0 };
1105
+ }
1106
+ const droppedChars = output.length - limit;
1107
+ return { output: output.slice(droppedChars), droppedChars };
1108
+ }
1109
+ appendTrackedOutput(tracked, delta) {
1110
+ if (!delta) {
1111
+ return;
1112
+ }
1113
+ const combined = tracked.output + delta;
1114
+ const limited = this.applyOutputLimit(combined);
1115
+ tracked.output = limited.output;
1116
+ tracked.droppedChars += limited.droppedChars;
1117
+ }
1118
+ formatToolOutput(output, droppedChars) {
1119
+ if (droppedChars <= 0) {
1120
+ return output;
1121
+ }
1122
+ return `[output truncated: ${droppedChars} chars omitted]
1123
+ ${output}`;
1124
+ }
1125
+ // ── Handlers ─────────────────────────────────────────────────────────────
1126
+ // turn/started
1127
+ handleTurnStarted(params) {
1128
+ const p = params;
1129
+ if (p?.turn?.id) {
1130
+ this.turnId = p.turn.id;
1131
+ }
1037
1132
  const parts = [];
1038
- const withMeta = (part) => withProviderMetadata(part, this.threadId, this.turnId);
1039
- const pushStreamStart = () => {
1040
- if (!this.streamStarted) {
1041
- parts.push({ type: "stream-start", warnings: [] });
1042
- this.streamStarted = true;
1043
- }
1044
- };
1045
- const pushReasoningDelta = (id, delta) => {
1046
- pushStreamStart();
1047
- if (!this.openReasoningParts.has(id)) {
1048
- this.openReasoningParts.add(id);
1049
- parts.push(withMeta({ type: "reasoning-start", id }));
1050
- }
1051
- if (!delta) {
1052
- return;
1053
- }
1054
- parts.push(withMeta({ type: "reasoning-delta", id, delta }));
1055
- };
1056
- switch (event.method) {
1057
- case "turn/started": {
1058
- const turnStartedParams = event.params;
1059
- if (turnStartedParams?.turn?.id) {
1060
- this.turnId = turnStartedParams.turn.id;
1061
- }
1062
- pushStreamStart();
1063
- break;
1064
- }
1065
- case "item/started": {
1066
- const params = event.params ?? {};
1067
- const item = params.item;
1068
- if (!item?.id) {
1069
- break;
1070
- }
1071
- switch (item.type) {
1072
- case "agentMessage": {
1073
- pushStreamStart();
1074
- this.openTextParts.add(item.id);
1075
- parts.push(withMeta({ type: "text-start", id: item.id }));
1076
- break;
1077
- }
1078
- case "commandExecution": {
1079
- pushStreamStart();
1080
- const toolName = "codex_command_execution";
1081
- this.openToolCalls.set(item.id, { toolName, output: "" });
1082
- parts.push(withMeta({
1083
- type: "tool-call",
1084
- toolCallId: item.id,
1085
- toolName,
1086
- input: JSON.stringify({ command: item.command, cwd: item.cwd }),
1087
- providerExecuted: true,
1088
- dynamic: true
1089
- }));
1090
- break;
1091
- }
1092
- case "reasoning":
1093
- case "plan":
1094
- case "fileChange":
1095
- case "mcpToolCall":
1096
- case "collabAgentToolCall":
1097
- case "webSearch":
1098
- case "imageView":
1099
- case "contextCompaction":
1100
- case "enteredReviewMode":
1101
- case "exitedReviewMode": {
1102
- pushReasoningDelta(item.id, "");
1103
- break;
1104
- }
1105
- }
1106
- break;
1107
- }
1108
- case "item/agentMessage/delta": {
1109
- const delta = event.params ?? {};
1110
- if (!delta.itemId || !delta.delta) {
1111
- break;
1112
- }
1113
- pushStreamStart();
1114
- if (!this.openTextParts.has(delta.itemId)) {
1115
- this.openTextParts.add(delta.itemId);
1116
- parts.push(withMeta({ type: "text-start", id: delta.itemId }));
1117
- }
1118
- parts.push(withMeta({ type: "text-delta", id: delta.itemId, delta: delta.delta }));
1119
- this.textDeltaReceived.add(delta.itemId);
1133
+ this.ensureStreamStarted(parts);
1134
+ return parts;
1135
+ }
1136
+ // item/started
1137
+ handleItemStarted(params) {
1138
+ const p = params ?? {};
1139
+ const item = p.item;
1140
+ if (!item?.id) {
1141
+ return [];
1142
+ }
1143
+ const parts = [];
1144
+ switch (item.type) {
1145
+ case "agentMessage": {
1146
+ this.ensureStreamStarted(parts);
1147
+ this.openTextParts.add(item.id);
1148
+ parts.push(this.withMeta({ type: "text-start", id: item.id }));
1120
1149
  break;
1121
1150
  }
1122
- case "item/completed": {
1123
- const params = event.params ?? {};
1124
- const item = params.item;
1125
- if (!item?.id) {
1126
- break;
1127
- }
1128
- if (item.type === "agentMessage") {
1129
- if (!this.textDeltaReceived.has(item.id) && item.text) {
1130
- pushStreamStart();
1131
- if (!this.openTextParts.has(item.id)) {
1132
- this.openTextParts.add(item.id);
1133
- parts.push(withMeta({ type: "text-start", id: item.id }));
1134
- }
1135
- parts.push(withMeta({ type: "text-delta", id: item.id, delta: item.text }));
1136
- }
1137
- if (this.openTextParts.has(item.id)) {
1138
- parts.push(withMeta({ type: "text-end", id: item.id }));
1139
- this.openTextParts.delete(item.id);
1140
- }
1141
- } else if (item.type === "commandExecution" && this.openToolCalls.has(item.id)) {
1142
- const tracked = this.openToolCalls.get(item.id);
1143
- const output = item.aggregatedOutput ?? tracked.output;
1144
- const exitCode = item.exitCode;
1145
- const status = item.status;
1146
- parts.push(withMeta({
1147
- type: "tool-result",
1148
- toolCallId: item.id,
1149
- toolName: tracked.toolName,
1150
- result: { output, exitCode, status }
1151
- }));
1152
- this.openToolCalls.delete(item.id);
1153
- } else if (this.openReasoningParts.has(item.id)) {
1154
- parts.push(withMeta({ type: "reasoning-end", id: item.id }));
1155
- this.openReasoningParts.delete(item.id);
1156
- }
1151
+ case "commandExecution": {
1152
+ this.ensureStreamStarted(parts);
1153
+ const toolName = "codex_command_execution";
1154
+ this.openToolCalls.set(item.id, { toolName, output: "", droppedChars: 0 });
1155
+ parts.push(this.withMeta({
1156
+ type: "tool-call",
1157
+ toolCallId: item.id,
1158
+ toolName,
1159
+ input: JSON.stringify({ command: item.command, cwd: item.cwd }),
1160
+ providerExecuted: true,
1161
+ dynamic: true
1162
+ }));
1157
1163
  break;
1158
1164
  }
1159
- case "item/reasoning/textDelta":
1160
- case "item/reasoning/summaryTextDelta":
1161
- case "item/plan/delta":
1162
- case "item/fileChange/outputDelta": {
1163
- const delta = event.params ?? {};
1164
- if (delta.itemId && delta.delta) {
1165
- pushReasoningDelta(delta.itemId, delta.delta);
1166
- }
1165
+ case "dynamicToolCall": {
1166
+ parts.push(...this.startDynamicToolCall(item));
1167
1167
  break;
1168
1168
  }
1169
- case "item/reasoning/summaryPartAdded": {
1170
- const params = event.params ?? {};
1171
- if (params.itemId) {
1172
- pushReasoningDelta(params.itemId, "\n\n");
1173
- }
1169
+ case "reasoning":
1170
+ case "plan":
1171
+ case "fileChange":
1172
+ case "mcpToolCall":
1173
+ case "collabAgentToolCall":
1174
+ case "webSearch":
1175
+ case "imageView":
1176
+ case "contextCompaction":
1177
+ case "enteredReviewMode":
1178
+ case "exitedReviewMode": {
1179
+ this.emitReasoningDelta(parts, item.id, "");
1174
1180
  break;
1175
1181
  }
1176
- // codex/event/agent_reasoning mirrors canonical reasoning summary
1177
- // stream events in current logs. Ignore wrapper to avoid duplicate
1178
- // reasoning text in consumers.
1179
- case "codex/event/agent_reasoning":
1180
- break;
1181
- // codex/event/agent_reasoning_section_break is the wrapper form of
1182
- // item/reasoning/summaryPartAdded (identical 1:1). Handled by the
1183
- // canonical event above — skip the wrapper to avoid double "\n\n".
1184
- case "codex/event/agent_reasoning_section_break":
1185
- break;
1186
- case "turn/plan/updated": {
1187
- if (!this.options.emitPlanUpdates) {
1188
- break;
1189
- }
1190
- const params = event.params ?? {};
1191
- const turnId = params.turnId;
1192
- const plan = params.plan;
1193
- if (turnId && plan) {
1194
- pushStreamStart();
1195
- const planSequence = this.nextPlanSequence(turnId);
1196
- const toolCallId = `plan:${turnId}:${planSequence}`;
1197
- const toolName = "codex_plan_update";
1198
- parts.push(withMeta({
1199
- type: "tool-call",
1200
- toolCallId,
1201
- toolName,
1202
- input: JSON.stringify({}),
1203
- providerExecuted: true,
1204
- dynamic: true
1205
- }));
1206
- parts.push(withMeta({
1207
- type: "tool-result",
1208
- toolCallId,
1209
- toolName,
1210
- result: { plan, explanation: params.explanation ?? void 0 }
1211
- }));
1182
+ }
1183
+ return parts;
1184
+ }
1185
+ // item/agentMessage/delta
1186
+ handleAgentMessageDelta(params) {
1187
+ const delta = params ?? {};
1188
+ if (!delta.itemId || !delta.delta) {
1189
+ return [];
1190
+ }
1191
+ const parts = [];
1192
+ this.ensureStreamStarted(parts);
1193
+ if (!this.openTextParts.has(delta.itemId)) {
1194
+ this.openTextParts.add(delta.itemId);
1195
+ parts.push(this.withMeta({ type: "text-start", id: delta.itemId }));
1196
+ }
1197
+ parts.push(this.withMeta({ type: "text-delta", id: delta.itemId, delta: delta.delta }));
1198
+ this.textDeltaReceived.add(delta.itemId);
1199
+ return parts;
1200
+ }
1201
+ // item/completed
1202
+ handleItemCompleted(params) {
1203
+ const p = params ?? {};
1204
+ const item = p.item;
1205
+ if (!item?.id) {
1206
+ return [];
1207
+ }
1208
+ const parts = [];
1209
+ if (item.type === "agentMessage") {
1210
+ if (!this.textDeltaReceived.has(item.id) && item.text) {
1211
+ this.ensureStreamStarted(parts);
1212
+ if (!this.openTextParts.has(item.id)) {
1213
+ this.openTextParts.add(item.id);
1214
+ parts.push(this.withMeta({ type: "text-start", id: item.id }));
1212
1215
  }
1213
- break;
1216
+ parts.push(this.withMeta({ type: "text-delta", id: item.id, delta: item.text }));
1214
1217
  }
1215
- // codex/event/plan_update is the wrapper form of turn/plan/updated (1:1).
1216
- case "codex/event/plan_update":
1217
- break;
1218
- // NOTE: turn/diff/updated and codex/event/turn_diff are intentionally
1219
- // NOT mapped. They carry full unified diffs (often 50-100 KB) which,
1220
- // when emitted as reasoning deltas, crash or freeze the frontend
1221
- // markdown renderer. If these need to surface in the UI, they should
1222
- // use a dedicated part type with lazy/collapsed rendering — not
1223
- // reasoning text.
1224
- case "turn/diff/updated":
1225
- case "codex/event/turn_diff":
1226
- break;
1227
- case "item/commandExecution/outputDelta": {
1228
- const delta = event.params ?? {};
1229
- if (delta.itemId && delta.delta && this.openToolCalls.has(delta.itemId)) {
1230
- const tracked = this.openToolCalls.get(delta.itemId);
1231
- tracked.output += delta.delta;
1232
- parts.push(withMeta({
1233
- type: "tool-result",
1234
- toolCallId: delta.itemId,
1235
- toolName: tracked.toolName,
1236
- result: { output: tracked.output },
1237
- preliminary: true
1238
- }));
1239
- }
1240
- break;
1218
+ if (this.openTextParts.has(item.id)) {
1219
+ parts.push(this.withMeta({ type: "text-end", id: item.id }));
1220
+ this.openTextParts.delete(item.id);
1241
1221
  }
1242
- case "codex/event/mcp_tool_call_begin": {
1243
- const params = event.params ?? {};
1244
- const callId = params.msg?.call_id;
1245
- const inv = params.msg?.invocation;
1246
- if (callId && inv) {
1247
- pushStreamStart();
1248
- const toolName = `mcp:${inv.server}/${inv.tool}`;
1249
- this.openToolCalls.set(callId, { toolName, output: "" });
1250
- parts.push(withMeta({
1251
- type: "tool-call",
1252
- toolCallId: callId,
1253
- toolName,
1254
- input: JSON.stringify(inv.arguments ?? {}),
1255
- providerExecuted: true,
1256
- dynamic: true
1257
- }));
1222
+ } else if (item.type === "commandExecution" && this.openToolCalls.has(item.id)) {
1223
+ const tracked = this.openToolCalls.get(item.id);
1224
+ const outputSource = item.aggregatedOutput ?? tracked.output;
1225
+ const limitedOutput = this.applyOutputLimit(outputSource);
1226
+ const output = this.formatToolOutput(
1227
+ limitedOutput.output,
1228
+ item.aggregatedOutput !== void 0 && item.aggregatedOutput !== null ? limitedOutput.droppedChars : tracked.droppedChars
1229
+ );
1230
+ const exitCode = item.exitCode;
1231
+ const status = item.status;
1232
+ parts.push(this.withMeta({
1233
+ type: "tool-result",
1234
+ toolCallId: item.id,
1235
+ toolName: tracked.toolName,
1236
+ result: { output, exitCode, status }
1237
+ }));
1238
+ this.openToolCalls.delete(item.id);
1239
+ } else if (item.type === "dynamicToolCall") {
1240
+ const dynamic = item;
1241
+ const tracked = this.openToolCalls.get(item.id);
1242
+ const toolName = tracked?.toolName ?? dynamic.tool ?? "dynamic_tool_call";
1243
+ const rawOutput = this.stringifyDynamicToolResult(dynamic);
1244
+ const limitedOutput = this.applyOutputLimit(rawOutput);
1245
+ parts.push(this.withMeta({
1246
+ type: "tool-result",
1247
+ toolCallId: item.id,
1248
+ toolName,
1249
+ result: {
1250
+ output: this.formatToolOutput(limitedOutput.output, limitedOutput.droppedChars),
1251
+ success: dynamic.success ?? void 0
1258
1252
  }
1259
- break;
1253
+ }));
1254
+ this.openToolCalls.delete(item.id);
1255
+ } else if (item.type === "webSearch") {
1256
+ const webSearchSummary = this.formatWebSearchItemSummary(item);
1257
+ if (webSearchSummary) {
1258
+ this.emitReasoningDelta(parts, item.id, webSearchSummary);
1260
1259
  }
1261
- case "codex/event/mcp_tool_call_end": {
1262
- const params = event.params ?? {};
1263
- const callId = params.msg?.call_id;
1264
- if (callId && this.openToolCalls.has(callId)) {
1265
- const tracked = this.openToolCalls.get(callId);
1266
- const result = params.msg?.result;
1267
- const textParts = result?.Ok?.content?.filter((c) => c.type === "text").map((c) => c.text) ?? [];
1268
- const output = textParts.join("\n") || (result?.Err ? JSON.stringify(result.Err) : "");
1269
- parts.push(withMeta({
1270
- type: "tool-result",
1271
- toolCallId: callId,
1272
- toolName: tracked.toolName,
1273
- result: { output }
1274
- }));
1275
- this.openToolCalls.delete(callId);
1276
- }
1277
- break;
1260
+ if (this.openReasoningParts.has(item.id)) {
1261
+ parts.push(this.withMeta({ type: "reasoning-end", id: item.id }));
1262
+ this.openReasoningParts.delete(item.id);
1278
1263
  }
1279
- case "item/mcpToolCall/progress": {
1280
- const params = event.params ?? {};
1281
- if (params.itemId && params.message) {
1282
- pushReasoningDelta(params.itemId, params.message);
1283
- }
1284
- break;
1264
+ } else if (this.openReasoningParts.has(item.id)) {
1265
+ parts.push(this.withMeta({ type: "reasoning-end", id: item.id }));
1266
+ this.openReasoningParts.delete(item.id);
1267
+ }
1268
+ return parts;
1269
+ }
1270
+ // item/reasoning/textDelta, item/reasoning/summaryTextDelta, item/plan/delta, item/fileChange/outputDelta
1271
+ handleReasoningDelta(params) {
1272
+ const delta = params ?? {};
1273
+ if (!delta.itemId || !delta.delta) {
1274
+ return [];
1275
+ }
1276
+ const parts = [];
1277
+ this.emitReasoningDelta(parts, delta.itemId, delta.delta);
1278
+ return parts;
1279
+ }
1280
+ // item/reasoning/summaryPartAdded
1281
+ handleSummaryPartAdded(params) {
1282
+ const p = params ?? {};
1283
+ if (!p.itemId) {
1284
+ return [];
1285
+ }
1286
+ const parts = [];
1287
+ this.emitReasoningDelta(parts, p.itemId, "\n\n");
1288
+ return parts;
1289
+ }
1290
+ // turn/plan/updated
1291
+ handlePlanUpdated(params) {
1292
+ if (!this.options.emitPlanUpdates) {
1293
+ return [];
1294
+ }
1295
+ const p = params ?? {};
1296
+ const turnId = p.turnId;
1297
+ const plan = p.plan;
1298
+ if (!turnId || !plan) {
1299
+ return [];
1300
+ }
1301
+ const parts = [];
1302
+ this.ensureStreamStarted(parts);
1303
+ const planSequence = this.nextPlanSequence(turnId);
1304
+ const toolCallId = `plan:${turnId}:${planSequence}`;
1305
+ const toolName = "codex_plan_update";
1306
+ parts.push(this.withMeta({
1307
+ type: "tool-call",
1308
+ toolCallId,
1309
+ toolName,
1310
+ input: JSON.stringify({}),
1311
+ providerExecuted: true,
1312
+ dynamic: true
1313
+ }));
1314
+ parts.push(this.withMeta({
1315
+ type: "tool-result",
1316
+ toolCallId,
1317
+ toolName,
1318
+ result: { plan, explanation: p.explanation ?? void 0 }
1319
+ }));
1320
+ return parts;
1321
+ }
1322
+ // item/commandExecution/outputDelta
1323
+ handleCommandOutputDelta(params) {
1324
+ const delta = params ?? {};
1325
+ if (!delta.itemId || !delta.delta || !this.openToolCalls.has(delta.itemId)) {
1326
+ return [];
1327
+ }
1328
+ const tracked = this.openToolCalls.get(delta.itemId);
1329
+ this.appendTrackedOutput(tracked, delta.delta);
1330
+ return [this.withMeta({
1331
+ type: "tool-result",
1332
+ toolCallId: delta.itemId,
1333
+ toolName: tracked.toolName,
1334
+ result: { output: this.formatToolOutput(tracked.output, tracked.droppedChars) },
1335
+ preliminary: true
1336
+ })];
1337
+ }
1338
+ // codex/event/mcp_tool_call_begin
1339
+ handleMcpToolCallBegin(params) {
1340
+ const p = params ?? {};
1341
+ const callId = p.msg?.call_id;
1342
+ const inv = p.msg?.invocation;
1343
+ if (!callId || !inv) {
1344
+ return [];
1345
+ }
1346
+ const parts = [];
1347
+ this.ensureStreamStarted(parts);
1348
+ const toolName = `mcp:${inv.server}/${inv.tool}`;
1349
+ this.openToolCalls.set(callId, { toolName, output: "", droppedChars: 0 });
1350
+ parts.push(this.withMeta({
1351
+ type: "tool-call",
1352
+ toolCallId: callId,
1353
+ toolName,
1354
+ input: JSON.stringify(inv.arguments ?? {}),
1355
+ providerExecuted: true,
1356
+ dynamic: true
1357
+ }));
1358
+ return parts;
1359
+ }
1360
+ // codex/event/mcp_tool_call_end
1361
+ handleMcpToolCallEnd(params) {
1362
+ const p = params ?? {};
1363
+ const callId = p.msg?.call_id;
1364
+ if (!callId || !this.openToolCalls.has(callId)) {
1365
+ return [];
1366
+ }
1367
+ const tracked = this.openToolCalls.get(callId);
1368
+ const result = p.msg?.result;
1369
+ const textParts = result?.Ok?.content?.filter((c) => c.type === "text").map((c) => c.text) ?? [];
1370
+ const rawOutput = textParts.join("\n") || (result?.Err ? JSON.stringify(result.Err) : "");
1371
+ const limitedOutput = this.applyOutputLimit(rawOutput);
1372
+ this.openToolCalls.delete(callId);
1373
+ return [this.withMeta({
1374
+ type: "tool-result",
1375
+ toolCallId: callId,
1376
+ toolName: tracked.toolName,
1377
+ result: { output: this.formatToolOutput(limitedOutput.output, limitedOutput.droppedChars) }
1378
+ })];
1379
+ }
1380
+ // item/mcpToolCall/progress
1381
+ handleMcpToolCallProgress(params) {
1382
+ const p = params ?? {};
1383
+ if (!p.itemId || !p.message) {
1384
+ return [];
1385
+ }
1386
+ const parts = [];
1387
+ this.emitReasoningDelta(parts, p.itemId, p.message);
1388
+ return parts;
1389
+ }
1390
+ // item/tool/callStarted
1391
+ handleToolCallStarted(params) {
1392
+ const p = params ?? {};
1393
+ if (!p.callId || !p.tool) {
1394
+ return [];
1395
+ }
1396
+ const parts = [];
1397
+ this.ensureStreamStarted(parts);
1398
+ parts.push(this.withMeta({ type: "tool-input-start", id: p.callId, toolName: p.tool, dynamic: true }));
1399
+ return parts;
1400
+ }
1401
+ // item/tool/callDelta
1402
+ handleToolCallDelta(params) {
1403
+ const p = params ?? {};
1404
+ if (!p.callId || !p.delta) {
1405
+ return [];
1406
+ }
1407
+ return [this.withMeta({ type: "tool-input-delta", id: p.callId, delta: p.delta })];
1408
+ }
1409
+ // item/tool/callFinished
1410
+ handleToolCallFinished(params) {
1411
+ const p = params ?? {};
1412
+ if (!p.callId) {
1413
+ return [];
1414
+ }
1415
+ return [this.withMeta({ type: "tool-input-end", id: p.callId })];
1416
+ }
1417
+ // item/tool/call
1418
+ handleToolCall(params) {
1419
+ const p = params ?? {};
1420
+ if (!p.callId || !p.tool) {
1421
+ return [];
1422
+ }
1423
+ return this.startDynamicToolCall({
1424
+ id: p.callId,
1425
+ tool: p.tool,
1426
+ arguments: p.arguments ?? {}
1427
+ });
1428
+ }
1429
+ startDynamicToolCall(item) {
1430
+ if (!item.id || !item.tool) {
1431
+ return [];
1432
+ }
1433
+ if (this.openToolCalls.has(item.id)) {
1434
+ return [];
1435
+ }
1436
+ const parts = [];
1437
+ this.ensureStreamStarted(parts);
1438
+ this.openToolCalls.set(item.id, { toolName: item.tool, output: "", droppedChars: 0 });
1439
+ parts.push(this.withMeta({
1440
+ type: "tool-call",
1441
+ toolCallId: item.id,
1442
+ toolName: item.tool,
1443
+ input: JSON.stringify(item.arguments ?? {}),
1444
+ providerExecuted: true,
1445
+ dynamic: true
1446
+ }));
1447
+ return parts;
1448
+ }
1449
+ stringifyDynamicToolResult(item) {
1450
+ const contentItems = item.contentItems ?? [];
1451
+ if (!contentItems.length) {
1452
+ return "";
1453
+ }
1454
+ const chunks = [];
1455
+ for (const contentItem of contentItems) {
1456
+ if (contentItem.type === "inputText" && contentItem.text) {
1457
+ chunks.push(contentItem.text);
1458
+ continue;
1285
1459
  }
1286
- case "item/tool/callStarted": {
1287
- const params = event.params ?? {};
1288
- if (params.callId && params.tool) {
1289
- pushStreamStart();
1290
- parts.push(withMeta({ type: "tool-input-start", id: params.callId, toolName: params.tool, dynamic: true }));
1291
- }
1292
- break;
1460
+ if (contentItem.type === "inputImage" && contentItem.imageUrl) {
1461
+ chunks.push(`[image] ${contentItem.imageUrl}`);
1293
1462
  }
1294
- case "item/tool/callDelta": {
1295
- const params = event.params ?? {};
1296
- if (params.callId && params.delta) {
1297
- parts.push(withMeta({ type: "tool-input-delta", id: params.callId, delta: params.delta }));
1298
- }
1299
- break;
1463
+ }
1464
+ return chunks.join("\n");
1465
+ }
1466
+ formatWebSearchItemSummary(item) {
1467
+ const query = item.query?.trim();
1468
+ const actionType = item.action?.type;
1469
+ if (actionType === "search") {
1470
+ if (query) {
1471
+ return `Web search: ${query}`;
1300
1472
  }
1301
- case "item/tool/callFinished": {
1302
- const params = event.params ?? {};
1303
- if (params.callId) {
1304
- parts.push(withMeta({ type: "tool-input-end", id: params.callId }));
1305
- }
1306
- break;
1473
+ const actionQuery = item.action?.query?.trim();
1474
+ return actionQuery ? `Web search: ${actionQuery}` : "Web search";
1475
+ }
1476
+ if (actionType === "openPage" || actionType === "open_page") {
1477
+ const url = item.action?.url?.trim();
1478
+ return url ? `Open page: ${url}` : "Open page";
1479
+ }
1480
+ if (actionType === "findInPage" || actionType === "find_in_page") {
1481
+ const pattern = item.action?.pattern?.trim();
1482
+ const url = item.action?.url?.trim();
1483
+ if (pattern && url) {
1484
+ return `Find in page: "${pattern}" (${url})`;
1307
1485
  }
1308
- case "thread/tokenUsage/updated": {
1309
- const params = event.params ?? {};
1310
- const last = params.tokenUsage?.last;
1311
- if (last) {
1312
- this.latestUsage = {
1313
- inputTokens: {
1314
- total: last.inputTokens,
1315
- noCache: void 0,
1316
- cacheRead: last.cachedInputTokens,
1317
- cacheWrite: void 0
1318
- },
1319
- outputTokens: {
1320
- total: last.outputTokens,
1321
- text: void 0,
1322
- reasoning: last.reasoningOutputTokens
1323
- }
1324
- };
1325
- }
1326
- break;
1486
+ if (pattern) {
1487
+ return `Find in page: "${pattern}"`;
1327
1488
  }
1328
- case "turn/completed": {
1329
- pushStreamStart();
1330
- for (const itemId of this.openTextParts) {
1331
- parts.push(withMeta({ type: "text-end", id: itemId }));
1332
- }
1333
- this.openTextParts.clear();
1334
- for (const itemId of this.openReasoningParts) {
1335
- parts.push(withMeta({ type: "reasoning-end", id: itemId }));
1336
- }
1337
- this.openReasoningParts.clear();
1338
- for (const [itemId, tracked] of this.openToolCalls) {
1339
- parts.push(withMeta({
1340
- type: "tool-result",
1341
- toolCallId: itemId,
1342
- toolName: tracked.toolName,
1343
- result: { output: tracked.output }
1344
- }));
1489
+ return url ? `Find in page: ${url}` : "Find in page";
1490
+ }
1491
+ return query ? `Web search: ${query}` : "";
1492
+ }
1493
+ // thread/tokenUsage/updated
1494
+ handleTokenUsageUpdated(params) {
1495
+ const p = params ?? {};
1496
+ const last = p.tokenUsage?.last;
1497
+ if (last) {
1498
+ this.latestUsage = {
1499
+ inputTokens: {
1500
+ total: last.inputTokens,
1501
+ noCache: void 0,
1502
+ cacheRead: last.cachedInputTokens,
1503
+ cacheWrite: void 0
1504
+ },
1505
+ outputTokens: {
1506
+ total: last.outputTokens,
1507
+ text: void 0,
1508
+ reasoning: last.reasoningOutputTokens
1345
1509
  }
1346
- this.openToolCalls.clear();
1347
- const completed = event.params ?? {};
1348
- if (completed.turn?.id) {
1349
- this.planSequenceByTurnId.delete(completed.turn.id);
1350
- }
1351
- const usage = this.latestUsage ?? EMPTY_USAGE;
1352
- parts.push(withMeta({ type: "finish", finishReason: toFinishReason(completed.turn?.status), usage }));
1353
- break;
1354
- }
1510
+ };
1355
1511
  }
1512
+ return [];
1513
+ }
1514
+ // turn/completed
1515
+ handleTurnCompleted(params) {
1516
+ const parts = [];
1517
+ this.ensureStreamStarted(parts);
1518
+ for (const itemId of this.openTextParts) {
1519
+ parts.push(this.withMeta({ type: "text-end", id: itemId }));
1520
+ }
1521
+ this.openTextParts.clear();
1522
+ for (const itemId of this.openReasoningParts) {
1523
+ parts.push(this.withMeta({ type: "reasoning-end", id: itemId }));
1524
+ }
1525
+ this.openReasoningParts.clear();
1526
+ for (const [itemId, tracked] of this.openToolCalls) {
1527
+ parts.push(this.withMeta({
1528
+ type: "tool-result",
1529
+ toolCallId: itemId,
1530
+ toolName: tracked.toolName,
1531
+ result: { output: this.formatToolOutput(tracked.output, tracked.droppedChars) }
1532
+ }));
1533
+ }
1534
+ this.openToolCalls.clear();
1535
+ const completed = params ?? {};
1536
+ if (completed.turn?.id) {
1537
+ this.planSequenceByTurnId.delete(completed.turn.id);
1538
+ }
1539
+ const usage = this.latestUsage ?? EMPTY_USAGE;
1540
+ parts.push(this.withMeta({ type: "finish", finishReason: toFinishReason(completed.turn?.status), usage }));
1356
1541
  return parts;
1357
1542
  }
1358
1543
  };
@@ -1676,16 +1861,28 @@ function extractToolResults(prompt, callId) {
1676
1861
  if (callId && part.toolCallId !== callId) {
1677
1862
  continue;
1678
1863
  }
1679
- if (part.output.type === "text") {
1864
+ if (part.output.type === "text" || part.output.type === "error-text") {
1680
1865
  contentItems.push({ type: "inputText", text: part.output.value });
1681
- } else if (part.output.type === "json") {
1866
+ if (part.output.type === "error-text") {
1867
+ success = false;
1868
+ }
1869
+ } else if (part.output.type === "json" || part.output.type === "error-json") {
1682
1870
  contentItems.push({ type: "inputText", text: JSON.stringify(part.output.value) });
1871
+ if (part.output.type === "error-json") {
1872
+ success = false;
1873
+ }
1683
1874
  } else if (part.output.type === "execution-denied") {
1684
1875
  success = false;
1685
1876
  contentItems.push({
1686
1877
  type: "inputText",
1687
1878
  text: part.output.reason ?? "Tool execution was denied."
1688
1879
  });
1880
+ } else if (part.output.type === "content") {
1881
+ for (const item of part.output.value) {
1882
+ if (item.type === "text") {
1883
+ contentItems.push({ type: "inputText", text: item.text });
1884
+ }
1885
+ }
1689
1886
  }
1690
1887
  }
1691
1888
  }
@@ -1839,7 +2036,7 @@ var CodexLanguageModel = class {
1839
2036
  }
1840
2037
  doStream(options) {
1841
2038
  const resumeThreadId = extractResumeThreadId(options.prompt);
1842
- const transport = this.config.providerSettings.transportFactory ? this.config.providerSettings.transportFactory(options.abortSignal, resumeThreadId) : this.config.providerSettings.transport?.type === "websocket" ? new WebSocketTransport(this.config.providerSettings.transport.websocket) : new StdioTransport(this.config.providerSettings.transport?.stdio);
2039
+ const transport = this.config.providerSettings.transportFactory ? this.config.providerSettings.transportFactory(stripUndefined({ signal: options.abortSignal, threadId: resumeThreadId })) : this.config.providerSettings.transport?.type === "websocket" ? new WebSocketTransport(this.config.providerSettings.transport.websocket) : new StdioTransport(this.config.providerSettings.transport?.stdio);
1843
2040
  const packetLogger = this.config.providerSettings.debug?.logPackets === true ? this.config.providerSettings.debug.logger ?? ((packet) => {
1844
2041
  if (packet.direction === "inbound") {
1845
2042
  console.debug("[codex packet]", packet.message);
@@ -1855,7 +2052,8 @@ var CodexLanguageModel = class {
1855
2052
  onPacket: packetLogger
1856
2053
  }));
1857
2054
  const mapper = new CodexEventMapper(stripUndefined({
1858
- emitPlanUpdates: this.config.providerSettings.emitPlanUpdates
2055
+ emitPlanUpdates: this.config.providerSettings.emitPlanUpdates,
2056
+ maxToolResultOutputChars: this.config.providerSettings.maxToolResultOutputChars
1859
2057
  }));
1860
2058
  let activeThreadId;
1861
2059
  let activeTurnId;
@@ -2272,7 +2470,7 @@ function createCodexAppServer(settings = {}) {
2272
2470
  const scope = settings.persistent.scope ?? "provider";
2273
2471
  const poolSize = settings.persistent.poolSize ?? 1;
2274
2472
  const idleTimeoutMs = settings.persistent.idleTimeoutMs ?? 3e5;
2275
- const poolTransportFactory = baseTransportFactory ?? (settings.transport?.type === "websocket" ? () => new WebSocketTransport(settings.transport?.websocket) : () => new StdioTransport(settings.transport?.stdio));
2473
+ const poolTransportFactory = baseTransportFactory ? () => baseTransportFactory({}) : settings.transport?.type === "websocket" ? () => new WebSocketTransport(settings.transport?.websocket) : () => new StdioTransport(settings.transport?.stdio);
2276
2474
  persistentPoolHandle = acquirePersistentPool({
2277
2475
  scope,
2278
2476
  ...stripUndefined({ key: settings.persistent.key }),
@@ -2282,7 +2480,7 @@ function createCodexAppServer(settings = {}) {
2282
2480
  });
2283
2481
  }
2284
2482
  const persistentPool = persistentPoolHandle?.pool ?? null;
2285
- const effectiveTransportFactory = persistentPool ? (signal, threadId) => new PersistentTransport(stripUndefined({ pool: persistentPool, signal, threadId })) : baseTransportFactory;
2483
+ const effectiveTransportFactory = persistentPool ? (context) => new PersistentTransport(stripUndefined({ pool: persistentPool, signal: context.signal, threadId: context.threadId })) : baseTransportFactory;
2286
2484
  const resolvedSettings = Object.freeze(stripUndefined({
2287
2485
  defaultModel: settings.defaultModel,
2288
2486
  experimentalApi: settings.experimentalApi,
@@ -2308,6 +2506,7 @@ function createCodexAppServer(settings = {}) {
2308
2506
  approvals: settings.approvals ? { ...settings.approvals } : void 0,
2309
2507
  debug: settings.debug ? { ...settings.debug } : void 0,
2310
2508
  emitPlanUpdates: settings.emitPlanUpdates,
2509
+ maxToolResultOutputChars: settings.maxToolResultOutputChars,
2311
2510
  onSessionCreated: settings.onSessionCreated
2312
2511
  }));
2313
2512
  const createLanguageModel = (modelId, modelSettings = {}) => new CodexLanguageModel(modelId, modelSettings, {
@@ -2331,7 +2530,7 @@ function createCodexAppServer(settings = {}) {
2331
2530
  throw createNoSuchModelError(modelId, "imageModel");
2332
2531
  },
2333
2532
  async listModels(params) {
2334
- const transport = effectiveTransportFactory ? effectiveTransportFactory() : resolvedSettings.transport?.type === "websocket" ? new WebSocketTransport(resolvedSettings.transport.websocket) : new StdioTransport(resolvedSettings.transport?.stdio);
2533
+ const transport = effectiveTransportFactory ? effectiveTransportFactory({}) : resolvedSettings.transport?.type === "websocket" ? new WebSocketTransport(resolvedSettings.transport.websocket) : new StdioTransport(resolvedSettings.transport?.stdio);
2335
2534
  const client = new AppServerClient(transport);
2336
2535
  try {
2337
2536
  await client.connect();