codemini-cli 0.5.12 → 0.6.1

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 (32) hide show
  1. package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-B-G99D0A.js → highlighted-body-OFNGDK62-DaefZcrn.js} +1 -1
  2. package/codemini-web/dist/assets/{index-DIGUEzan.js → index-Bk_gka_s.js} +98 -93
  3. package/codemini-web/dist/assets/index-BzSHdGYY.css +2 -0
  4. package/codemini-web/dist/assets/mermaid-GHXKKRXX-BSt5tun0.js +1 -0
  5. package/codemini-web/dist/index.html +2 -2
  6. package/codemini-web/dist/logos/chatglm-color.svg +1 -0
  7. package/codemini-web/dist/logos/claude-color.svg +1 -0
  8. package/codemini-web/dist/logos/codemini-banner.png +0 -0
  9. package/codemini-web/dist/logos/codemini_banner.png +0 -0
  10. package/codemini-web/dist/logos/deepseek-color.svg +1 -0
  11. package/codemini-web/dist/logos/gemini-color.svg +1 -0
  12. package/codemini-web/dist/logos/glm-color.svg +1 -0
  13. package/codemini-web/dist/logos/google-color.svg +1 -0
  14. package/codemini-web/dist/logos/kimi-color.svg +1 -0
  15. package/codemini-web/dist/logos/minimax-color.svg +1 -0
  16. package/codemini-web/dist/logos/moonshot.svg +1 -0
  17. package/codemini-web/dist/logos/nvidia-color.svg +1 -0
  18. package/codemini-web/dist/logos/openai.svg +1 -0
  19. package/codemini-web/dist/logos/qwen-color.svg +1 -0
  20. package/codemini-web/dist/logos/zhipu-color.svg +1 -0
  21. package/codemini-web/lib/runtime-bridge.js +203 -63
  22. package/package.json +1 -1
  23. package/src/core/agent-loop.js +25 -22
  24. package/src/core/chat-runtime.js +382 -52
  25. package/src/core/config-store.js +24 -31
  26. package/src/core/provider/anthropic.js +12 -9
  27. package/src/core/provider/openai-compatible.js +80 -50
  28. package/src/core/session-store.js +63 -24
  29. package/src/core/tools.js +15 -18
  30. package/codemini-web/dist/assets/index-Dkq1DdDX.css +0 -2
  31. package/codemini-web/dist/assets/mermaid-GHXKKRXX-va2Kl89u.js +0 -1
  32. /package/codemini-web/dist/{codemini_logo.png → logos/codemini_logo.png} +0 -0
@@ -83,11 +83,283 @@ function slugify(input) {
83
83
  return base || 'untitled';
84
84
  }
85
85
 
