codexmate 0.0.53 → 0.0.55

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.
@@ -300,9 +300,28 @@ function cloneJsonValue(value) {
300
300
  }
301
301
 
302
302
  function normalizeResponsesToolOutput(value) {
303
- if (typeof value === 'string') return value;
304
- if (value == null) return '';
305
- return stringifyJsonValue(value, '');
303
+ if (typeof value === 'string') return value || '(empty)';
304
+ if (value == null) return '(empty)';
305
+ return stringifyJsonValue(value, '(empty)') || '(empty)';
306
+ }
307
+
308
+ function chatContentToPlainText(content) {
309
+ if (typeof content === 'string') return content;
310
+ if (!Array.isArray(content)) return '';
311
+ return content
312
+ .map((item) => {
313
+ if (typeof item === 'string') return item;
314
+ if (!isRecord(item)) return '';
315
+ if (typeof item.text === 'string') return item.text;
316
+ if (typeof item.content === 'string') return item.content;
317
+ return '';
318
+ })
319
+ .join('');
320
+ }
321
+
322
+ function wrapReasoningContentForChat(content) {
323
+ const text = chatContentToPlainText(content).trim();
324
+ return text ? `<thinking>${text}</thinking>` : '';
306
325
  }
307
326
 
308
327
  function normalizeOpenAiToolArguments(value) {
@@ -530,7 +549,7 @@ function normalizeResponsesInputToChatMessages(input) {
530
549
 
531
550
  if (itemType === 'reasoning') {
532
551
  flushPendingToolCalls();
533
- const reasoningContent = toOpenAiMessageContent(item.summary != null ? item.summary : (item.content != null ? item.content : item));
552
+ const reasoningContent = wrapReasoningContentForChat(toOpenAiMessageContent(item.summary != null ? item.summary : (item.content != null ? item.content : item)));
534
553
  const reasoningSignature = asTrimmedString(item.encrypted_content || item.reasoning_signature);
535
554
  if (!hasOpenAiMessageContent(reasoningContent) && !reasoningSignature) return;
536
555
  const message = { role: 'assistant', content: reasoningContent };
@@ -889,6 +908,12 @@ function convertResponsesRequestToChatCompletions(payload) {
889
908
  top_p: Number.isFinite(body.top_p) ? Number(body.top_p) : undefined,
890
909
  max_tokens: Number.isFinite(maxOutputTokens) && maxOutputTokens > 0 ? maxOutputTokens : undefined
891
910
  };
911
+ if (isRecord(body.reasoning)) {
912
+ const effort = asTrimmedString(body.reasoning.effort);
913
+ if (effort) chat.reasoning_effort = effort;
914
+ } else if (asTrimmedString(body.reasoning_effort)) {
915
+ chat.reasoning_effort = asTrimmedString(body.reasoning_effort);
916
+ }
892
917
  if (Array.isArray(body.stop) && body.stop.length) {
893
918
  chat.stop = body.stop.filter((item) => typeof item === 'string' && item.trim());
894
919
  }
@@ -940,12 +965,22 @@ function buildResponsesPayloadFromChatResult(model, text, toolCalls, upstreamPay
940
965
  }
941
966
  }
942
967
 
968
+ const choice = upstreamPayload && typeof upstreamPayload === 'object' && Array.isArray(upstreamPayload.choices)
969
+ ? upstreamPayload.choices[0]
970
+ : null;
971
+ const finishReason = choice && typeof choice.finish_reason === 'string' ? choice.finish_reason : '';
972
+ const statusPatch = finishReason === 'length'
973
+ ? { status: 'incomplete', incomplete_details: { reason: 'max_output_tokens' } }
974
+ : (finishReason === 'content_filter'
975
+ ? { status: 'incomplete', incomplete_details: { reason: 'content_filter' } }
976
+ : { status: 'completed' });
977
+
943
978
  const payload = {
944
979
  id: responseId,
945
980
  object: 'response',
946
981
  model,
947
982
  created_at: createdAt,
948
- status: 'completed',
983
+ ...statusPatch,
949
984
  output,
950
985
  output_text: trimmedText
951
986
  };
@@ -1017,12 +1052,14 @@ function sendResponsesSse(res, responsePayload) {
1017
1052
  const itemId = typeof item.id === 'string' && item.id.trim()
1018
1053
  ? item.id.trim()
1019
1054
  : (typeof item.call_id === 'string' && item.call_id.trim() ? item.call_id.trim() : `item_${crypto.randomBytes(8).toString('hex')}`);
1055
+ const wireItem = buildResponsesSseItem(item, itemId);
1020
1056
 
1021
1057
  // Emit item added so Codex can anchor subsequent deltas by output_index/content_index/item_id.
1022
1058
  writeSse(res, 'response.output_item.added', {
1023
1059
  type: 'response.output_item.added',
1024
1060
  output_index: outputIndex,
1025
- item: { ...item, id: itemId }
1061
+ item: wireItem,
1062
+ sequence_number: nextSeq()
1026
1063
  });
1027
1064
 
1028
1065
  if (itemType === 'message') {
@@ -1032,6 +1069,7 @@ function sendResponsesSse(res, responsePayload) {
1032
1069
  if (!block || typeof block !== 'object') continue;
1033
1070
  if (block.type !== 'output_text') continue;
1034
1071
  const text = typeof block.text === 'string' ? block.text : '';
1072
+ emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq);
1035
1073
  if (text) {
1036
1074
  writeSse(res, 'response.output_text.delta', {
1037
1075
  type: 'response.output_text.delta',
@@ -1050,6 +1088,33 @@ function sendResponsesSse(res, responsePayload) {
1050
1088
  text,
1051
1089
  sequence_number: nextSeq()
1052
1090
  });
1091
+ emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq);
1092
+ }
1093
+ } else if (itemType === 'function_call' || itemType === 'custom_tool_call') {
1094
+ emitResponsesToolArgumentEvents(res, wireItem, outputIndex, nextSeq);
1095
+ } else if (itemType === 'reasoning') {
1096
+ const summary = Array.isArray(item.summary) ? item.summary : [];
1097
+ for (let summaryIndex = 0; summaryIndex < summary.length; summaryIndex += 1) {
1098
+ const block = summary[summaryIndex];
1099
+ const text = block && typeof block.text === 'string' ? block.text : '';
1100
+ if (text) {
1101
+ writeSse(res, 'response.reasoning_summary_text.delta', {
1102
+ type: 'response.reasoning_summary_text.delta',
1103
+ item_id: itemId,
1104
+ output_index: outputIndex,
1105
+ summary_index: summaryIndex,
1106
+ delta: text,
1107
+ sequence_number: nextSeq()
1108
+ });
1109
+ }
1110
+ writeSse(res, 'response.reasoning_summary_text.done', {
1111
+ type: 'response.reasoning_summary_text.done',
1112
+ item_id: itemId,
1113
+ output_index: outputIndex,
1114
+ summary_index: summaryIndex,
1115
+ text,
1116
+ sequence_number: nextSeq()
1117
+ });
1053
1118
  }
1054
1119
  }
1055
1120
 
@@ -1057,7 +1122,7 @@ function sendResponsesSse(res, responsePayload) {
1057
1122
  writeSse(res, 'response.output_item.done', {
1058
1123
  type: 'response.output_item.done',
1059
1124
  output_index: outputIndex,
1060
- item: { ...item, id: itemId },
1125
+ item: wireItem,
1061
1126
  sequence_number: nextSeq()
1062
1127
  });
1063
1128
  }
@@ -1083,15 +1148,25 @@ function extractResponsesOutputText(payload) {
1083
1148
  return '';
1084
1149
  }
1085
1150
 
1086
- function toUpstreamNonStreamingResponsesPayload(payload) {
1151
+ function normalizeResponsesPayloadForUpstream(payload, stream) {
1087
1152
  const body = payload && typeof payload === 'object' ? payload : {};
1088
- const normalized = { ...body, stream: false };
1153
+ const normalized = { ...body, stream };
1089
1154
  if (Array.isArray(body.tools)) {
1090
1155
  normalized.tools = normalizeResponsesToolsForResponsesApi(body.tools);
1091
1156
  }
1157
+ if (isRecord(body.reasoning)) {
1158
+ const include = Array.isArray(body.include) ? body.include.filter((item) => typeof item === 'string') : [];
1159
+ if (!include.includes('reasoning.encrypted_content')) {
1160
+ normalized.include = [...include, 'reasoning.encrypted_content'];
1161
+ }
1162
+ }
1092
1163
  return normalized;
1093
1164
  }
1094
1165
 
1166
+ function toUpstreamNonStreamingResponsesPayload(payload) {
1167
+ return normalizeResponsesPayloadForUpstream(payload, false);
1168
+ }
1169
+
1095
1170
  function shouldFallbackFromUpstreamResponses(status, bodyText) {
1096
1171
  if (!Number.isFinite(status)) return false;
1097
1172
  // Common "unsupported" status codes for a route.
@@ -1202,6 +1277,101 @@ function writeSse(res, eventName, dataObj) {
1202
1277
  res.write(`data: ${JSON.stringify(dataObj)}\n\n`);
1203
1278
  }
1204
1279
 
1280
+ function buildResponsesSseItem(item, fallbackId) {
1281
+ const source = isRecord(item) ? item : {};
1282
+ const itemId = asTrimmedString(source.id || source.call_id) || fallbackId || `item_${crypto.randomBytes(8).toString('hex')}`;
1283
+ if (source.type === 'message') {
1284
+ return {
1285
+ ...source,
1286
+ id: itemId,
1287
+ role: source.role || 'assistant',
1288
+ content: Array.isArray(source.content) ? source.content : []
1289
+ };
1290
+ }
1291
+ if (source.type === 'reasoning') {
1292
+ return {
1293
+ ...source,
1294
+ id: itemId,
1295
+ summary: Array.isArray(source.summary) ? source.summary : []
1296
+ };
1297
+ }
1298
+ if (source.type === 'function_call') {
1299
+ return {
1300
+ ...source,
1301
+ id: itemId,
1302
+ call_id: asTrimmedString(source.call_id) || itemId,
1303
+ name: asTrimmedString(source.name),
1304
+ arguments: typeof source.arguments === 'string' ? source.arguments : ''
1305
+ };
1306
+ }
1307
+ if (source.type === 'custom_tool_call') {
1308
+ return {
1309
+ ...source,
1310
+ id: itemId,
1311
+ call_id: asTrimmedString(source.call_id) || itemId,
1312
+ name: asTrimmedString(source.name),
1313
+ input: typeof source.input === 'string' ? source.input : ''
1314
+ };
1315
+ }
1316
+ return { ...source, id: itemId };
1317
+ }
1318
+
1319
+ function emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq) {
1320
+ writeSse(res, 'response.content_part.added', {
1321
+ type: 'response.content_part.added',
1322
+ item_id: itemId,
1323
+ output_index: outputIndex,
1324
+ content_index: contentIndex,
1325
+ part: { type: 'output_text', text: '', annotations: [], logprobs: [] },
1326
+ sequence_number: nextSeq()
1327
+ });
1328
+ }
1329
+
1330
+ function emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq) {
1331
+ writeSse(res, 'response.content_part.done', {
1332
+ type: 'response.content_part.done',
1333
+ item_id: itemId,
1334
+ output_index: outputIndex,
1335
+ content_index: contentIndex,
1336
+ part: { type: 'output_text', text: typeof text === 'string' ? text : '', annotations: [], logprobs: [] },
1337
+ sequence_number: nextSeq()
1338
+ });
1339
+ }
1340
+
1341
+ function emitResponsesToolArgumentEvents(res, item, outputIndex, nextSeq) {
1342
+ const eventType = item.type === 'custom_tool_call'
1343
+ ? 'response.custom_tool_call_input.delta'
1344
+ : 'response.function_call_arguments.delta';
1345
+ const doneType = item.type === 'custom_tool_call'
1346
+ ? ''
1347
+ : 'response.function_call_arguments.done';
1348
+ const value = item.type === 'custom_tool_call'
1349
+ ? (typeof item.input === 'string' ? item.input : '')
1350
+ : (typeof item.arguments === 'string' ? item.arguments : '');
1351
+ if (value) {
1352
+ writeSse(res, eventType, {
1353
+ type: eventType,
1354
+ item_id: item.id,
1355
+ output_index: outputIndex,
1356
+ call_id: item.call_id,
1357
+ name: item.name,
1358
+ delta: value,
1359
+ sequence_number: nextSeq()
1360
+ });
1361
+ }
1362
+ if (doneType) {
1363
+ writeSse(res, doneType, {
1364
+ type: doneType,
1365
+ item_id: item.id,
1366
+ output_index: outputIndex,
1367
+ call_id: item.call_id,
1368
+ name: item.name,
1369
+ arguments: value,
1370
+ sequence_number: nextSeq()
1371
+ });
1372
+ }
1373
+ }
1374
+
1205
1375
  function appendChatStreamToolCall(target, toolCall) {
1206
1376
  if (!toolCall || typeof toolCall !== 'object') return;
1207
1377
  const index = Number.isFinite(toolCall.index) ? toolCall.index : target.length;
@@ -1232,6 +1402,37 @@ function writeChatCompletionChunkAsResponsesSse(state, chunk) {
1232
1402
  const delta = choice && choice.delta && typeof choice.delta === 'object' ? choice.delta : null;
1233
1403
  if (!delta) continue;
1234
1404
 
1405
+ const reasoningSegment = typeof delta.reasoning_content === 'string'
1406
+ ? delta.reasoning_content
1407
+ : (typeof delta.reasoning === 'string'
1408
+ ? delta.reasoning
1409
+ : (typeof delta.reasoning_text === 'string' ? delta.reasoning_text : ''));
1410
+ if (reasoningSegment) {
1411
+ if (!state.reasoningItem) {
1412
+ state.reasoningItem = {
1413
+ id: `rs_${crypto.randomBytes(8).toString('hex')}`,
1414
+ type: 'reasoning',
1415
+ summary: [{ type: 'summary_text', text: '' }]
1416
+ };
1417
+ state.output.push(state.reasoningItem);
1418
+ writeSse(state.res, 'response.output_item.added', {
1419
+ type: 'response.output_item.added',
1420
+ output_index: state.output.length - 1,
1421
+ item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id)
1422
+ });
1423
+ }
1424
+ state.reasoningText += reasoningSegment;
1425
+ state.reasoningItem.summary[0].text = state.reasoningText;
1426
+ writeSse(state.res, 'response.reasoning_summary_text.delta', {
1427
+ type: 'response.reasoning_summary_text.delta',
1428
+ item_id: state.reasoningItem.id,
1429
+ output_index: state.output.indexOf(state.reasoningItem),
1430
+ summary_index: 0,
1431
+ delta: reasoningSegment,
1432
+ sequence_number: state.nextSeq()
1433
+ });
1434
+ }
1435
+
1235
1436
  const segments = [];
1236
1437
  // DeepSeek-style OpenAI-compatible streams may emit private reasoning in
1237
1438
  // `reasoning_content` before the final answer. Responses `output_text`
@@ -1252,8 +1453,9 @@ function writeChatCompletionChunkAsResponsesSse(state, chunk) {
1252
1453
  writeSse(state.res, 'response.output_item.added', {
1253
1454
  type: 'response.output_item.added',
1254
1455
  output_index: state.output.length - 1,
1255
- item: state.messageItem
1456
+ item: buildResponsesSseItem(state.messageItem, state.messageItem.id)
1256
1457
  });
1458
+ emitResponsesTextPartAdded(state.res, state.messageItem.id, state.output.length - 1, 0, state.nextSeq);
1257
1459
  }
1258
1460
  state.messageText += seg;
1259
1461
  state.messageItem.content[0].text = state.messageText;
@@ -1283,6 +1485,24 @@ function finishChatStreamResponsesSse(state) {
1283
1485
  if (!state || state.finished) return;
1284
1486
  state.finished = true;
1285
1487
 
1488
+ if (state.reasoningItem) {
1489
+ const outputIndex = state.output.indexOf(state.reasoningItem);
1490
+ writeSse(state.res, 'response.reasoning_summary_text.done', {
1491
+ type: 'response.reasoning_summary_text.done',
1492
+ item_id: state.reasoningItem.id,
1493
+ output_index: outputIndex,
1494
+ summary_index: 0,
1495
+ text: state.reasoningText,
1496
+ sequence_number: state.nextSeq()
1497
+ });
1498
+ writeSse(state.res, 'response.output_item.done', {
1499
+ type: 'response.output_item.done',
1500
+ output_index: outputIndex,
1501
+ item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id),
1502
+ sequence_number: state.nextSeq()
1503
+ });
1504
+ }
1505
+
1286
1506
  if (state.messageItem) {
1287
1507
  const outputIndex = state.output.indexOf(state.messageItem);
1288
1508
  writeSse(state.res, 'response.output_text.done', {
@@ -1293,10 +1513,11 @@ function finishChatStreamResponsesSse(state) {
1293
1513
  text: state.messageText,
1294
1514
  sequence_number: state.nextSeq()
1295
1515
  });
1516
+ emitResponsesTextPartDone(state.res, state.messageItem.id, outputIndex, 0, state.messageText, state.nextSeq);
1296
1517
  writeSse(state.res, 'response.output_item.done', {
1297
1518
  type: 'response.output_item.done',
1298
1519
  output_index: outputIndex,
1299
- item: state.messageItem,
1520
+ item: buildResponsesSseItem(state.messageItem, state.messageItem.id),
1300
1521
  sequence_number: state.nextSeq()
1301
1522
  });
1302
1523
  }
@@ -1310,12 +1531,13 @@ function finishChatStreamResponsesSse(state) {
1310
1531
  writeSse(state.res, 'response.output_item.added', {
1311
1532
  type: 'response.output_item.added',
1312
1533
  output_index: outputIndex,
1313
- item
1534
+ item: buildResponsesSseItem(item, item.call_id)
1314
1535
  });
1536
+ emitResponsesToolArgumentEvents(state.res, buildResponsesSseItem(item, item.call_id), outputIndex, state.nextSeq);
1315
1537
  writeSse(state.res, 'response.output_item.done', {
1316
1538
  type: 'response.output_item.done',
1317
1539
  output_index: outputIndex,
1318
- item,
1540
+ item: buildResponsesSseItem(item, item.call_id),
1319
1541
  sequence_number: state.nextSeq()
1320
1542
  });
1321
1543
  }
@@ -1485,6 +1707,8 @@ function streamChatCompletionsAsResponsesSse(targetUrl, options = {}) {
1485
1707
  output: [],
1486
1708
  messageItem: null,
1487
1709
  messageText: '',
1710
+ reasoningItem: null,
1711
+ reasoningText: '',
1488
1712
  toolCalls: [],
1489
1713
  toolTypesByName: options.toolTypesByName || {},
1490
1714
  finished: false,
@@ -2012,7 +2236,7 @@ function createOpenaiBridgeHttpHandler(options = {}) {
2012
2236
  ? { ok: false, status: 404, bodyText: '' }
2013
2237
  : await retryTransientRequest(() => streamResponsesSse(upstreamResponsesUrl, {
2014
2238
  method: 'POST',
2015
- body: responsesRequest,
2239
+ body: normalizeResponsesPayloadForUpstream(responsesRequest, true),
2016
2240
  headers: codexHeaders,
2017
2241
  timeoutMs: streamTimeoutMs,
2018
2242
  maxBytes: maxUpstreamBytes,