@shareai-lab/kode-sdk 2.7.1 → 2.7.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/dist/core/agent/breakpoint-manager.js +36 -0
- package/dist/core/agent/message-queue.js +57 -0
- package/dist/core/agent/permission-manager.js +32 -0
- package/dist/core/agent/todo-manager.js +91 -0
- package/dist/core/agent/tool-runner.js +45 -0
- package/dist/core/agent.js +2035 -0
- package/dist/core/config.js +2 -0
- package/dist/core/context-manager.js +241 -0
- package/dist/core/errors.js +49 -0
- package/dist/core/events.js +329 -0
- package/dist/core/file-pool.d.ts +2 -0
- package/dist/core/file-pool.js +125 -0
- package/dist/core/hooks.js +71 -0
- package/dist/core/permission-modes.js +61 -0
- package/dist/core/pool.js +301 -0
- package/dist/core/room.js +57 -0
- package/dist/core/scheduler.js +58 -0
- package/dist/core/skills/index.js +20 -0
- package/dist/core/skills/management-manager.js +557 -0
- package/dist/core/skills/manager.js +243 -0
- package/dist/core/skills/operation-queue.js +113 -0
- package/dist/core/skills/sandbox-file-manager.js +183 -0
- package/dist/core/skills/types.js +9 -0
- package/dist/core/skills/xml-generator.js +70 -0
- package/dist/core/template.js +35 -0
- package/dist/core/time-bridge.js +100 -0
- package/dist/core/todo.js +89 -0
- package/dist/core/types.js +3 -0
- package/dist/index.js +148 -60461
- package/dist/infra/db/postgres/postgres-store.js +1073 -0
- package/dist/infra/db/sqlite/sqlite-store.js +800 -0
- package/dist/infra/e2b/e2b-fs.js +128 -0
- package/dist/infra/e2b/e2b-sandbox.js +156 -0
- package/dist/infra/e2b/e2b-template.js +105 -0
- package/dist/infra/e2b/index.js +9 -0
- package/dist/infra/e2b/types.js +2 -0
- package/dist/infra/provider.js +67 -0
- package/dist/infra/providers/anthropic.js +308 -0
- package/dist/infra/providers/core/errors.js +353 -0
- package/dist/infra/providers/core/fork.js +418 -0
- package/dist/infra/providers/core/index.js +76 -0
- package/dist/infra/providers/core/logger.js +191 -0
- package/dist/infra/providers/core/retry.js +189 -0
- package/dist/infra/providers/core/usage.js +376 -0
- package/dist/infra/providers/gemini.js +493 -0
- package/dist/infra/providers/index.js +83 -0
- package/dist/infra/providers/openai.js +662 -0
- package/dist/infra/providers/types.js +20 -0
- package/dist/infra/providers/utils.js +400 -0
- package/dist/infra/sandbox-factory.js +30 -0
- package/dist/infra/sandbox.js +243 -0
- package/dist/infra/store/factory.js +80 -0
- package/dist/infra/store/index.js +26 -0
- package/dist/infra/store/json-store.js +606 -0
- package/dist/infra/store/types.js +2 -0
- package/dist/infra/store.js +29 -0
- package/dist/tools/bash_kill/index.js +35 -0
- package/dist/tools/bash_kill/prompt.js +14 -0
- package/dist/tools/bash_logs/index.js +40 -0
- package/dist/tools/bash_logs/prompt.js +14 -0
- package/dist/tools/bash_run/index.js +61 -0
- package/dist/tools/bash_run/prompt.js +18 -0
- package/dist/tools/builtin.js +26 -0
- package/dist/tools/define.js +214 -0
- package/dist/tools/fs_edit/index.js +62 -0
- package/dist/tools/fs_edit/prompt.js +15 -0
- package/dist/tools/fs_glob/index.js +40 -0
- package/dist/tools/fs_glob/prompt.js +15 -0
- package/dist/tools/fs_grep/index.js +66 -0
- package/dist/tools/fs_grep/prompt.js +16 -0
- package/dist/tools/fs_multi_edit/index.js +106 -0
- package/dist/tools/fs_multi_edit/prompt.js +16 -0
- package/dist/tools/fs_read/index.js +40 -0
- package/dist/tools/fs_read/prompt.js +16 -0
- package/dist/tools/fs_write/index.js +40 -0
- package/dist/tools/fs_write/prompt.js +15 -0
- package/dist/tools/index.js +61 -0
- package/dist/tools/mcp.js +185 -0
- package/dist/tools/registry.js +26 -0
- package/dist/tools/scripts.js +205 -0
- package/dist/tools/skills.js +115 -0
- package/dist/tools/task_run/index.js +58 -0
- package/dist/tools/task_run/prompt.js +25 -0
- package/dist/tools/todo_read/index.js +29 -0
- package/dist/tools/todo_read/prompt.js +18 -0
- package/dist/tools/todo_write/index.js +42 -0
- package/dist/tools/todo_write/prompt.js +23 -0
- package/dist/tools/tool.js +211 -0
- package/dist/tools/toolkit.js +98 -0
- package/dist/tools/type-inference.js +207 -0
- package/dist/utils/agent-id.js +28 -0
- package/dist/utils/logger.js +44 -0
- package/dist/utils/session-id.js +64 -0
- package/package.json +7 -38
- package/dist/index.js.map +0 -7
- package/dist/index.mjs +0 -60385
- package/dist/index.mjs.map +0 -7
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fork Point Detection and Resume Support
|
|
4
|
+
*
|
|
5
|
+
* Provides utilities for detecting safe fork points in message history
|
|
6
|
+
* and preparing messages for resume across different providers.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.geminiResumeHandler = exports.openaiResponsesResumeHandler = exports.openaiChatResumeHandler = exports.qwenResumeHandler = exports.deepseekResumeHandler = exports.anthropicResumeHandler = void 0;
|
|
10
|
+
exports.findSafeForkPoints = findSafeForkPoints;
|
|
11
|
+
exports.getLastSafeForkPoint = getLastSafeForkPoint;
|
|
12
|
+
exports.serializeForResume = serializeForResume;
|
|
13
|
+
exports.getResumeHandler = getResumeHandler;
|
|
14
|
+
exports.prepareMessagesForResume = prepareMessagesForResume;
|
|
15
|
+
exports.validateMessagesForResume = validateMessagesForResume;
|
|
16
|
+
exports.canForkAt = canForkAt;
|
|
17
|
+
exports.forkAt = forkAt;
|
|
18
|
+
/**
|
|
19
|
+
* Find all safe fork points in a message sequence.
|
|
20
|
+
*
|
|
21
|
+
* Safe fork points are:
|
|
22
|
+
* 1. User messages
|
|
23
|
+
* 2. Assistant messages without tool_use
|
|
24
|
+
* 3. After a user message containing all tool_results for preceding tool_uses
|
|
25
|
+
*/
|
|
26
|
+
function findSafeForkPoints(messages) {
|
|
27
|
+
const points = [];
|
|
28
|
+
for (let i = 0; i < messages.length; i++) {
|
|
29
|
+
const msg = messages[i];
|
|
30
|
+
const point = analyzeForkSafety(msg, i, messages);
|
|
31
|
+
points.push(point);
|
|
32
|
+
}
|
|
33
|
+
return points;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Analyze if a specific message index is a safe fork point.
|
|
37
|
+
*/
|
|
38
|
+
function analyzeForkSafety(msg, index, messages) {
|
|
39
|
+
// User messages are always safe fork points
|
|
40
|
+
if (msg.role === 'user') {
|
|
41
|
+
// But check if this user message contains incomplete tool results
|
|
42
|
+
const prevMsg = messages[index - 1];
|
|
43
|
+
if (prevMsg?.role === 'assistant') {
|
|
44
|
+
const toolUseIds = getToolUseIds(prevMsg);
|
|
45
|
+
if (toolUseIds.length > 0) {
|
|
46
|
+
const resultIds = getToolResultIds(msg);
|
|
47
|
+
const allHaveResults = toolUseIds.every(id => resultIds.includes(id));
|
|
48
|
+
if (!allHaveResults) {
|
|
49
|
+
return {
|
|
50
|
+
messageIndex: index,
|
|
51
|
+
isSafe: false,
|
|
52
|
+
reason: 'User message does not contain all required tool_results',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { messageIndex: index, isSafe: true };
|
|
58
|
+
}
|
|
59
|
+
// Assistant messages without tool_use are safe
|
|
60
|
+
if (msg.role === 'assistant') {
|
|
61
|
+
const hasToolUse = msg.content.some(b => b.type === 'tool_use');
|
|
62
|
+
if (!hasToolUse) {
|
|
63
|
+
return { messageIndex: index, isSafe: true };
|
|
64
|
+
}
|
|
65
|
+
// Check if all tool calls have results in the next message
|
|
66
|
+
const toolUseIds = getToolUseIds(msg);
|
|
67
|
+
const nextMsg = messages[index + 1];
|
|
68
|
+
if (nextMsg?.role === 'user') {
|
|
69
|
+
const resultIds = getToolResultIds(nextMsg);
|
|
70
|
+
const allHaveResults = toolUseIds.every(id => resultIds.includes(id));
|
|
71
|
+
if (allHaveResults) {
|
|
72
|
+
// The next user message (index + 1) is the safe fork point
|
|
73
|
+
return {
|
|
74
|
+
messageIndex: index,
|
|
75
|
+
isSafe: false,
|
|
76
|
+
reason: 'Fork at next message (after tool results)',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
messageIndex: index,
|
|
82
|
+
isSafe: false,
|
|
83
|
+
reason: 'Pending tool calls without results',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// System messages
|
|
87
|
+
if (msg.role === 'system') {
|
|
88
|
+
return { messageIndex: index, isSafe: true };
|
|
89
|
+
}
|
|
90
|
+
return { messageIndex: index, isSafe: false, reason: 'Unknown message role' };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the last safe fork point index.
|
|
94
|
+
*/
|
|
95
|
+
function getLastSafeForkPoint(messages) {
|
|
96
|
+
const points = findSafeForkPoints(messages);
|
|
97
|
+
for (let i = points.length - 1; i >= 0; i--) {
|
|
98
|
+
if (points[i].isSafe) {
|
|
99
|
+
return points[i].messageIndex;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return -1; // No safe fork point found
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Extract tool_use IDs from a message.
|
|
106
|
+
*/
|
|
107
|
+
function getToolUseIds(msg) {
|
|
108
|
+
return msg.content
|
|
109
|
+
.filter((b) => b.type === 'tool_use')
|
|
110
|
+
.map(b => b.id);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Extract tool_result IDs from a message.
|
|
114
|
+
*/
|
|
115
|
+
function getToolResultIds(msg) {
|
|
116
|
+
return msg.content
|
|
117
|
+
.filter((b) => b.type === 'tool_result')
|
|
118
|
+
.map(b => b.tool_use_id);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Serialize messages for persistence.
|
|
122
|
+
*/
|
|
123
|
+
function serializeForResume(messages, options) {
|
|
124
|
+
return messages.map(msg => serializeMessage(msg, options));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Serialize a single message.
|
|
128
|
+
*/
|
|
129
|
+
function serializeMessage(msg, options) {
|
|
130
|
+
const serialized = {
|
|
131
|
+
role: msg.role,
|
|
132
|
+
content: [],
|
|
133
|
+
metadata: msg.metadata,
|
|
134
|
+
};
|
|
135
|
+
for (const block of msg.content) {
|
|
136
|
+
const serializedBlock = serializeBlock(block, options);
|
|
137
|
+
if (serializedBlock) {
|
|
138
|
+
serialized.content.push(serializedBlock);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return serialized;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Serialize a content block based on options.
|
|
145
|
+
*/
|
|
146
|
+
function serializeBlock(block, options) {
|
|
147
|
+
// Handle reasoning blocks based on transport
|
|
148
|
+
if (block.type === 'reasoning') {
|
|
149
|
+
switch (options.reasoningTransport) {
|
|
150
|
+
case 'provider':
|
|
151
|
+
// Keep as-is for providers that support it
|
|
152
|
+
return block;
|
|
153
|
+
case 'text':
|
|
154
|
+
// Convert to text block with <think> tags
|
|
155
|
+
return {
|
|
156
|
+
type: 'text',
|
|
157
|
+
text: `<think>${block.reasoning}</think>`,
|
|
158
|
+
};
|
|
159
|
+
case 'omit':
|
|
160
|
+
// Exclude from serialized output
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// All other blocks pass through
|
|
165
|
+
return block;
|
|
166
|
+
}
|
|
167
|
+
// ============================================================================
|
|
168
|
+
// Provider-Specific Resume Handlers
|
|
169
|
+
// ============================================================================
|
|
170
|
+
/**
|
|
171
|
+
* Anthropic resume handler.
|
|
172
|
+
* Preserves thinking blocks with signatures for Claude 4+.
|
|
173
|
+
*/
|
|
174
|
+
exports.anthropicResumeHandler = {
|
|
175
|
+
prepareForResume(messages) {
|
|
176
|
+
// Anthropic requires thinking blocks with signatures for Claude 4+
|
|
177
|
+
// Blocks without signatures will be ignored by the API
|
|
178
|
+
return messages.map(msg => {
|
|
179
|
+
if (msg.role !== 'assistant')
|
|
180
|
+
return msg;
|
|
181
|
+
// Keep all blocks, including reasoning
|
|
182
|
+
// The API will verify signatures and ignore invalid ones
|
|
183
|
+
return msg;
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
validateForResume(messages) {
|
|
187
|
+
const errors = [];
|
|
188
|
+
const warnings = [];
|
|
189
|
+
for (let i = 0; i < messages.length; i++) {
|
|
190
|
+
const msg = messages[i];
|
|
191
|
+
// Check tool_use has corresponding tool_result
|
|
192
|
+
if (msg.role === 'assistant') {
|
|
193
|
+
const toolUses = msg.content.filter(b => b.type === 'tool_use');
|
|
194
|
+
if (toolUses.length > 0 && i < messages.length - 1) {
|
|
195
|
+
const nextMsg = messages[i + 1];
|
|
196
|
+
if (nextMsg.role !== 'user') {
|
|
197
|
+
errors.push(`Tool use at index ${i} not followed by user message`);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
const resultIds = getToolResultIds(nextMsg);
|
|
201
|
+
const toolUseIds = getToolUseIds(msg);
|
|
202
|
+
const missing = toolUseIds.filter(id => !resultIds.includes(id));
|
|
203
|
+
if (missing.length > 0) {
|
|
204
|
+
errors.push(`Missing tool_results for tool_use IDs: ${missing.join(', ')}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Check for reasoning without signatures
|
|
210
|
+
if (msg.role === 'assistant') {
|
|
211
|
+
const reasoningBlocks = msg.content.filter(b => b.type === 'reasoning');
|
|
212
|
+
for (const block of reasoningBlocks) {
|
|
213
|
+
if (block.type === 'reasoning' && !block.meta?.signature) {
|
|
214
|
+
warnings.push(`Reasoning block at index ${i} has no signature (may be ignored)`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
valid: errors.length === 0,
|
|
221
|
+
errors,
|
|
222
|
+
warnings,
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* DeepSeek resume handler.
|
|
228
|
+
* CRITICAL: Must NOT include reasoning_content in message history.
|
|
229
|
+
*/
|
|
230
|
+
exports.deepseekResumeHandler = {
|
|
231
|
+
prepareForResume(messages) {
|
|
232
|
+
// DeepSeek returns 400 if reasoning_content is included in next turn
|
|
233
|
+
// Only include content field (text blocks)
|
|
234
|
+
return messages.map(msg => {
|
|
235
|
+
if (msg.role !== 'assistant')
|
|
236
|
+
return msg;
|
|
237
|
+
// Filter out reasoning blocks entirely
|
|
238
|
+
const filteredBlocks = msg.content.filter(b => b.type !== 'reasoning');
|
|
239
|
+
return { ...msg, content: filteredBlocks };
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
validateForResume(messages) {
|
|
243
|
+
const errors = [];
|
|
244
|
+
// Check that reasoning is not included in non-last messages
|
|
245
|
+
for (let i = 0; i < messages.length - 1; i++) {
|
|
246
|
+
const msg = messages[i];
|
|
247
|
+
if (msg.role === 'assistant') {
|
|
248
|
+
const hasReasoning = msg.content.some(b => b.type === 'reasoning');
|
|
249
|
+
if (hasReasoning) {
|
|
250
|
+
errors.push(`DeepSeek: reasoning_content must not be included at index ${i} (returns 400 error)`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
valid: errors.length === 0,
|
|
256
|
+
errors,
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Qwen resume handler.
|
|
262
|
+
* Similar to DeepSeek - reasoning should be omitted.
|
|
263
|
+
*/
|
|
264
|
+
exports.qwenResumeHandler = {
|
|
265
|
+
prepareForResume(messages) {
|
|
266
|
+
// Qwen: Similar to DeepSeek, reasoning_content should be omitted
|
|
267
|
+
return messages.map(msg => {
|
|
268
|
+
if (msg.role !== 'assistant')
|
|
269
|
+
return msg;
|
|
270
|
+
const filteredBlocks = msg.content.filter(b => b.type !== 'reasoning');
|
|
271
|
+
return { ...msg, content: filteredBlocks };
|
|
272
|
+
});
|
|
273
|
+
},
|
|
274
|
+
validateForResume(messages) {
|
|
275
|
+
// Less strict than DeepSeek, but still recommend omitting
|
|
276
|
+
return { valid: true, errors: [] };
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* OpenAI Chat resume handler.
|
|
281
|
+
* Reasoning is converted to text with <think> tags.
|
|
282
|
+
*/
|
|
283
|
+
exports.openaiChatResumeHandler = {
|
|
284
|
+
prepareForResume(messages) {
|
|
285
|
+
return messages.map(msg => {
|
|
286
|
+
if (msg.role !== 'assistant')
|
|
287
|
+
return msg;
|
|
288
|
+
// Convert reasoning to text with <think> tags
|
|
289
|
+
const convertedBlocks = msg.content.map(block => {
|
|
290
|
+
if (block.type === 'reasoning') {
|
|
291
|
+
return {
|
|
292
|
+
type: 'text',
|
|
293
|
+
text: `<think>${block.reasoning}</think>`,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
return block;
|
|
297
|
+
});
|
|
298
|
+
return { ...msg, content: convertedBlocks };
|
|
299
|
+
});
|
|
300
|
+
},
|
|
301
|
+
validateForResume(messages) {
|
|
302
|
+
return { valid: true, errors: [] };
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* OpenAI Responses API resume handler.
|
|
307
|
+
* Uses previous_response_id for state persistence.
|
|
308
|
+
*/
|
|
309
|
+
exports.openaiResponsesResumeHandler = {
|
|
310
|
+
prepareForResume(messages) {
|
|
311
|
+
// Responses API handles state via previous_response_id
|
|
312
|
+
// Messages are passed normally
|
|
313
|
+
return messages;
|
|
314
|
+
},
|
|
315
|
+
validateForResume(messages) {
|
|
316
|
+
// Responses API manages state via previous_response_id
|
|
317
|
+
// This is typically passed in options, not in message metadata
|
|
318
|
+
return { valid: true, errors: [] };
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* Gemini resume handler.
|
|
323
|
+
* Preserves thoughtSignature for function calls.
|
|
324
|
+
*/
|
|
325
|
+
exports.geminiResumeHandler = {
|
|
326
|
+
prepareForResume(messages) {
|
|
327
|
+
// Gemini: Preserve thoughtSignature for function call continuation
|
|
328
|
+
return messages.map(msg => {
|
|
329
|
+
if (msg.role !== 'assistant')
|
|
330
|
+
return msg;
|
|
331
|
+
// Keep reasoning blocks that have thoughtSignature
|
|
332
|
+
// Others can be omitted or converted
|
|
333
|
+
const processedBlocks = msg.content.map(block => {
|
|
334
|
+
if (block.type === 'reasoning') {
|
|
335
|
+
// Keep if has signature, otherwise omit
|
|
336
|
+
if (block.meta?.thoughtSignature) {
|
|
337
|
+
return block;
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
return block;
|
|
342
|
+
}).filter((b) => b !== null);
|
|
343
|
+
return { ...msg, content: processedBlocks };
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
validateForResume(messages) {
|
|
347
|
+
return { valid: true, errors: [] };
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* Get resume handler for a provider.
|
|
352
|
+
*/
|
|
353
|
+
function getResumeHandler(provider) {
|
|
354
|
+
switch (provider) {
|
|
355
|
+
case 'anthropic':
|
|
356
|
+
return exports.anthropicResumeHandler;
|
|
357
|
+
case 'deepseek':
|
|
358
|
+
return exports.deepseekResumeHandler;
|
|
359
|
+
case 'qwen':
|
|
360
|
+
return exports.qwenResumeHandler;
|
|
361
|
+
case 'openai':
|
|
362
|
+
case 'openai-chat':
|
|
363
|
+
return exports.openaiChatResumeHandler;
|
|
364
|
+
case 'openai-responses':
|
|
365
|
+
return exports.openaiResponsesResumeHandler;
|
|
366
|
+
case 'gemini':
|
|
367
|
+
return exports.geminiResumeHandler;
|
|
368
|
+
default:
|
|
369
|
+
// Default handler - omit reasoning to be safe
|
|
370
|
+
return {
|
|
371
|
+
prepareForResume(messages) {
|
|
372
|
+
return messages.map(msg => {
|
|
373
|
+
if (msg.role !== 'assistant')
|
|
374
|
+
return msg;
|
|
375
|
+
const filtered = msg.content.filter(b => b.type !== 'reasoning');
|
|
376
|
+
return { ...msg, content: filtered };
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
validateForResume() {
|
|
380
|
+
return { valid: true, errors: [] };
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Prepare messages for resume with a specific provider.
|
|
387
|
+
*/
|
|
388
|
+
function prepareMessagesForResume(messages, provider) {
|
|
389
|
+
const handler = getResumeHandler(provider);
|
|
390
|
+
return handler.prepareForResume(messages);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Validate messages for resume with a specific provider.
|
|
394
|
+
*/
|
|
395
|
+
function validateMessagesForResume(messages, provider) {
|
|
396
|
+
const handler = getResumeHandler(provider);
|
|
397
|
+
return handler.validateForResume(messages);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Check if a message sequence can be safely forked at a given index.
|
|
401
|
+
*/
|
|
402
|
+
function canForkAt(messages, index) {
|
|
403
|
+
if (index < 0 || index >= messages.length) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
const points = findSafeForkPoints(messages);
|
|
407
|
+
return points[index]?.isSafe ?? false;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Fork messages at a given index.
|
|
411
|
+
* Returns messages up to and including the fork point.
|
|
412
|
+
*/
|
|
413
|
+
function forkAt(messages, index) {
|
|
414
|
+
if (!canForkAt(messages, index)) {
|
|
415
|
+
throw new Error(`Cannot fork at index ${index} - not a safe fork point`);
|
|
416
|
+
}
|
|
417
|
+
return messages.slice(0, index + 1);
|
|
418
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core Provider Module
|
|
4
|
+
*
|
|
5
|
+
* Re-exports all core utilities for provider implementations.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.openaiChatResumeHandler = exports.qwenResumeHandler = exports.deepseekResumeHandler = exports.anthropicResumeHandler = exports.serializeForResume = exports.getLastSafeForkPoint = exports.findSafeForkPoints = exports.generateAuditId = exports.truncateContent = exports.redactSensitive = exports.createProviderLogger = exports.createConsoleLogger = exports.DEFAULT_DEBUG_CONFIG = exports.getRetryDelay = exports.shouldRetry = exports.createRetryWrapper = exports.withRetryAndTimeout = exports.withRetry = exports.AGGRESSIVE_RETRY_CONFIG = exports.DEFAULT_RETRY_CONFIG = exports.formatUsageString = exports.aggregateUsage = exports.normalizeDeepSeekUsage = exports.normalizeGeminiUsage = exports.normalizeOpenAIUsage = exports.normalizeAnthropicUsage = exports.calculateCost = exports.createEmptyUsage = exports.PROVIDER_PRICING = exports.isContentFilterError = exports.isContextLengthError = exports.isAuthError = exports.isRateLimitError = exports.isRetryableError = exports.parseProviderError = exports.ParseError = exports.StreamError = exports.ThinkingSignatureError = exports.ServiceUnavailableError = exports.QuotaExceededError = exports.ModelNotFoundError = exports.ContentFilterError = exports.NetworkError = exports.TimeoutError = exports.ServerError = exports.InvalidRequestError = exports.ContextLengthError = exports.AuthenticationError = exports.RateLimitError = exports.ProviderError = void 0;
|
|
9
|
+
exports.forkAt = exports.canForkAt = exports.validateMessagesForResume = exports.prepareMessagesForResume = exports.getResumeHandler = exports.geminiResumeHandler = exports.openaiResponsesResumeHandler = void 0;
|
|
10
|
+
// Error types and utilities
|
|
11
|
+
var errors_1 = require("./errors");
|
|
12
|
+
Object.defineProperty(exports, "ProviderError", { enumerable: true, get: function () { return errors_1.ProviderError; } });
|
|
13
|
+
Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_1.RateLimitError; } });
|
|
14
|
+
Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_1.AuthenticationError; } });
|
|
15
|
+
Object.defineProperty(exports, "ContextLengthError", { enumerable: true, get: function () { return errors_1.ContextLengthError; } });
|
|
16
|
+
Object.defineProperty(exports, "InvalidRequestError", { enumerable: true, get: function () { return errors_1.InvalidRequestError; } });
|
|
17
|
+
Object.defineProperty(exports, "ServerError", { enumerable: true, get: function () { return errors_1.ServerError; } });
|
|
18
|
+
Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return errors_1.TimeoutError; } });
|
|
19
|
+
Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return errors_1.NetworkError; } });
|
|
20
|
+
Object.defineProperty(exports, "ContentFilterError", { enumerable: true, get: function () { return errors_1.ContentFilterError; } });
|
|
21
|
+
Object.defineProperty(exports, "ModelNotFoundError", { enumerable: true, get: function () { return errors_1.ModelNotFoundError; } });
|
|
22
|
+
Object.defineProperty(exports, "QuotaExceededError", { enumerable: true, get: function () { return errors_1.QuotaExceededError; } });
|
|
23
|
+
Object.defineProperty(exports, "ServiceUnavailableError", { enumerable: true, get: function () { return errors_1.ServiceUnavailableError; } });
|
|
24
|
+
Object.defineProperty(exports, "ThinkingSignatureError", { enumerable: true, get: function () { return errors_1.ThinkingSignatureError; } });
|
|
25
|
+
Object.defineProperty(exports, "StreamError", { enumerable: true, get: function () { return errors_1.StreamError; } });
|
|
26
|
+
Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return errors_1.ParseError; } });
|
|
27
|
+
Object.defineProperty(exports, "parseProviderError", { enumerable: true, get: function () { return errors_1.parseProviderError; } });
|
|
28
|
+
Object.defineProperty(exports, "isRetryableError", { enumerable: true, get: function () { return errors_1.isRetryableError; } });
|
|
29
|
+
Object.defineProperty(exports, "isRateLimitError", { enumerable: true, get: function () { return errors_1.isRateLimitError; } });
|
|
30
|
+
Object.defineProperty(exports, "isAuthError", { enumerable: true, get: function () { return errors_1.isAuthError; } });
|
|
31
|
+
Object.defineProperty(exports, "isContextLengthError", { enumerable: true, get: function () { return errors_1.isContextLengthError; } });
|
|
32
|
+
Object.defineProperty(exports, "isContentFilterError", { enumerable: true, get: function () { return errors_1.isContentFilterError; } });
|
|
33
|
+
// Usage statistics and cost calculation
|
|
34
|
+
var usage_1 = require("./usage");
|
|
35
|
+
Object.defineProperty(exports, "PROVIDER_PRICING", { enumerable: true, get: function () { return usage_1.PROVIDER_PRICING; } });
|
|
36
|
+
Object.defineProperty(exports, "createEmptyUsage", { enumerable: true, get: function () { return usage_1.createEmptyUsage; } });
|
|
37
|
+
Object.defineProperty(exports, "calculateCost", { enumerable: true, get: function () { return usage_1.calculateCost; } });
|
|
38
|
+
Object.defineProperty(exports, "normalizeAnthropicUsage", { enumerable: true, get: function () { return usage_1.normalizeAnthropicUsage; } });
|
|
39
|
+
Object.defineProperty(exports, "normalizeOpenAIUsage", { enumerable: true, get: function () { return usage_1.normalizeOpenAIUsage; } });
|
|
40
|
+
Object.defineProperty(exports, "normalizeGeminiUsage", { enumerable: true, get: function () { return usage_1.normalizeGeminiUsage; } });
|
|
41
|
+
Object.defineProperty(exports, "normalizeDeepSeekUsage", { enumerable: true, get: function () { return usage_1.normalizeDeepSeekUsage; } });
|
|
42
|
+
Object.defineProperty(exports, "aggregateUsage", { enumerable: true, get: function () { return usage_1.aggregateUsage; } });
|
|
43
|
+
Object.defineProperty(exports, "formatUsageString", { enumerable: true, get: function () { return usage_1.formatUsageString; } });
|
|
44
|
+
// Retry strategy
|
|
45
|
+
var retry_1 = require("./retry");
|
|
46
|
+
Object.defineProperty(exports, "DEFAULT_RETRY_CONFIG", { enumerable: true, get: function () { return retry_1.DEFAULT_RETRY_CONFIG; } });
|
|
47
|
+
Object.defineProperty(exports, "AGGRESSIVE_RETRY_CONFIG", { enumerable: true, get: function () { return retry_1.AGGRESSIVE_RETRY_CONFIG; } });
|
|
48
|
+
Object.defineProperty(exports, "withRetry", { enumerable: true, get: function () { return retry_1.withRetry; } });
|
|
49
|
+
Object.defineProperty(exports, "withRetryAndTimeout", { enumerable: true, get: function () { return retry_1.withRetryAndTimeout; } });
|
|
50
|
+
Object.defineProperty(exports, "createRetryWrapper", { enumerable: true, get: function () { return retry_1.createRetryWrapper; } });
|
|
51
|
+
Object.defineProperty(exports, "shouldRetry", { enumerable: true, get: function () { return retry_1.shouldRetry; } });
|
|
52
|
+
Object.defineProperty(exports, "getRetryDelay", { enumerable: true, get: function () { return retry_1.getRetryDelay; } });
|
|
53
|
+
// Logging and debugging
|
|
54
|
+
var logger_1 = require("./logger");
|
|
55
|
+
Object.defineProperty(exports, "DEFAULT_DEBUG_CONFIG", { enumerable: true, get: function () { return logger_1.DEFAULT_DEBUG_CONFIG; } });
|
|
56
|
+
Object.defineProperty(exports, "createConsoleLogger", { enumerable: true, get: function () { return logger_1.createConsoleLogger; } });
|
|
57
|
+
Object.defineProperty(exports, "createProviderLogger", { enumerable: true, get: function () { return logger_1.createProviderLogger; } });
|
|
58
|
+
Object.defineProperty(exports, "redactSensitive", { enumerable: true, get: function () { return logger_1.redactSensitive; } });
|
|
59
|
+
Object.defineProperty(exports, "truncateContent", { enumerable: true, get: function () { return logger_1.truncateContent; } });
|
|
60
|
+
Object.defineProperty(exports, "generateAuditId", { enumerable: true, get: function () { return logger_1.generateAuditId; } });
|
|
61
|
+
// Fork point detection and resume
|
|
62
|
+
var fork_1 = require("./fork");
|
|
63
|
+
Object.defineProperty(exports, "findSafeForkPoints", { enumerable: true, get: function () { return fork_1.findSafeForkPoints; } });
|
|
64
|
+
Object.defineProperty(exports, "getLastSafeForkPoint", { enumerable: true, get: function () { return fork_1.getLastSafeForkPoint; } });
|
|
65
|
+
Object.defineProperty(exports, "serializeForResume", { enumerable: true, get: function () { return fork_1.serializeForResume; } });
|
|
66
|
+
Object.defineProperty(exports, "anthropicResumeHandler", { enumerable: true, get: function () { return fork_1.anthropicResumeHandler; } });
|
|
67
|
+
Object.defineProperty(exports, "deepseekResumeHandler", { enumerable: true, get: function () { return fork_1.deepseekResumeHandler; } });
|
|
68
|
+
Object.defineProperty(exports, "qwenResumeHandler", { enumerable: true, get: function () { return fork_1.qwenResumeHandler; } });
|
|
69
|
+
Object.defineProperty(exports, "openaiChatResumeHandler", { enumerable: true, get: function () { return fork_1.openaiChatResumeHandler; } });
|
|
70
|
+
Object.defineProperty(exports, "openaiResponsesResumeHandler", { enumerable: true, get: function () { return fork_1.openaiResponsesResumeHandler; } });
|
|
71
|
+
Object.defineProperty(exports, "geminiResumeHandler", { enumerable: true, get: function () { return fork_1.geminiResumeHandler; } });
|
|
72
|
+
Object.defineProperty(exports, "getResumeHandler", { enumerable: true, get: function () { return fork_1.getResumeHandler; } });
|
|
73
|
+
Object.defineProperty(exports, "prepareMessagesForResume", { enumerable: true, get: function () { return fork_1.prepareMessagesForResume; } });
|
|
74
|
+
Object.defineProperty(exports, "validateMessagesForResume", { enumerable: true, get: function () { return fork_1.validateMessagesForResume; } });
|
|
75
|
+
Object.defineProperty(exports, "canForkAt", { enumerable: true, get: function () { return fork_1.canForkAt; } });
|
|
76
|
+
Object.defineProperty(exports, "forkAt", { enumerable: true, get: function () { return fork_1.forkAt; } });
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logging and Debugging Module
|
|
4
|
+
*
|
|
5
|
+
* Unified logging interfaces for provider operations,
|
|
6
|
+
* request/response tracking, and audit trail.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.DEFAULT_DEBUG_CONFIG = void 0;
|
|
10
|
+
exports.createConsoleLogger = createConsoleLogger;
|
|
11
|
+
exports.createProviderLogger = createProviderLogger;
|
|
12
|
+
exports.redactSensitive = redactSensitive;
|
|
13
|
+
exports.truncateContent = truncateContent;
|
|
14
|
+
exports.generateAuditId = generateAuditId;
|
|
15
|
+
/**
|
|
16
|
+
* Default debug configuration.
|
|
17
|
+
*/
|
|
18
|
+
exports.DEFAULT_DEBUG_CONFIG = {
|
|
19
|
+
verbose: false,
|
|
20
|
+
logRawRequests: false,
|
|
21
|
+
logRawResponses: false,
|
|
22
|
+
logThinking: false,
|
|
23
|
+
logTokenUsage: true,
|
|
24
|
+
logCache: true,
|
|
25
|
+
logRetries: true,
|
|
26
|
+
redactSensitive: true,
|
|
27
|
+
maxContentLength: 500,
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Create a console-based logger.
|
|
31
|
+
*/
|
|
32
|
+
function createConsoleLogger(context = {}, debugConfig = {}) {
|
|
33
|
+
const config = { ...exports.DEFAULT_DEBUG_CONFIG, ...debugConfig };
|
|
34
|
+
const log = (level, message, ctx) => {
|
|
35
|
+
if (!config.verbose && level === 'debug')
|
|
36
|
+
return;
|
|
37
|
+
const entry = {
|
|
38
|
+
level,
|
|
39
|
+
message,
|
|
40
|
+
timestamp: Date.now(),
|
|
41
|
+
context: { ...context, ...ctx },
|
|
42
|
+
};
|
|
43
|
+
const prefix = `[${level.toUpperCase()}]`;
|
|
44
|
+
const contextStr = Object.keys(entry.context || {}).length > 0
|
|
45
|
+
? ` ${JSON.stringify(entry.context)}`
|
|
46
|
+
: '';
|
|
47
|
+
const output = `${prefix} ${message}${contextStr}`;
|
|
48
|
+
switch (level) {
|
|
49
|
+
case 'debug':
|
|
50
|
+
console.debug(output);
|
|
51
|
+
break;
|
|
52
|
+
case 'info':
|
|
53
|
+
console.info(output);
|
|
54
|
+
break;
|
|
55
|
+
case 'warn':
|
|
56
|
+
console.warn(output);
|
|
57
|
+
break;
|
|
58
|
+
case 'error':
|
|
59
|
+
console.error(output);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
debug: (message, ctx) => log('debug', message, ctx),
|
|
65
|
+
info: (message, ctx) => log('info', message, ctx),
|
|
66
|
+
warn: (message, ctx) => log('warn', message, ctx),
|
|
67
|
+
error: (message, ctx) => log('error', message, ctx),
|
|
68
|
+
child: (childContext) => createConsoleLogger({ ...context, ...childContext }, debugConfig),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a provider logger with request/response tracking.
|
|
73
|
+
*/
|
|
74
|
+
function createProviderLogger(provider, debugConfig = {}) {
|
|
75
|
+
const config = { ...exports.DEFAULT_DEBUG_CONFIG, ...debugConfig };
|
|
76
|
+
const base = createConsoleLogger({ provider }, debugConfig);
|
|
77
|
+
return {
|
|
78
|
+
...base,
|
|
79
|
+
logRequest(request) {
|
|
80
|
+
if (!config.verbose)
|
|
81
|
+
return;
|
|
82
|
+
base.info('Provider request', {
|
|
83
|
+
model: request.model,
|
|
84
|
+
requestId: request.requestId,
|
|
85
|
+
messageCount: request.messageCount,
|
|
86
|
+
estimatedTokens: request.estimatedTokens,
|
|
87
|
+
streaming: request.streaming,
|
|
88
|
+
toolCount: request.toolCount,
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
logResponse(response, durationMs) {
|
|
92
|
+
if (config.logTokenUsage) {
|
|
93
|
+
base.info('Provider response', {
|
|
94
|
+
model: response.model,
|
|
95
|
+
requestId: response.requestId,
|
|
96
|
+
inputTokens: response.usage.inputTokens,
|
|
97
|
+
outputTokens: response.usage.outputTokens,
|
|
98
|
+
durationMs,
|
|
99
|
+
stopReason: response.stopReason,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
logError(error) {
|
|
104
|
+
base.error('Provider error', {
|
|
105
|
+
code: error.code,
|
|
106
|
+
requestId: error.requestId,
|
|
107
|
+
retryable: error.retryable,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
logStreamStart(requestId) {
|
|
111
|
+
if (config.verbose) {
|
|
112
|
+
base.debug('Stream started', { requestId });
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
logStreamChunk(requestId, chunkSize) {
|
|
116
|
+
if (config.verbose) {
|
|
117
|
+
base.debug('Stream chunk', { requestId, chunkSize });
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
logStreamEnd(requestId, totalChunks) {
|
|
121
|
+
if (config.verbose) {
|
|
122
|
+
base.debug('Stream ended', { requestId, totalChunks });
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
logCacheHit(tokens) {
|
|
126
|
+
if (config.logCache) {
|
|
127
|
+
base.info('Cache hit', { tokens });
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
logCacheWrite(tokens, ttl) {
|
|
131
|
+
if (config.logCache) {
|
|
132
|
+
base.info('Cache write', { tokens, ttl });
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
logRetry(attempt, delayMs, error) {
|
|
136
|
+
if (config.logRetries) {
|
|
137
|
+
base.warn('Retrying request', {
|
|
138
|
+
attempt,
|
|
139
|
+
delayMs,
|
|
140
|
+
errorCode: error.code,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
child(childContext) {
|
|
145
|
+
return createProviderLogger(provider, debugConfig);
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Redact sensitive data from an object.
|
|
151
|
+
*/
|
|
152
|
+
function redactSensitive(obj) {
|
|
153
|
+
const sensitiveKeys = [
|
|
154
|
+
'apiKey',
|
|
155
|
+
'api_key',
|
|
156
|
+
'authorization',
|
|
157
|
+
'Authorization',
|
|
158
|
+
'token',
|
|
159
|
+
'secret',
|
|
160
|
+
'password',
|
|
161
|
+
'credential',
|
|
162
|
+
];
|
|
163
|
+
const redacted = {};
|
|
164
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
165
|
+
if (sensitiveKeys.some(k => key.toLowerCase().includes(k.toLowerCase()))) {
|
|
166
|
+
redacted[key] = '[REDACTED]';
|
|
167
|
+
}
|
|
168
|
+
else if (typeof value === 'object' && value !== null) {
|
|
169
|
+
redacted[key] = redactSensitive(value);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
redacted[key] = value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return redacted;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Truncate content for logging.
|
|
179
|
+
*/
|
|
180
|
+
function truncateContent(content, maxLength) {
|
|
181
|
+
if (content.length <= maxLength) {
|
|
182
|
+
return content;
|
|
183
|
+
}
|
|
184
|
+
return content.slice(0, maxLength) + `... [truncated, ${content.length - maxLength} more chars]`;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Generate unique audit ID.
|
|
188
|
+
*/
|
|
189
|
+
function generateAuditId() {
|
|
190
|
+
return `audit_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
191
|
+
}
|