codemini-cli 0.4.1 → 0.4.2
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/OPERATIONS.md +4 -2
- package/README.md +83 -5
- package/deployment.md +14 -7
- package/package.json +1 -2
- package/src/cli.js +1 -1
- package/src/commands/skill.js +145 -53
- package/src/core/agent-loop.js +1 -206
- package/src/core/chat-runtime.js +306 -53
- package/src/core/command-loader.js +12 -5
- package/src/core/context-compact.js +2 -1
- package/src/core/dream-audit.js +12 -0
- package/src/core/dream-consolidate.js +131 -59
- package/src/core/dream-evaluator.js +86 -0
- package/src/core/fff-adapter.js +1 -1
- package/src/core/memory-store.js +145 -10
- package/src/core/reflect-skill.js +178 -0
- package/src/core/tool-result-store.js +206 -0
- package/src/core/tools.js +126 -30
- package/src/tui/chat-app.js +247 -27
- package/src/core/provider/anthropic.sdk-backup.js +0 -439
- package/src/core/provider/openai-compatible.sdk-backup.js +0 -412
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
2
|
-
|
|
3
|
-
function extractTextContent(content) {
|
|
4
|
-
if (typeof content === 'string') return content;
|
|
5
|
-
if (Array.isArray(content)) {
|
|
6
|
-
return content
|
|
7
|
-
.map((part) => {
|
|
8
|
-
if (typeof part === 'string') return part;
|
|
9
|
-
if (part?.type === 'text') return part.text || '';
|
|
10
|
-
return '';
|
|
11
|
-
})
|
|
12
|
-
.join('');
|
|
13
|
-
}
|
|
14
|
-
return '';
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function extractReasoningText(details) {
|
|
18
|
-
const source = Array.isArray(details) ? details : [];
|
|
19
|
-
return source
|
|
20
|
-
.map((detail) => {
|
|
21
|
-
if (typeof detail === 'string') return detail;
|
|
22
|
-
return typeof detail?.text === 'string' ? detail.text : '';
|
|
23
|
-
})
|
|
24
|
-
.join('');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function normalizeReasoningDetails(details) {
|
|
28
|
-
if (!Array.isArray(details) || details.length === 0) return undefined;
|
|
29
|
-
const normalized = details
|
|
30
|
-
.map((detail) => {
|
|
31
|
-
if (!detail || typeof detail !== 'object') return null;
|
|
32
|
-
return { ...detail };
|
|
33
|
-
})
|
|
34
|
-
.filter(Boolean);
|
|
35
|
-
return normalized.length > 0 ? normalized : undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function emptyToolCall(index) {
|
|
39
|
-
return {
|
|
40
|
-
index,
|
|
41
|
-
id: '',
|
|
42
|
-
name: '',
|
|
43
|
-
arguments: ''
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function isMiniMaxModel(model) {
|
|
48
|
-
return String(model || '').toLowerCase().includes('minimax');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function normalizeToolCallArguments(argumentsText) {
|
|
52
|
-
const raw = typeof argumentsText === 'string' ? argumentsText : JSON.stringify(argumentsText ?? {});
|
|
53
|
-
try {
|
|
54
|
-
const parsed = JSON.parse(raw);
|
|
55
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
56
|
-
return JSON.stringify(parsed);
|
|
57
|
-
}
|
|
58
|
-
} catch {}
|
|
59
|
-
return '{}';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function normalizeIncomingToolCallArguments(argumentsValue) {
|
|
63
|
-
if (typeof argumentsValue === 'string') return argumentsValue;
|
|
64
|
-
if (argumentsValue == null) return '{}';
|
|
65
|
-
try {
|
|
66
|
-
return JSON.stringify(argumentsValue);
|
|
67
|
-
} catch {
|
|
68
|
-
return '{}';
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function sanitizeGatewayMessages(messages) {
|
|
73
|
-
const source = Array.isArray(messages) ? messages : [];
|
|
74
|
-
return source
|
|
75
|
-
.filter((message) => message && typeof message === 'object')
|
|
76
|
-
.map((message) => {
|
|
77
|
-
if (!Array.isArray(message.tool_calls) || message.tool_calls.length === 0) {
|
|
78
|
-
return message;
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
...message,
|
|
82
|
-
tool_calls: message.tool_calls.map((toolCall) => ({
|
|
83
|
-
...toolCall,
|
|
84
|
-
function: {
|
|
85
|
-
...toolCall?.function,
|
|
86
|
-
arguments: normalizeToolCallArguments(toolCall?.function?.arguments)
|
|
87
|
-
}
|
|
88
|
-
}))
|
|
89
|
-
};
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function sanitizeMiniMaxMessages(messages) {
|
|
94
|
-
const source = Array.isArray(messages) ? messages : [];
|
|
95
|
-
const out = [];
|
|
96
|
-
let seenNonSystem = false;
|
|
97
|
-
let keptLeadingSystem = false;
|
|
98
|
-
|
|
99
|
-
for (const message of source) {
|
|
100
|
-
if (!message || typeof message !== 'object') continue;
|
|
101
|
-
if (message.role === 'system') {
|
|
102
|
-
if (!seenNonSystem && !keptLeadingSystem) {
|
|
103
|
-
out.push(message);
|
|
104
|
-
keptLeadingSystem = true;
|
|
105
|
-
} else {
|
|
106
|
-
out.push({
|
|
107
|
-
role: 'user',
|
|
108
|
-
content: `[system-note]\n${extractTextContent(message.content)}`
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
seenNonSystem = true;
|
|
114
|
-
out.push(message);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return out;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function buildPayload({ model, temperature, messages, tools, stream = false }) {
|
|
121
|
-
const sanitizedMessages = sanitizeGatewayMessages(messages);
|
|
122
|
-
const payload = {
|
|
123
|
-
model,
|
|
124
|
-
temperature,
|
|
125
|
-
messages: isMiniMaxModel(model) ? sanitizeMiniMaxMessages(sanitizedMessages) : sanitizedMessages
|
|
126
|
-
};
|
|
127
|
-
if (stream) payload.stream = true;
|
|
128
|
-
if (Array.isArray(tools) && tools.length > 0) {
|
|
129
|
-
payload.tools = tools;
|
|
130
|
-
payload.tool_choice = 'auto';
|
|
131
|
-
}
|
|
132
|
-
if (isMiniMaxModel(model)) {
|
|
133
|
-
payload.extra_body = { reasoning_split: true };
|
|
134
|
-
}
|
|
135
|
-
return payload;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function hasTrailingToolContext(messages) {
|
|
139
|
-
const source = Array.isArray(messages) ? messages : [];
|
|
140
|
-
for (let index = source.length - 1; index >= 0; index -= 1) {
|
|
141
|
-
const message = source[index];
|
|
142
|
-
if (!message || typeof message !== 'object') continue;
|
|
143
|
-
if (message.role === 'tool') return true;
|
|
144
|
-
if (message.role === 'assistant' || message.role === 'user') return false;
|
|
145
|
-
}
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function stripMiniMaxThinkContent(text) {
|
|
150
|
-
const input = String(text || '');
|
|
151
|
-
if (!input) return '';
|
|
152
|
-
|
|
153
|
-
let cursor = 0;
|
|
154
|
-
let out = '';
|
|
155
|
-
let removedThink = false;
|
|
156
|
-
|
|
157
|
-
while (cursor < input.length) {
|
|
158
|
-
const openIdx = input.indexOf('<think>', cursor);
|
|
159
|
-
const closeIdx = input.indexOf('</think>', cursor);
|
|
160
|
-
|
|
161
|
-
if (openIdx === -1 && closeIdx === -1) {
|
|
162
|
-
out += input.slice(cursor);
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (closeIdx !== -1 && (openIdx === -1 || closeIdx < openIdx)) {
|
|
167
|
-
removedThink = true;
|
|
168
|
-
cursor = closeIdx + '</think>'.length;
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
out += input.slice(cursor, openIdx);
|
|
173
|
-
const closingTagIdx = input.indexOf('</think>', openIdx + '<think>'.length);
|
|
174
|
-
removedThink = true;
|
|
175
|
-
if (closingTagIdx === -1) {
|
|
176
|
-
cursor = input.length;
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
cursor = closingTagIdx + '</think>'.length;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return removedThink ? out.trimStart() : out;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function sanitizeMiniMaxText(model, text) {
|
|
186
|
-
return isMiniMaxModel(model) ? stripMiniMaxThinkContent(text) : text;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function nextMiniMaxVisibleChunk(state, content) {
|
|
190
|
-
const rawChunk = extractTextContent(content);
|
|
191
|
-
if (!rawChunk) {
|
|
192
|
-
return { textDelta: '', nextState: state };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const nextRawContent = rawChunk.startsWith(state.rawContent) ? rawChunk : `${state.rawContent}${rawChunk}`;
|
|
196
|
-
const nextVisibleText = stripMiniMaxThinkContent(nextRawContent);
|
|
197
|
-
const textDelta = nextVisibleText.startsWith(state.visibleText)
|
|
198
|
-
? nextVisibleText.slice(state.visibleText.length)
|
|
199
|
-
: nextVisibleText;
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
textDelta,
|
|
203
|
-
nextState: {
|
|
204
|
-
rawContent: nextRawContent,
|
|
205
|
-
visibleText: nextVisibleText
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function buildAssistantMessage({ text = '', toolCalls = [], content, reasoningDetails }) {
|
|
211
|
-
const assistantMessage = {
|
|
212
|
-
role: 'assistant',
|
|
213
|
-
content: content ?? text
|
|
214
|
-
};
|
|
215
|
-
const normalizedReasoningDetails = normalizeReasoningDetails(reasoningDetails);
|
|
216
|
-
if (normalizedReasoningDetails) {
|
|
217
|
-
assistantMessage.reasoning_details = normalizedReasoningDetails;
|
|
218
|
-
assistantMessage.reasoning_content = extractReasoningText(normalizedReasoningDetails);
|
|
219
|
-
}
|
|
220
|
-
if (Array.isArray(toolCalls) && toolCalls.length > 0) {
|
|
221
|
-
assistantMessage.tool_calls = toolCalls.map((tc) => ({
|
|
222
|
-
id: tc.id,
|
|
223
|
-
type: 'function',
|
|
224
|
-
function: {
|
|
225
|
-
name: tc.name,
|
|
226
|
-
arguments: tc.arguments || '{}'
|
|
227
|
-
}
|
|
228
|
-
}));
|
|
229
|
-
}
|
|
230
|
-
return assistantMessage;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function createClient({ baseUrl, apiKey, timeoutMs = 90000, maxRetries = 2 }) {
|
|
234
|
-
return new OpenAI({
|
|
235
|
-
apiKey,
|
|
236
|
-
baseURL: String(baseUrl || '').replace(/\/$/, ''),
|
|
237
|
-
timeout: timeoutMs,
|
|
238
|
-
maxRetries
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function buildFinalStreamResult({ text, toolCallsByIndex, usage, messages, reasoningDetails }) {
|
|
243
|
-
const toolCalls = Array.from(toolCallsByIndex.entries())
|
|
244
|
-
.sort((a, b) => a[0] - b[0])
|
|
245
|
-
.map(([, tc], i) => ({
|
|
246
|
-
id: tc.id || `tc-${i + 1}`,
|
|
247
|
-
name: tc.name,
|
|
248
|
-
arguments: tc.arguments || '{}'
|
|
249
|
-
}))
|
|
250
|
-
.filter((tc) => tc.name);
|
|
251
|
-
const normalizedText = String(text || '').trim();
|
|
252
|
-
|
|
253
|
-
if (!normalizedText && toolCalls.length === 0) {
|
|
254
|
-
if (hasTrailingToolContext(messages)) {
|
|
255
|
-
return {
|
|
256
|
-
text: '',
|
|
257
|
-
toolCalls: [],
|
|
258
|
-
usage,
|
|
259
|
-
incomplete: true
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
throw new Error('Gateway stream returned empty assistant response');
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
text,
|
|
267
|
-
toolCalls,
|
|
268
|
-
usage,
|
|
269
|
-
incomplete: false,
|
|
270
|
-
assistantMessage: buildAssistantMessage({
|
|
271
|
-
text,
|
|
272
|
-
toolCalls,
|
|
273
|
-
reasoningDetails
|
|
274
|
-
})
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export async function createChatCompletion({
|
|
279
|
-
baseUrl,
|
|
280
|
-
apiKey,
|
|
281
|
-
model,
|
|
282
|
-
messages,
|
|
283
|
-
temperature = 0.2,
|
|
284
|
-
tools,
|
|
285
|
-
timeoutMs = 90000,
|
|
286
|
-
maxRetries = 2
|
|
287
|
-
}) {
|
|
288
|
-
const client = createClient({ baseUrl, apiKey, timeoutMs, maxRetries });
|
|
289
|
-
const response = await client.chat.completions.create(buildPayload({ model, temperature, messages, tools }));
|
|
290
|
-
const message = response?.choices?.[0]?.message || {};
|
|
291
|
-
const text = sanitizeMiniMaxText(model, extractTextContent(message.content));
|
|
292
|
-
const toolCalls = (message.tool_calls || []).map((tc) => ({
|
|
293
|
-
id: tc.id,
|
|
294
|
-
name: tc.function?.name,
|
|
295
|
-
arguments: normalizeIncomingToolCallArguments(tc.function?.arguments)
|
|
296
|
-
}));
|
|
297
|
-
const normalizedText = String(text || '').trim();
|
|
298
|
-
|
|
299
|
-
if (!normalizedText && toolCalls.length === 0) {
|
|
300
|
-
if (hasTrailingToolContext(messages)) {
|
|
301
|
-
return {
|
|
302
|
-
text: '',
|
|
303
|
-
toolCalls: [],
|
|
304
|
-
usage: response?.usage || null,
|
|
305
|
-
incomplete: true
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
throw new Error('Gateway returned empty assistant response');
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
text,
|
|
313
|
-
toolCalls,
|
|
314
|
-
usage: response?.usage || null,
|
|
315
|
-
assistantMessage: buildAssistantMessage({
|
|
316
|
-
text,
|
|
317
|
-
toolCalls,
|
|
318
|
-
content: message.content ?? text,
|
|
319
|
-
reasoningDetails: message.reasoning_details
|
|
320
|
-
})
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export async function createChatCompletionStream({
|
|
325
|
-
baseUrl,
|
|
326
|
-
apiKey,
|
|
327
|
-
model,
|
|
328
|
-
messages,
|
|
329
|
-
temperature = 0.2,
|
|
330
|
-
tools,
|
|
331
|
-
onTextDelta,
|
|
332
|
-
onToolCallDelta,
|
|
333
|
-
timeoutMs = 90000,
|
|
334
|
-
maxRetries = 2
|
|
335
|
-
}) {
|
|
336
|
-
const client = createClient({ baseUrl, apiKey, timeoutMs, maxRetries });
|
|
337
|
-
const stream = await client.chat.completions.create(buildPayload({ model, temperature, messages, tools, stream: true }));
|
|
338
|
-
|
|
339
|
-
let text = '';
|
|
340
|
-
let usage = null;
|
|
341
|
-
const toolCallsByIndex = new Map();
|
|
342
|
-
let miniMaxStreamState = { rawContent: '', visibleText: '' };
|
|
343
|
-
let reasoningDetails = [];
|
|
344
|
-
let previousReasoningText = '';
|
|
345
|
-
|
|
346
|
-
for await (const chunk of stream) {
|
|
347
|
-
usage = chunk?.usage || usage;
|
|
348
|
-
const choice0 = chunk?.choices?.[0] || {};
|
|
349
|
-
const delta = choice0?.delta || {};
|
|
350
|
-
const content = delta.content;
|
|
351
|
-
const nextReasoningDetails = normalizeReasoningDetails(delta.reasoning_details);
|
|
352
|
-
if (nextReasoningDetails) {
|
|
353
|
-
const nextReasoningText = extractReasoningText(nextReasoningDetails);
|
|
354
|
-
if (nextReasoningText.length >= previousReasoningText.length) {
|
|
355
|
-
previousReasoningText = nextReasoningText;
|
|
356
|
-
} else {
|
|
357
|
-
previousReasoningText += nextReasoningText;
|
|
358
|
-
}
|
|
359
|
-
reasoningDetails = previousReasoningText ? [{ type: 'reasoning', text: previousReasoningText }] : reasoningDetails;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (isMiniMaxModel(model)) {
|
|
363
|
-
const next = nextMiniMaxVisibleChunk(miniMaxStreamState, content);
|
|
364
|
-
miniMaxStreamState = next.nextState;
|
|
365
|
-
if (next.textDelta) {
|
|
366
|
-
text += next.textDelta;
|
|
367
|
-
if (onTextDelta) onTextDelta(next.textDelta);
|
|
368
|
-
}
|
|
369
|
-
} else if (typeof content === 'string' && content.length > 0) {
|
|
370
|
-
text += content;
|
|
371
|
-
if (onTextDelta) onTextDelta(content);
|
|
372
|
-
} else if (Array.isArray(content) && content.length > 0) {
|
|
373
|
-
const chunkText = extractTextContent(content);
|
|
374
|
-
if (chunkText) {
|
|
375
|
-
text += chunkText;
|
|
376
|
-
if (onTextDelta) onTextDelta(chunkText);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
381
|
-
for (const td of toolDeltas) {
|
|
382
|
-
const idx = typeof td.index === 'number' ? td.index : 0;
|
|
383
|
-
const current = toolCallsByIndex.get(idx) || emptyToolCall(idx);
|
|
384
|
-
if (td.id) current.id = td.id;
|
|
385
|
-
if (td.function?.name) current.name = `${current.name}${td.function.name}`;
|
|
386
|
-
if (td.function?.arguments !== undefined) {
|
|
387
|
-
current.arguments = `${current.arguments}${normalizeIncomingToolCallArguments(td.function.arguments)}`;
|
|
388
|
-
}
|
|
389
|
-
toolCallsByIndex.set(idx, current);
|
|
390
|
-
if (onToolCallDelta) {
|
|
391
|
-
onToolCallDelta({
|
|
392
|
-
index: idx,
|
|
393
|
-
id: current.id || `tc-${idx + 1}`,
|
|
394
|
-
name: current.name,
|
|
395
|
-
arguments: current.arguments || '{}'
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (choice0?.finish_reason) {
|
|
401
|
-
break;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return buildFinalStreamResult({
|
|
406
|
-
text,
|
|
407
|
-
toolCallsByIndex,
|
|
408
|
-
usage,
|
|
409
|
-
messages,
|
|
410
|
-
reasoningDetails
|
|
411
|
-
});
|
|
412
|
-
}
|