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.
package/README.md CHANGED
@@ -10,6 +10,7 @@
10
10
  <a href="https://sakurabytecore.github.io/codexmate/">[Documentation]</a>
11
11
  <a href="#quick-start">[Quick Start]</a>
12
12
  <a href="README.zh.md">[简体中文]</a>
13
+ <a href="README.vi.md">[Tiếng Việt]</a>
13
14
  </p>
14
15
 
15
16
  [![Version](https://img.shields.io/npm/v/codexmate?style=flat-square&color=A179FF)](https://www.npmjs.com/package/codexmate)
package/README.vi.md ADDED
@@ -0,0 +1,170 @@
1
+ <div align="center">
2
+
3
+ <img src="site/.vitepress/public/images/logo.png" alt="Codex Mate logo" width="160" />
4
+
5
+ # Codex Mate
6
+
7
+ **Một dashboard duy nhất cho tất cả AI coding agent cục bộ của bạn. Chuyển đổi provider, quản lý phiên làm việc và điều phối tác vụ giữa Codex, Claude Code và OpenClaw. Không cloud, trung tâm điều khiển agent hoàn toàn cục bộ.**
8
+
9
+ <p>
10
+ <a href="https://sakurabytecore.github.io/codexmate/">[Tài liệu]</a>
11
+ <a href="#bắt-đầu-nhanh">[Bắt đầu nhanh]</a>
12
+ <a href="README.md">[English]</a>
13
+ <a href="README.zh.md">[简体中文]</a>
14
+ </p>
15
+
16
+ [![Version](https://img.shields.io/npm/v/codexmate?style=flat-square&color=A179FF)](https://www.npmjs.com/package/codexmate)
17
+ [![Build](https://img.shields.io/github/actions/workflow/status/SakuraByteCore/codexmate/release.yml?style=flat-square&color=44cc11)](https://github.com/SakuraByteCore/codexmate/actions/workflows/release.yml)
18
+ [![Downloads](https://img.shields.io/npm/dt/codexmate?style=flat-square)](https://www.npmjs.com/package/codexmate)
19
+ [![Install](https://img.shields.io/badge/cài%20đặt-brew%20%7C%20curl%20%7C%20npm-0A0?style=flat-square)](#bắt-đầu-nhanh)
20
+ [![Platform](https://img.shields.io/badge/nền%20tảng-Termux%20%7C%20Linux%20%7C%20macOS%20%7C%20Windows-555?style=flat-square)](#bắt-đầu-nhanh)
21
+ [![Node](https://img.shields.io/node/v/codexmate?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org/)
22
+ [![License](https://img.shields.io/npm/l/codexmate?style=flat-square)](LICENSE)
23
+ [![Stars](https://img.shields.io/github/stars/SakuraByteCore/codexmate?style=flat-square&color=gold)](https://github.com/SakuraByteCore/codexmate/stargazers)
24
+ [![Issues](https://img.shields.io/github/issues/SakuraByteCore/codexmate?style=flat-square&color=ff69b4)](https://github.com/SakuraByteCore/codexmate/issues)
25
+
26
+ <br />
27
+
28
+ <img src="site/.vitepress/public/images/readme-hero.png" alt="Giao diện Codex Mate" width="100%" />
29
+
30
+ </div>
31
+
32
+ ---
33
+
34
+ > [!TIP]
35
+ > **Ưu tiên cục bộ**: Toàn bộ cấu hình và phiên làm việc được lưu trong thư mục home của bạn. Không có telemetry, không cần tài khoản cloud.
36
+
37
+ > [!IMPORTANT]
38
+ > Dự án đang ở giai đoạn đầu. Chúng tôi đang tìm kiếm các nhà phát triển để cùng xây dựng hệ sinh thái agent cục bộ!
39
+
40
+ ## Codex Mate là gì?
41
+
42
+ Bạn có bao giờ cảm thấy rối khi phải quản lý nhiều AI agent cục bộ? Mỗi tool lại có định dạng cấu hình, nơi lưu phiên và thư mục skill riêng.
43
+
44
+ **Codex Mate** cung cấp một trung tâm điều khiển thống nhất để giải quyết sự hỗn loạn đó. Đây là CLI + Web UI ưu tiên cục bộ, được thiết kế để quản lý [Codex](https://github.com/openai/codex), [Claude Code](https://github.com/anthropic-ai/claude-code) và [OpenClaw](https://github.com/moeru-ai/openclaw) một cách liền mạch.
45
+
46
+ ### Điểm nổi bật
47
+
48
+ Khác với các wrapper đơn giản, Codex Mate hoạt động như một **Agent Bridge cục bộ**:
49
+ - **Trình duyệt phiên thống nhất**: Tìm kiếm và xuất phiên làm việc từ tất cả tool trong một nơi duy nhất.
50
+ - **Bridge tương thích OpenAI**: Dùng Codex với bất kỳ UI nào hỗ trợ OpenAI bằng cách chuẩn hóa Responses API.
51
+ - **Chợ skill cục bộ**: Chia sẻ và nhập skill giữa các app agent khác nhau.
52
+ - **Điều phối tác vụ**: Lập kế hoạch và thực thi tác vụ phức tạp với theo dõi phụ thuộc.
53
+
54
+ ---
55
+
56
+ ## Tiến độ hiện tại
57
+
58
+ | Tính năng | Trạng thái | Mô tả |
59
+ | --- | --- | --- |
60
+ | **Quản lý Provider** | ✅ | Chuyển đổi provider/model cho Codex, Claude và OpenClaw |
61
+ | **Đồng bộ Agent trực tiếp** | ✅ | Giám sát cấu hình & trạng thái Codex/Claude theo thời gian thực |
62
+ | **Trình duyệt phiên** | ✅ | Liệt kê, lọc và xuất phiên (Codex/Claude/Gemini) |
63
+ | **Phân tích sử dụng** | ✅ | Trực quan hóa xu hướng tin nhắn và dự án nổi bật |
64
+ | **Chợ skill cục bộ** | ✅ | Import/export skill giữa các app agent |
65
+ | **Hàng đợi tác vụ** | ✅ | Thực thi tác vụ theo DAG và xem log |
66
+ | **Bridge OpenAI** | ✅ | Chuyển đổi Codex Responses API sang định dạng OpenAI chuẩn |
67
+ | **Mẫu Prompt** | ✅ | Plugin prompt tái sử dụng được với hỗ trợ biến |
68
+ | **Tích hợp MCP** | ✅ | Expose tool và resource cục bộ qua MCP stdio |
69
+ | **Tự động cập nhật** | ✅ | Cập nhật nhanh qua `codexmate update` |
70
+
71
+ ---
72
+
73
+ ## Bắt đầu nhanh
74
+
75
+ ### Cài đặt qua Homebrew (macOS / Linux)
76
+
77
+ ```bash
78
+ brew tap SakuraByteCore/codexmate
79
+ brew install codexmate
80
+ ```
81
+
82
+ Yêu cầu [Node.js](https://nodejs.org/) (`brew install node` nếu chưa có).
83
+
84
+ ### Cài đặt qua npm
85
+
86
+ ```bash
87
+ npm install -g codexmate
88
+ codexmate run
89
+ ```
90
+
91
+ Nếu cổng mặc định `3737` bận, Codex Mate tự động thử cổng kế tiếp (`3738`, `3739`, ...). Để chỉ định cổng cố định:
92
+
93
+ ```bash
94
+ CODEXMATE_PORT=8080 codexmate run
95
+ ```
96
+
97
+ Windows PowerShell:
98
+
99
+ ```powershell
100
+ $env:CODEXMATE_PORT=8080; codexmate run
101
+ ```
102
+
103
+ ### Cài đặt qua curl (Standalone)
104
+
105
+ ```bash
106
+ curl -fsSL https://raw.githubusercontent.com/SakuraByteCore/codexmate/main/scripts/install.sh | bash
107
+ ```
108
+
109
+ ### Agent được hỗ trợ
110
+
111
+ - **Codex**: `npm install -g @openai/codex`
112
+ - **Claude Code**: `npm install -g @anthropic-ai/claude-code`
113
+ - **Gemini CLI**: `npm install -g @google/gemini-cli`
114
+ - **CodeBuddy**: `npm install -g @tencent-ai/codebuddy-code`
115
+ - **OpenCode**: xem [tài liệu chính thức OpenCode](https://opencode.ai/)
116
+
117
+ ---
118
+
119
+ ## Kiến trúc
120
+
121
+ ```mermaid
122
+ %%{ init: { 'flowchart': { 'curve': 'catmullRom' } } }%%
123
+ flowchart TD
124
+ User([Người dùng])
125
+ CLI[CLI]
126
+ WebUI[Web UI]
127
+ MCP[MCP Server]
128
+
129
+ subgraph Mate [Codex Mate Core]
130
+ API[HTTP API]
131
+ Config[Config Engine]
132
+ Session[Quản lý phiên]
133
+ Skills[Chợ Skill]
134
+ Tasks[Thực thi tác vụ]
135
+ end
136
+
137
+ subgraph Local [Hệ thống tệp cục bộ]
138
+ CodexDir[~/.codex]
139
+ ClaudeDir[~/.claude]
140
+ ClawDir[~/.openclaw]
141
+ State[Phiên/Sử dụng/Thùng rác]
142
+ end
143
+
144
+ User --> CLI & WebUI & MCP
145
+ CLI & WebUI & MCP --> API
146
+
147
+ API --> Config & Session & Skills & Tasks
148
+
149
+ Config --> CodexDir & ClaudeDir & ClawDir
150
+ Session --> State
151
+ Skills --> Local
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Lời cảm ơn
157
+
158
+ Cảm ơn tất cả những người đóng góp cho Codex Mate ❤️
159
+
160
+ <a href="https://github.com/SakuraByteCore/codexmate/graphs/contributors">
161
+ <img src="https://contrib.rocks/image?repo=SakuraByteCore/codexmate" alt="Contributors" />
162
+ </a>
163
+
164
+ ## Lịch sử Star
165
+
166
+ [![Star History Chart](https://api.star-history.com/svg?repos=SakuraByteCore/codexmate&type=Date)](https://star-history.com/#SakuraByteCore/codexmate&Date)
167
+
168
+ ## Giấy phép
169
+
170
+ Apache-2.0
@@ -940,7 +940,6 @@ function createBuiltinProxyRuntimeController(deps = {}) {
940
940
  'n',
941
941
  'modalities',
942
942
  'audio',
943
- 'reasoning',
944
943
  'reasoning_effort',
945
944
  'service_tier'
946
945
  ];
@@ -964,10 +963,15 @@ function createBuiltinProxyRuntimeController(deps = {}) {
964
963
  if (isRecord(source.text) && asTrimmedString(source.text.verbosity)) {
965
964
  chatBody.verbosity = asTrimmedString(source.text.verbosity);
966
965
  }
966
+ if (isRecord(source.reasoning) && asTrimmedString(source.reasoning.effort)) {
967
+ chatBody.reasoning_effort = asTrimmedString(source.reasoning.effort);
968
+ }
967
969
 
968
970
  pruneInvalidChatToolChoice(chatBody);
969
971
 
970
- if (Object.prototype.hasOwnProperty.call(source, 'max_tokens')) {
972
+ if (Object.prototype.hasOwnProperty.call(source, 'max_completion_tokens')) {
973
+ chatBody.max_tokens = Math.max(128, Number(source.max_completion_tokens) || 0);
974
+ } else if (Object.prototype.hasOwnProperty.call(source, 'max_tokens')) {
971
975
  chatBody.max_tokens = source.max_tokens;
972
976
  } else if (source.max_output_tokens != null) {
973
977
  chatBody.max_tokens = source.max_output_tokens;
@@ -976,6 +980,18 @@ function createBuiltinProxyRuntimeController(deps = {}) {
976
980
  return chatBody;
977
981
  }
978
982
 
983
+ function normalizeResponsesPayloadForUpstream(payload, stream) {
984
+ const source = isRecord(payload) ? payload : {};
985
+ const normalized = { ...source, stream };
986
+ if (isRecord(source.reasoning)) {
987
+ const include = Array.isArray(source.include) ? source.include.filter((item) => typeof item === 'string') : [];
988
+ if (!include.includes('reasoning.encrypted_content')) {
989
+ normalized.include = [...include, 'reasoning.encrypted_content'];
990
+ }
991
+ }
992
+ return normalized;
993
+ }
994
+
979
995
  function ensureResponseMetadata(payload) {
980
996
  const base = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload : {};
981
997
  const id = typeof base.id === 'string' && base.id.trim()
@@ -1001,6 +1017,112 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1001
1017
  res.write(`data: ${JSON.stringify(dataObj)}\n\n`);
1002
1018
  }
1003
1019
 
1020
+ function buildResponsesSseItem(item, fallbackId) {
1021
+ const source = isRecord(item) ? item : {};
1022
+ const itemId = asTrimmedString(source.id || source.call_id) || fallbackId || `item_${crypto.randomBytes(8).toString('hex')}`;
1023
+ if (source.type === 'message') {
1024
+ return {
1025
+ ...source,
1026
+ id: itemId,
1027
+ role: source.role || 'assistant',
1028
+ content: Array.isArray(source.content)
1029
+ ? source.content.map((part) => {
1030
+ if (!isRecord(part)) return part;
1031
+ if (part.type !== 'output_text') return cloneJsonValue(part);
1032
+ return {
1033
+ ...part,
1034
+ text: typeof part.text === 'string' ? part.text : '',
1035
+ annotations: Array.isArray(part.annotations) ? part.annotations : [],
1036
+ logprobs: Array.isArray(part.logprobs) ? part.logprobs : []
1037
+ };
1038
+ })
1039
+ : []
1040
+ };
1041
+ }
1042
+ if (source.type === 'reasoning') {
1043
+ return {
1044
+ ...source,
1045
+ id: itemId,
1046
+ summary: Array.isArray(source.summary) ? source.summary : []
1047
+ };
1048
+ }
1049
+ if (source.type === 'function_call') {
1050
+ return {
1051
+ ...source,
1052
+ id: itemId,
1053
+ call_id: asTrimmedString(source.call_id) || itemId,
1054
+ name: asTrimmedString(source.name),
1055
+ arguments: typeof source.arguments === 'string' ? source.arguments : ''
1056
+ };
1057
+ }
1058
+ if (source.type === 'custom_tool_call') {
1059
+ return {
1060
+ ...source,
1061
+ id: itemId,
1062
+ call_id: asTrimmedString(source.call_id) || itemId,
1063
+ name: asTrimmedString(source.name),
1064
+ input: typeof source.input === 'string' ? source.input : ''
1065
+ };
1066
+ }
1067
+ return { ...source, id: itemId };
1068
+ }
1069
+
1070
+ function emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq) {
1071
+ writeSse(res, 'response.content_part.added', {
1072
+ type: 'response.content_part.added',
1073
+ item_id: itemId,
1074
+ output_index: outputIndex,
1075
+ content_index: contentIndex,
1076
+ part: { type: 'output_text', text: '', annotations: [], logprobs: [] },
1077
+ sequence_number: nextSeq()
1078
+ });
1079
+ }
1080
+
1081
+ function emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq) {
1082
+ writeSse(res, 'response.content_part.done', {
1083
+ type: 'response.content_part.done',
1084
+ item_id: itemId,
1085
+ output_index: outputIndex,
1086
+ content_index: contentIndex,
1087
+ part: { type: 'output_text', text: typeof text === 'string' ? text : '', annotations: [], logprobs: [] },
1088
+ sequence_number: nextSeq()
1089
+ });
1090
+ }
1091
+
1092
+ function emitResponsesToolArgumentEvents(res, item, outputIndex, nextSeq) {
1093
+ const eventType = item.type === 'custom_tool_call'
1094
+ ? 'response.custom_tool_call_input.delta'
1095
+ : 'response.function_call_arguments.delta';
1096
+ const doneType = item.type === 'custom_tool_call'
1097
+ ? ''
1098
+ : 'response.function_call_arguments.done';
1099
+ const value = item.type === 'custom_tool_call'
1100
+ ? (typeof item.input === 'string' ? item.input : '')
1101
+ : (typeof item.arguments === 'string' ? item.arguments : '');
1102
+ if (value) {
1103
+ writeSse(res, eventType, {
1104
+ type: eventType,
1105
+ item_id: item.id,
1106
+ output_index: outputIndex,
1107
+ call_id: item.call_id,
1108
+ name: item.name,
1109
+ delta: value,
1110
+ sequence_number: nextSeq()
1111
+ });
1112
+ }
1113
+ if (doneType) {
1114
+ writeSse(res, doneType, {
1115
+ type: doneType,
1116
+ item_id: item.id,
1117
+ output_index: outputIndex,
1118
+ call_id: item.call_id,
1119
+ name: item.name,
1120
+ arguments: value,
1121
+ sequence_number: nextSeq()
1122
+ });
1123
+ }
1124
+ }
1125
+
1004
1126
  function sendResponsesSse(res, responsePayload) {
1005
1127
  const response = ensureResponseMetadata(responsePayload);
1006
1128
  const responseId = response.id;
@@ -1027,12 +1149,14 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1027
1149
  const itemType = typeof item.type === 'string' ? item.type : '';
1028
1150
  const itemId = typeof item.id === 'string' && item.id.trim()
1029
1151
  ? item.id.trim()
1030
- : `item_${crypto.randomBytes(8).toString('hex')}`;
1152
+ : (typeof item.call_id === 'string' && item.call_id.trim() ? item.call_id.trim() : `item_${crypto.randomBytes(8).toString('hex')}`);
1153
+ const wireItem = buildResponsesSseItem(item, itemId);
1031
1154
 
1032
1155
  writeSse(res, 'response.output_item.added', {
1033
1156
  type: 'response.output_item.added',
1034
1157
  output_index: outputIndex,
1035
- item: { ...item, id: itemId }
1158
+ item: wireItem,
1159
+ sequence_number: nextSeq()
1036
1160
  });
1037
1161
 
1038
1162
  if (itemType === 'message') {
@@ -1042,6 +1166,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1042
1166
  if (!block || typeof block !== 'object') continue;
1043
1167
  if (block.type !== 'output_text') continue;
1044
1168
  const text = typeof block.text === 'string' ? block.text : '';
1169
+ emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq);
1045
1170
  if (text) {
1046
1171
  writeSse(res, 'response.output_text.delta', {
1047
1172
  type: 'response.output_text.delta',
@@ -1060,13 +1185,40 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1060
1185
  text,
1061
1186
  sequence_number: nextSeq()
1062
1187
  });
1188
+ emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq);
1189
+ }
1190
+ } else if (itemType === 'function_call' || itemType === 'custom_tool_call') {
1191
+ emitResponsesToolArgumentEvents(res, wireItem, outputIndex, nextSeq);
1192
+ } else if (itemType === 'reasoning') {
1193
+ const summary = Array.isArray(item.summary) ? item.summary : [];
1194
+ for (let summaryIndex = 0; summaryIndex < summary.length; summaryIndex += 1) {
1195
+ const block = summary[summaryIndex];
1196
+ const text = block && typeof block.text === 'string' ? block.text : '';
1197
+ if (text) {
1198
+ writeSse(res, 'response.reasoning_summary_text.delta', {
1199
+ type: 'response.reasoning_summary_text.delta',
1200
+ item_id: itemId,
1201
+ output_index: outputIndex,
1202
+ summary_index: summaryIndex,
1203
+ delta: text,
1204
+ sequence_number: nextSeq()
1205
+ });
1206
+ }
1207
+ writeSse(res, 'response.reasoning_summary_text.done', {
1208
+ type: 'response.reasoning_summary_text.done',
1209
+ item_id: itemId,
1210
+ output_index: outputIndex,
1211
+ summary_index: summaryIndex,
1212
+ text,
1213
+ sequence_number: nextSeq()
1214
+ });
1063
1215
  }
1064
1216
  }
1065
1217
 
1066
1218
  writeSse(res, 'response.output_item.done', {
1067
1219
  type: 'response.output_item.done',
1068
1220
  output_index: outputIndex,
1069
- item: { ...item, id: itemId },
1221
+ item: wireItem,
1070
1222
  sequence_number: nextSeq()
1071
1223
  });
1072
1224
  }
@@ -1105,6 +1257,40 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1105
1257
  const delta = choice && choice.delta && typeof choice.delta === 'object' ? choice.delta : null;
1106
1258
  if (!delta) continue;
1107
1259
 
1260
+ const reasoningSegment = typeof delta.reasoning_content === 'string'
1261
+ ? delta.reasoning_content
1262
+ : (typeof delta.reasoning === 'string'
1263
+ ? delta.reasoning
1264
+ : (typeof delta.reasoning_text === 'string' ? delta.reasoning_text : ''));
1265
+ if (reasoningSegment) {
1266
+ if (!state.reasoningItem) {
1267
+ state.reasoningItem = {
1268
+ id: `rs_${crypto.randomBytes(8).toString('hex')}`,
1269
+ type: 'reasoning',
1270
+ summary: [{ type: 'summary_text', text: '' }]
1271
+ };
1272
+ state.output.push(state.reasoningItem);
1273
+ state.outputStarted = true;
1274
+ beginChatStreamResponsesSse(state);
1275
+ writeSse(state.res, 'response.output_item.added', {
1276
+ type: 'response.output_item.added',
1277
+ output_index: state.output.length - 1,
1278
+ item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id),
1279
+ sequence_number: state.nextSeq()
1280
+ });
1281
+ }
1282
+ state.reasoningText += reasoningSegment;
1283
+ state.reasoningItem.summary[0].text = state.reasoningText;
1284
+ writeSse(state.res, 'response.reasoning_summary_text.delta', {
1285
+ type: 'response.reasoning_summary_text.delta',
1286
+ item_id: state.reasoningItem.id,
1287
+ output_index: state.output.indexOf(state.reasoningItem),
1288
+ summary_index: 0,
1289
+ delta: reasoningSegment,
1290
+ sequence_number: state.nextSeq()
1291
+ });
1292
+ }
1293
+
1108
1294
  if (typeof delta.content === 'string' && delta.content) {
1109
1295
  if (!state.messageItem) {
1110
1296
  state.messageItem = {
@@ -1119,8 +1305,10 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1119
1305
  writeSse(state.res, 'response.output_item.added', {
1120
1306
  type: 'response.output_item.added',
1121
1307
  output_index: state.output.length - 1,
1122
- item: state.messageItem
1308
+ item: buildResponsesSseItem(state.messageItem, state.messageItem.id),
1309
+ sequence_number: state.nextSeq()
1123
1310
  });
1311
+ emitResponsesTextPartAdded(state.res, state.messageItem.id, state.output.length - 1, 0, state.nextSeq);
1124
1312
  }
1125
1313
  state.messageText += delta.content;
1126
1314
  state.messageItem.content[0].text = state.messageText;
@@ -1172,6 +1360,24 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1172
1360
  state.finished = true;
1173
1361
  stopChatStreamHeartbeat(state);
1174
1362
 
1363
+ if (state.reasoningItem) {
1364
+ const outputIndex = state.output.indexOf(state.reasoningItem);
1365
+ writeSse(state.res, 'response.reasoning_summary_text.done', {
1366
+ type: 'response.reasoning_summary_text.done',
1367
+ item_id: state.reasoningItem.id,
1368
+ output_index: outputIndex,
1369
+ summary_index: 0,
1370
+ text: state.reasoningText,
1371
+ sequence_number: state.nextSeq()
1372
+ });
1373
+ writeSse(state.res, 'response.output_item.done', {
1374
+ type: 'response.output_item.done',
1375
+ output_index: outputIndex,
1376
+ item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id),
1377
+ sequence_number: state.nextSeq()
1378
+ });
1379
+ }
1380
+
1175
1381
  if (state.messageItem) {
1176
1382
  const outputIndex = state.output.indexOf(state.messageItem);
1177
1383
  writeSse(state.res, 'response.output_text.done', {
@@ -1182,10 +1388,11 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1182
1388
  text: state.messageText,
1183
1389
  sequence_number: state.nextSeq()
1184
1390
  });
1391
+ emitResponsesTextPartDone(state.res, state.messageItem.id, outputIndex, 0, state.messageText, state.nextSeq);
1185
1392
  writeSse(state.res, 'response.output_item.done', {
1186
1393
  type: 'response.output_item.done',
1187
1394
  output_index: outputIndex,
1188
- item: state.messageItem,
1395
+ item: buildResponsesSseItem(state.messageItem, state.messageItem.id),
1189
1396
  sequence_number: state.nextSeq()
1190
1397
  });
1191
1398
  }
@@ -1200,12 +1407,14 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1200
1407
  writeSse(state.res, 'response.output_item.added', {
1201
1408
  type: 'response.output_item.added',
1202
1409
  output_index: outputIndex,
1203
- item
1410
+ item: buildResponsesSseItem(item, item.call_id),
1411
+ sequence_number: state.nextSeq()
1204
1412
  });
1413
+ emitResponsesToolArgumentEvents(state.res, buildResponsesSseItem(item, item.call_id), outputIndex, state.nextSeq);
1205
1414
  writeSse(state.res, 'response.output_item.done', {
1206
1415
  type: 'response.output_item.done',
1207
1416
  output_index: outputIndex,
1208
- item,
1417
+ item: buildResponsesSseItem(item, item.call_id),
1209
1418
  sequence_number: state.nextSeq()
1210
1419
  });
1211
1420
  }
@@ -1282,6 +1491,8 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1282
1491
  output: [],
1283
1492
  messageItem: null,
1284
1493
  messageText: '',
1494
+ reasoningItem: null,
1495
+ reasoningText: '',
1285
1496
  toolCalls: [],
1286
1497
  toolTypesByName: options.toolTypesByName || {},
1287
1498
  finished: false,
@@ -1983,7 +2194,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
1983
2194
  method: 'POST',
1984
2195
  headers: commonHeaders,
1985
2196
  timeoutMs,
1986
- body: { ...payload, stream: true },
2197
+ body: normalizeResponsesPayloadForUpstream(payload, true),
1987
2198
  res,
1988
2199
  model
1989
2200
  });
@@ -2031,7 +2242,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
2031
2242
  method: 'POST',
2032
2243
  headers: commonHeaders,
2033
2244
  timeoutMs,
2034
- body: { ...payload, stream: false }
2245
+ body: normalizeResponsesPayloadForUpstream(payload, false)
2035
2246
  });
2036
2247
 
2037
2248
  // 优先走上游 /responses(如果支持)。若上游报错且不是“端点不支持”,则直接透传错误。