86
- function nowStamp() {
87
- return new Date().toISOString().replace(/[:.]/g, '-');
88
- }
89
-
90
- function prioritizeByPreferredOrder(items, preferredOrder) {
86
+ function nowStamp() {
87
+ return new Date().toISOString().replace(/[:.]/g, '-');
88
+ }
89
+
90
+ function numberFromPath(obj, pathParts) {
91
+ let current = obj;
92
+ for (const part of pathParts) {
93
+ if (!current || typeof current !== 'object') return null;
94
+ current = current[part];
95
+ }
96
+ const value = Number(current);
97
+ return Number.isFinite(value) ? Math.max(0, value) : null;
98
+ }
99
+
100
+ function firstFiniteNumber(obj, paths) {
101
+ for (const pathParts of paths) {
102
+ const value = numberFromPath(obj, pathParts);
103
+ if (value != null) return value;
104
+ }
105
+ return null;
106
+ }
107
+
108
+ function sumFiniteNumbers(obj, paths) {
109
+ let sum = 0;
110
+ let found = false;
111
+ for (const pathParts of paths) {
112
+ const value = numberFromPath(obj, pathParts);
113
+ if (value != null) {
114
+ sum += value;
115
+ found = true;
116
+ }
117
+ }
118
+ return found ? sum : null;
119
+ }
120
+
121
+ function collectRawUsage(usage) {
122
+ if (!usage || typeof usage !== 'object') return [];
123
+ if (Array.isArray(usage.raw)) {
124
+ return usage.raw
125
+ .filter((item) => item && typeof item === 'object')
126
+ .map((item) => ({ ...item }));
127
+ }
128
+ return [{ ...usage }];
129
+ }
130
+
131
+ export function normalizeModelUsage(usage) {
132
+ if (!usage || typeof usage !== 'object') return null;
133
+ const promptCacheHitTokens = firstFiniteNumber(usage, [
134
+ ['prompt_cache_hit_tokens'],
135
+ ['promptCacheHitTokens'],
136
+ ['cache_hit_tokens'],
137
+ ['cacheHitTokens']
138
+ ]);
139
+ const promptCacheMissTokens = firstFiniteNumber(usage, [
140
+ ['prompt_cache_miss_tokens'],
141
+ ['promptCacheMissTokens'],
142
+ ['cache_miss_tokens'],
143
+ ['cacheMissTokens']
144
+ ]);
145
+ const explicitInputTokens = firstFiniteNumber(usage, [
146
+ ['prompt_tokens'],
147
+ ['input_tokens'],
148
+ ['inputTokens'],
149
+ ['promptTokens'],
150
+ ['prompt_token_count'],
151
+ ['promptTokenCount'],
152
+ ['input_token_count'],
153
+ ['inputTokenCount'],
154
+ ['input_total_tokens'],
155
+ ['total_input_tokens'],
156
+ ['usage', 'prompt_tokens'],
157
+ ['usage', 'input_tokens'],
158
+ ['usage_metadata', 'prompt_token_count'],
159
+ ['usage_metadata', 'input_token_count'],
160
+ ['usageMetadata', 'promptTokenCount'],
161
+ ['usageMetadata', 'inputTokenCount'],
162
+ ['token_usage', 'prompt_tokens'],
163
+ ['token_usage', 'input_tokens'],
164
+ ['tokenUsage', 'promptTokens'],
165
+ ['tokenUsage', 'inputTokens'],
166
+ ['tokens', 'input_tokens'],
167
+ ['tokens', 'inputTokens'],
168
+ ['tokens', 'prompt_tokens'],
169
+ ['tokens', 'promptTokens'],
170
+ ['billed_units', 'input_tokens'],
171
+ ['billedUnits', 'inputTokens']
172
+ ]);
173
+ const cacheReadInputTokens = firstFiniteNumber(usage, [
174
+ ['cache_read_input_tokens'],
175
+ ['cacheReadInputTokens'],
176
+ ['cache_read_tokens'],
177
+ ['cacheReadTokens']
178
+ ]);
179
+ const outputTokens = firstFiniteNumber(usage, [
180
+ ['completion_tokens'],
181
+ ['output_tokens'],
182
+ ['outputTokens'],
183
+ ['completionTokens'],
184
+ ['completion_token_count'],
185
+ ['completionTokenCount'],
186
+ ['output_token_count'],
187
+ ['outputTokenCount'],
188
+ ['candidates_token_count'],
189
+ ['candidatesTokenCount'],
190
+ ['usage', 'completion_tokens'],
191
+ ['usage', 'output_tokens'],
192
+ ['usage_metadata', 'candidates_token_count'],
193
+ ['usage_metadata', 'output_token_count'],
194
+ ['usageMetadata', 'candidatesTokenCount'],
195
+ ['usageMetadata', 'outputTokenCount'],
196
+ ['token_usage', 'completion_tokens'],
197
+ ['token_usage', 'output_tokens'],
198
+ ['tokenUsage', 'completionTokens'],
199
+ ['tokenUsage', 'outputTokens'],
200
+ ['tokens', 'output_tokens'],
201
+ ['tokens', 'outputTokens'],
202
+ ['tokens', 'completion_tokens'],
203
+ ['tokens', 'completionTokens'],
204
+ ['billed_units', 'output_tokens'],
205
+ ['billedUnits', 'outputTokens']
206
+ ]);
207
+ const explicitTotal = firstFiniteNumber(usage, [
208
+ ['total_tokens'],
209
+ ['totalTokens'],
210
+ ['total_token_count'],
211
+ ['totalTokenCount'],
212
+ ['usage', 'total_tokens'],
213
+ ['usage_metadata', 'total_token_count'],
214
+ ['usageMetadata', 'totalTokenCount'],
215
+ ['token_usage', 'total_tokens'],
216
+ ['tokenUsage', 'totalTokens'],
217
+ ['tokens', 'total_tokens'],
218
+ ['tokens', 'totalTokens']
219
+ ]);
220
+ const cachedInputTokens = firstFiniteNumber(usage, [
221
+ ['prompt_tokens_details', 'cached_tokens'],
222
+ ['input_tokens_details', 'cached_tokens'],
223
+ ['promptTokensDetails', 'cachedTokens'],
224
+ ['inputTokensDetails', 'cachedTokens'],
225
+ ['cache_read_input_tokens'],
226
+ ['cacheReadInputTokens'],
227
+ ['cache_read_tokens'],
228
+ ['cacheReadTokens'],
229
+ ['cached_tokens'],
230
+ ['cachedTokens'],
231
+ ['cached_input_tokens'],
232
+ ['cachedInputTokens'],
233
+ ['cached_content_token_count'],
234
+ ['cachedContentTokenCount'],
235
+ ['usage', 'prompt_tokens_details', 'cached_tokens'],
236
+ ['usage', 'input_tokens_details', 'cached_tokens'],
237
+ ['usage_metadata', 'cached_content_token_count'],
238
+ ['usageMetadata', 'cachedContentTokenCount'],
239
+ ['token_usage', 'prompt_tokens_details', 'cached_tokens'],
240
+ ['tokenUsage', 'promptTokensDetails', 'cachedTokens'],
241
+ ['tokens', 'cached_tokens'],
242
+ ['tokens', 'cachedTokens'],
243
+ ['prompt_cache_hit_tokens'],
244
+ ['promptCacheHitTokens'],
245
+ ['cache_hit_tokens'],
246
+ ['cacheHitTokens']
247
+ ]);
248
+ const explicitCacheMissInputTokens = firstFiniteNumber(usage, [
249
+ ['prompt_cache_miss_tokens'],
250
+ ['promptCacheMissTokens'],
251
+ ['cache_miss_tokens'],
252
+ ['cacheMissTokens']
253
+ ]);
254
+ const cacheWriteInputTokens = firstFiniteNumber(usage, [
255
+ ['cache_creation_input_tokens'],
256
+ ['cacheCreationInputTokens'],
257
+ ['cache_write_input_tokens'],
258
+ ['cacheWriteInputTokens'],
259
+ ['cache_creation_tokens'],
260
+ ['cacheCreationTokens'],
261
+ ['usage', 'cache_creation_input_tokens'],
262
+ ['usage', 'cache_write_input_tokens'],
263
+ ['token_usage', 'cache_creation_input_tokens'],
264
+ ['tokenUsage', 'cacheCreationInputTokens']
265
+ ]) ?? sumFiniteNumbers(usage, [
266
+ ['cache_creation', 'ephemeral_5m_input_tokens'],
267
+ ['cache_creation', 'ephemeral_1h_input_tokens'],
268
+ ['cacheCreation', 'ephemeral5mInputTokens'],
269
+ ['cacheCreation', 'ephemeral1hInputTokens'],
270
+ ['usage', 'cache_creation', 'ephemeral_5m_input_tokens'],
271
+ ['usage', 'cache_creation', 'ephemeral_1h_input_tokens']
272
+ ]);
273
+ const hasAnthropicSplitCacheInput = explicitInputTokens != null
274
+ && (cacheReadInputTokens != null || cacheWriteInputTokens != null)
275
+ && promptCacheHitTokens == null;
276
+ const cacheMissInputTokens = explicitCacheMissInputTokens ?? (
277
+ hasAnthropicSplitCacheInput
278
+ ? Number(explicitInputTokens || 0) + Number(cacheWriteInputTokens || 0)
279
+ : null
280
+ );
281
+ const inputTokens = explicitInputTokens != null
282
+ ? Number(explicitInputTokens || 0)
283
+ + (hasAnthropicSplitCacheInput ? Number(cacheReadInputTokens || 0) + Number(cacheWriteInputTokens || 0) : 0)
284
+ : (
285
+ promptCacheHitTokens != null || promptCacheMissTokens != null
286
+ ? Number(promptCacheHitTokens || 0) + Number(promptCacheMissTokens || 0)
287
+ : null
288
+ );
289
+ const reasoningOutputTokens = firstFiniteNumber(usage, [
290
+ ['completion_tokens_details', 'reasoning_tokens'],
291
+ ['output_tokens_details', 'reasoning_tokens'],
292
+ ['completionTokensDetails', 'reasoningTokens'],
293
+ ['outputTokensDetails', 'reasoningTokens'],
294
+ ['reasoning_tokens'],
295
+ ['reasoningTokens'],
296
+ ['thoughts_token_count'],
297
+ ['thoughtsTokenCount'],
298
+ ['usage', 'completion_tokens_details', 'reasoning_tokens'],
299
+ ['usage_metadata', 'thoughts_token_count'],
300
+ ['usageMetadata', 'thoughtsTokenCount']
301
+ ]);
302
+ const totalTokens = explicitTotal ?? (
303
+ inputTokens != null || outputTokens != null
304
+ ? Number(inputTokens || 0) + Number(outputTokens || 0)
305
+ : null
306
+ );
307
+ if (
308
+ inputTokens == null &&
309
+ outputTokens == null &&
310
+ totalTokens == null &&
311
+ cachedInputTokens == null &&
312
+ cacheWriteInputTokens == null
313
+ ) {
314
+ return null;
315
+ }
316
+ return {
317
+ inputTokens: Math.round(inputTokens || 0),
318
+ outputTokens: Math.round(outputTokens || 0),
319
+ totalTokens: Math.round(totalTokens || 0),
320
+ cachedInputTokens: Math.round(cachedInputTokens || 0),
321
+ cacheMissInputTokens: Math.round(cacheMissInputTokens || 0),
322
+ cacheWriteInputTokens: Math.round(cacheWriteInputTokens || 0),
323
+ reasoningOutputTokens: Math.round(reasoningOutputTokens || 0),
324
+ requests: 1,
325
+ raw: collectRawUsage(usage)
326
+ };
327
+ }
328
+
329
+ function cloneModelUsage(usage) {
330
+ if (!usage || typeof usage !== 'object') return null;
331
+ return {
332
+ inputTokens: Math.max(0, Math.round(Number(usage.inputTokens || 0))),
333
+ outputTokens: Math.max(0, Math.round(Number(usage.outputTokens || 0))),
334
+ totalTokens: Math.max(0, Math.round(Number(usage.totalTokens || 0))),
335
+ cachedInputTokens: Math.max(0, Math.round(Number(usage.cachedInputTokens || 0))),
336
+ cacheMissInputTokens: Math.max(0, Math.round(Number(usage.cacheMissInputTokens || 0))),
337
+ cacheWriteInputTokens: Math.max(0, Math.round(Number(usage.cacheWriteInputTokens || 0))),
338
+ reasoningOutputTokens: Math.max(0, Math.round(Number(usage.reasoningOutputTokens || 0))),
339
+ requests: Math.max(0, Math.round(Number(usage.requests || 0))),
340
+ raw: Array.isArray(usage.raw) ? usage.raw.map((item) => ({ ...item })) : []
341
+ };
342
+ }
343
+
344
+ function mergeModelUsage(left, right) {
345
+ const a = cloneModelUsage(left);
346
+ const b = cloneModelUsage(right);
347
+ if (!a) return b;
348
+ if (!b) return a;
349
+ return {
350
+ inputTokens: a.inputTokens + b.inputTokens,
351
+ outputTokens: a.outputTokens + b.outputTokens,
352
+ totalTokens: a.totalTokens + b.totalTokens,
353
+ cachedInputTokens: a.cachedInputTokens + b.cachedInputTokens,
354
+ cacheMissInputTokens: a.cacheMissInputTokens + b.cacheMissInputTokens,
355
+ cacheWriteInputTokens: a.cacheWriteInputTokens + b.cacheWriteInputTokens,
356
+ reasoningOutputTokens: a.reasoningOutputTokens + b.reasoningOutputTokens,
357
+ requests: a.requests + b.requests,
358
+ raw: [...a.raw, ...b.raw]
359
+ };
360
+ }
361
+
362
+ function prioritizeByPreferredOrder(items, preferredOrder) {
91
363
  const source = Array.isArray(items) ? items : [];
92
364
  const priorities = new Map((Array.isArray(preferredOrder) ? preferredOrder : []).map((value, index) => [value, index]));
93
365
  return [...source].sort((left, right) => {
@@ -2885,30 +3157,64 @@ async function askModel({
2885
3157
  session.messages.push(stampedMessage('assistant', ''));
2886
3158
  activeAssistantIndex = session.messages.length - 1;
2887
3159
  if (persistSession) scheduleSessionSave();
2888
- } else if (event?.type === 'assistant:delta') {
2889
- if (activeAssistantIndex >= 0 && session.messages[activeAssistantIndex]) {
2890
- const current = session.messages[activeAssistantIndex];
2891
- current.content = `${current.content || ''}${event.text || ''}`;
2892
- current.at = new Date().toISOString();
2893
- if (persistSession) scheduleSessionSave();
2894
- }
2895
- } else if (event?.type === 'assistant:response') {
2896
- if (activeAssistantIndex >= 0 && session.messages[activeAssistantIndex]) {
2897
- const current = session.messages[activeAssistantIndex];
2898
- current.content = event.assistantMessage?.content ?? event.text ?? current.content;
2899
- if (typeof event.assistantMessage?.reasoning_content === 'string' && event.assistantMessage.reasoning_content) {
2900
- current.reasoning_content = event.assistantMessage.reasoning_content;
2901
- }
3160
+ } else if (event?.type === 'assistant:delta') {
3161
+ if (activeAssistantIndex >= 0 && session.messages[activeAssistantIndex]) {
3162
+ const current = session.messages[activeAssistantIndex];
3163
+ const now = new Date();
3164
+ if (current.reasoning_started_at && !current.reasoning_ended_at) {
3165
+ current.reasoning_ended_at = now.toISOString();
3166
+ current.reasoning_duration_ms = Math.max(
3167
+ Number(current.reasoning_duration_ms || 0),
3168
+ Date.parse(current.reasoning_ended_at) - Date.parse(current.reasoning_started_at)
3169
+ );
3170
+ }
3171
+ current.content = `${current.content || ''}${event.text || ''}`;
3172
+ current.at = now.toISOString();
3173
+ if (persistSession) scheduleSessionSave();
3174
+ }
3175
+ } else if (event?.type === 'assistant:reasoning_delta') {
3176
+ if (activeAssistantIndex >= 0 && session.messages[activeAssistantIndex]) {
3177
+ const current = session.messages[activeAssistantIndex];
3178
+ const now = new Date();
3179
+ if (!current.reasoning_started_at) current.reasoning_started_at = now.toISOString();
3180
+ current.reasoning_content = `${current.reasoning_content || ''}${event.text || ''}`;
3181
+ current.reasoning_duration_ms = Math.max(
3182
+ 0,
3183
+ now.getTime() - Date.parse(current.reasoning_started_at)
3184
+ );
3185
+ current.at = now.toISOString();
3186
+ if (persistSession) scheduleSessionSave();
3187
+ }
3188
+ } else if (event?.type === 'assistant:response') {
3189
+ const eventUsage = normalizeModelUsage(event.usage || event.assistantMessage?.usage);
3190
+ if (eventUsage) event.usage = eventUsage;
3191
+ if (activeAssistantIndex >= 0 && session.messages[activeAssistantIndex]) {
3192
+ const current = session.messages[activeAssistantIndex];
3193
+ const now = new Date();
3194
+ current.content = event.assistantMessage?.content ?? event.text ?? current.content;
3195
+ if (typeof event.assistantMessage?.reasoning_content === 'string' && event.assistantMessage.reasoning_content) {
3196
+ current.reasoning_content = event.assistantMessage.reasoning_content;
3197
+ }
2902
3198
  if (Array.isArray(event.assistantMessage?.reasoning_details) && event.assistantMessage.reasoning_details.length > 0) {
2903
3199
  current.reasoning_details = event.assistantMessage.reasoning_details;
2904
3200
  }
2905
- if (Array.isArray(event.assistantMessage?.tool_calls) && event.assistantMessage.tool_calls.length > 0) {
2906
- current.tool_calls = event.assistantMessage.tool_calls;
2907
- }
2908
- current.at = new Date().toISOString();
2909
- if (persistSession) scheduleSessionSave();
2910
- } else {
2911
- const assistantMessage = event.assistantMessage && typeof event.assistantMessage === 'object'
3201
+ if (Array.isArray(event.assistantMessage?.tool_calls) && event.assistantMessage.tool_calls.length > 0) {
3202
+ current.tool_calls = event.assistantMessage.tool_calls;
3203
+ }
3204
+ if (eventUsage) {
3205
+ current.usage = mergeModelUsage(current.usage, eventUsage);
3206
+ }
3207
+ if ((current.reasoning_content || current.reasoning_details) && current.reasoning_started_at) {
3208
+ current.reasoning_ended_at = current.reasoning_ended_at || now.toISOString();
3209
+ current.reasoning_duration_ms = Math.max(
3210
+ Number(current.reasoning_duration_ms || 0),
3211
+ Date.parse(current.reasoning_ended_at) - Date.parse(current.reasoning_started_at)
3212
+ );
3213
+ }
3214
+ current.at = now.toISOString();
3215
+ if (persistSession) scheduleSessionSave();
3216
+ } else {
3217
+ const assistantMessage = event.assistantMessage && typeof event.assistantMessage === 'object'
2912
3218
  ? event.assistantMessage
2913
3219
  : { content: event.text || '' };
2914
3220
  session.messages.push(stampedMessage('assistant', assistantMessage.content || event.text || '', {
@@ -2918,14 +3224,29 @@ async function askModel({
2918
3224
  ...(Array.isArray(assistantMessage.reasoning_details) && assistantMessage.reasoning_details.length > 0
2919
3225
  ? { reasoning_details: assistantMessage.reasoning_details }
2920
3226
  : {}),
2921
- ...(Array.isArray(assistantMessage.tool_calls) && assistantMessage.tool_calls.length > 0
2922
- ? { tool_calls: assistantMessage.tool_calls }
2923
- : {})
2924
- }));
2925
- if (persistSession) scheduleSessionSave();
2926
- }
3227
+ ...(Array.isArray(assistantMessage.tool_calls) && assistantMessage.tool_calls.length > 0
3228
+ ? { tool_calls: assistantMessage.tool_calls }
3229
+ : {}),
3230
+ ...(eventUsage ? { usage: eventUsage } : {})
3231
+ }));
3232
+ if (persistSession) scheduleSessionSave();
3233
+ }
2927
3234
  activeAssistantIndex = -1;
2928
- } else if (event?.type === 'tool:end' || event?.type === 'tool:error' || event?.type === 'tool:blocked') {
3235
+ } else if (event?.type === 'tool:start') {
3236
+ if (activeAssistantIndex >= 0 && session.messages[activeAssistantIndex]) {
3237
+ const current = session.messages[activeAssistantIndex];
3238
+ const now = new Date();
3239
+ if (current.reasoning_started_at && !current.reasoning_ended_at) {
3240
+ current.reasoning_ended_at = now.toISOString();
3241
+ current.reasoning_duration_ms = Math.max(
3242
+ Number(current.reasoning_duration_ms || 0),
3243
+ Date.parse(current.reasoning_ended_at) - Date.parse(current.reasoning_started_at)
3244
+ );
3245
+ current.at = now.toISOString();
3246
+ if (persistSession) scheduleSessionSave();
3247
+ }
3248
+ }
3249
+ } else if (event?.type === 'tool:end' || event?.type === 'tool:error' || event?.type === 'tool:blocked') {
2929
3250
  const toolId = String(event.id || '');
2930
3251
  if (toolId) {
2931
3252
  const meta = {
@@ -3000,11 +3321,15 @@ async function askModel({
3000
3321
  timeoutMs: config.gateway.timeout_ms || 1800000,
3001
3322
  maxRetries: config.gateway.max_retries ?? 2,
3002
3323
  signal,
3003
- onTextDelta: (delta) => {
3004
- startAssistantStream();
3005
- wrappedAgentEvent({ type: 'assistant:delta', text: delta });
3006
- },
3007
- onToolCallDelta: (toolCall) => {
3324
+ onTextDelta: (delta) => {
3325
+ startAssistantStream();
3326
+ wrappedAgentEvent({ type: 'assistant:delta', text: delta });
3327
+ },
3328
+ onReasoningDelta: (delta) => {
3329
+ startAssistantStream();
3330
+ wrappedAgentEvent({ type: 'assistant:reasoning_delta', text: delta });
3331
+ },
3332
+ onToolCallDelta: (toolCall) => {
3008
3333
  startAssistantStream();
3009
3334
  wrappedAgentEvent({ type: 'assistant:tool_call_delta', toolCall });
3010
3335
  }
@@ -3131,7 +3456,7 @@ async function runSubAgentTask({
3131
3456
  }
3132
3457
  if (
3133
3458
  role !== 'summarizer' &&
3134
- ['assistant:start', 'assistant:delta', 'assistant:response', 'assistant:tool_call_delta'].includes(String(evt?.type || ''))
3459
+ ['assistant:start', 'assistant:delta', 'assistant:reasoning_delta', 'assistant:response', 'assistant:tool_call_delta'].includes(String(evt?.type || ''))
3135
3460
  ) {
3136
3461
  return;
3137
3462
  }
@@ -3171,14 +3496,18 @@ async function runSubAgentTask({
3171
3496
  };
3172
3497
  }
3173
3498
 
3174
- function buildPlanStepTranscript({ stepRecord, stepIndex, totalSteps, messages }) {
3175
- const toolCardsById = new Map();
3176
- const toolCards = [];
3177
- const source = Array.isArray(messages) ? messages : [];
3178
-
3179
- for (const msg of source) {
3180
- if (msg?.role === 'assistant' && Array.isArray(msg.tool_calls)) {
3181
- for (const tc of msg.tool_calls) {
3499
+ function buildPlanStepTranscript({ stepRecord, stepIndex, totalSteps, messages }) {
3500
+ const toolCardsById = new Map();
3501
+ const toolCards = [];
3502
+ const source = Array.isArray(messages) ? messages : [];
3503
+ let usage = null;
3504
+
3505
+ for (const msg of source) {
3506
+ if (msg?.role === 'assistant' && msg.usage) {
3507
+ usage = mergeModelUsage(usage, msg.usage);
3508
+ }
3509
+ if (msg?.role === 'assistant' && Array.isArray(msg.tool_calls)) {
3510
+ for (const tc of msg.tool_calls) {
3182
3511
  const id = String(tc?.id || `tool-${toolCards.length + 1}`);
3183
3512
  if (toolCardsById.has(id)) continue;
3184
3513
  const card = {
@@ -3217,11 +3546,12 @@ function buildPlanStepTranscript({ stepRecord, stepIndex, totalSteps, messages }
3217
3546
  total: totalSteps,
3218
3547
  role: stepRecord.role || 'general',
3219
3548
  title: stepRecord.title || '',
3220
- status: stepRecord.failed ? 'failed' : 'done',
3221
- summary: stepRecord.failed ? stepRecord.failureReason : trimInline(stepRecord.output || '', 160),
3222
- segments
3223
- };
3224
- }
3549
+ status: stepRecord.failed ? 'failed' : 'done',
3550
+ summary: stepRecord.failed ? stepRecord.failureReason : trimInline(stepRecord.output || '', 160),
3551
+ segments,
3552
+ ...(usage ? { usage } : {})
3553
+ };
3554
+ }
3225
3555
 
3226
3556
  async function executePlanWithSubAgents({
3227
3557
  planState,
@@ -40,22 +40,18 @@ const DEFAULT_CONFIG = {
40
40
  project_instructions_enabled: true,
41
41
  project_instructions_max_chars: 12000
42
42
  },
43
- execution: {
44
- mode: 'normal',
45
- always_allow_tools: [
46
- 'read',
47
- 'grep',
48
- 'glob',
49
- 'list',
50
- 'edit',
51
- 'write',
52
- 'run',
53
- 'list_background_tasks',
54
- 'get_background_task',
55
- 'stop_background_task'
56
- ],
57
- max_steps: 16
58
- },
43
+ execution: {
44
+ mode: 'normal',
45
+ always_allow_tools: [
46
+ 'read',
47
+ 'grep',
48
+ 'glob',
49
+ 'list',
50
+ 'list_background_tasks',
51
+ 'get_background_task'
52
+ ],
53
+ max_steps: 16
54
+ },
59
55
  sessions: {
60
56
  max_sessions: 100,
61
57
  retention_days: 30
@@ -158,21 +154,18 @@ function normalizePolicyLists(config) {
158
154
  const rawTools = Array.isArray(next.execution.always_allow_tools)
159
155
  ? next.execution.always_allow_tools
160
156
  : [];
161
- next.execution.always_allow_tools = uniqueStrings(
162
- [
163
- 'read',
164
- 'grep',
165
- 'glob',
166
- 'list',
167
- 'edit',
168
- 'write',
169
- 'run',
170
- 'list_background_tasks',
171
- 'get_background_task',
172
- 'stop_background_task',
173
- ...rawTools
174
- ].filter((name) => String(name) !== 'list_files')
175
- );
157
+ next.execution.always_allow_tools = uniqueStrings(
158
+ [
159
+ 'read',
160
+ 'grep',
161
+ 'glob',
162
+ 'list',
163
+ 'list_background_tasks',
164
+ 'get_background_task',
165
+ ...rawTools
166
+ ].filter((name) => String(name) !== 'list_files')
167
+ .filter((name) => !['edit', 'write', 'delete', 'run', 'stop_background_task'].includes(String(name)))
168
+ );
176
169
  next.ui = next.ui || {};
177
170
  next.ui.language = normalizeUiLanguage(next.ui.language);
178
171
  next.ui.reply_language = normalizeReplyLanguage(next.ui.reply_language);
@@ -403,9 +403,10 @@ export async function createChatCompletionStream({
403
403
  model,
404
404
  messages,
405
405
  temperature = 0.2,
406
- tools,
407
- onTextDelta,
408
- onToolCallDelta,
406
+ tools,
407
+ onTextDelta,
408
+ onReasoningDelta,
409
+ onToolCallDelta,
409
410
  timeoutMs = 1800000,
410
411
  maxTokens = 4096,
411
412
  signal: externalSignal
@@ -476,12 +477,14 @@ export async function createChatCompletionStream({
476
477
  continue;
477
478
  }
478
479
 
479
- if (delta.type === 'thinking_delta') {
480
- const current = thinkingBlocksByIndex.get(index) || { type: 'thinking', thinking: '' };
481
- current.thinking = `${current.thinking || ''}${String(delta.thinking || '')}`;
482
- thinkingBlocksByIndex.set(index, current);
483
- continue;
484
- }
480
+ if (delta.type === 'thinking_delta') {
481
+ const current = thinkingBlocksByIndex.get(index) || { type: 'thinking', thinking: '' };
482
+ const thinkingDelta = String(delta.thinking || '');
483
+ current.thinking = `${current.thinking || ''}${thinkingDelta}`;
484
+ thinkingBlocksByIndex.set(index, current);
485
+ if (thinkingDelta && onReasoningDelta) onReasoningDelta(thinkingDelta);
486
+ continue;
487
+ }
485
488
 
486
489
  if (delta.type === 'signature_delta') {
487
490
  const current = thinkingBlocksByIndex.get(index) || { type: 'thinking', thinking: '' };