ai-sdk-provider-claude-code 3.5.2 → 4.0.0
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 +223 -120
- package/dist/index.d.ts +236 -51
- package/dist/index.js +951 -223
- package/dist/index.js.map +1 -1
- package/docs/sessions.md +4 -4
- package/package.json +12 -14
- package/dist/index.cjs +0 -4025
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -1329
package/dist/index.cjs
DELETED
|
@@ -1,4025 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
AbortError: () => import_claude_agent_sdk3.AbortError,
|
|
24
|
-
ClaudeCodeLanguageModel: () => ClaudeCodeLanguageModel,
|
|
25
|
-
HOOK_EVENTS: () => import_claude_agent_sdk3.HOOK_EVENTS,
|
|
26
|
-
InMemorySessionStore: () => import_claude_agent_sdk3.InMemorySessionStore,
|
|
27
|
-
SYSTEM_PROMPT_DYNAMIC_BOUNDARY: () => import_claude_agent_sdk3.SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
|
|
28
|
-
claudeCode: () => claudeCode,
|
|
29
|
-
createAPICallError: () => createAPICallError,
|
|
30
|
-
createAiSdkMcpServer: () => createAiSdkMcpServer,
|
|
31
|
-
createAuthenticationError: () => createAuthenticationError,
|
|
32
|
-
createClaudeCode: () => createClaudeCode,
|
|
33
|
-
createCustomMcpServer: () => createCustomMcpServer,
|
|
34
|
-
createSdkMcpServer: () => import_claude_agent_sdk3.createSdkMcpServer,
|
|
35
|
-
createTimeoutError: () => createTimeoutError,
|
|
36
|
-
deleteSession: () => import_claude_agent_sdk4.deleteSession,
|
|
37
|
-
foldSessionSummary: () => import_claude_agent_sdk4.foldSessionSummary,
|
|
38
|
-
forkSession: () => import_claude_agent_sdk4.forkSession,
|
|
39
|
-
getErrorMetadata: () => getErrorMetadata,
|
|
40
|
-
getSessionInfo: () => import_claude_agent_sdk4.getSessionInfo,
|
|
41
|
-
getSessionMessages: () => import_claude_agent_sdk4.getSessionMessages,
|
|
42
|
-
getSubagentMessages: () => import_claude_agent_sdk4.getSubagentMessages,
|
|
43
|
-
importSessionToStore: () => import_claude_agent_sdk4.importSessionToStore,
|
|
44
|
-
isAuthenticationError: () => isAuthenticationError,
|
|
45
|
-
isTimeoutError: () => isTimeoutError,
|
|
46
|
-
listSessions: () => import_claude_agent_sdk4.listSessions,
|
|
47
|
-
listSubagents: () => import_claude_agent_sdk4.listSubagents,
|
|
48
|
-
renameSession: () => import_claude_agent_sdk4.renameSession,
|
|
49
|
-
startup: () => import_claude_agent_sdk5.startup,
|
|
50
|
-
tagSession: () => import_claude_agent_sdk4.tagSession,
|
|
51
|
-
tool: () => import_claude_agent_sdk3.tool
|
|
52
|
-
});
|
|
53
|
-
module.exports = __toCommonJS(index_exports);
|
|
54
|
-
|
|
55
|
-
// src/claude-code-provider.ts
|
|
56
|
-
var import_provider3 = require("@ai-sdk/provider");
|
|
57
|
-
|
|
58
|
-
// src/claude-code-language-model.ts
|
|
59
|
-
var import_provider2 = require("@ai-sdk/provider");
|
|
60
|
-
var import_provider_utils = require("@ai-sdk/provider-utils");
|
|
61
|
-
|
|
62
|
-
// src/convert-to-claude-code-messages.ts
|
|
63
|
-
var IMAGE_URL_WARNING = "Image URLs are not supported by this provider; supply base64/data URLs.";
|
|
64
|
-
var IMAGE_CONVERSION_WARNING = "Unable to convert image content; supply base64/data URLs.";
|
|
65
|
-
var MAX_TOOL_CALL_INPUT_LENGTH = 1e3;
|
|
66
|
-
function serializeToolCallInput(input) {
|
|
67
|
-
let serialized;
|
|
68
|
-
if (input === void 0) {
|
|
69
|
-
serialized = "";
|
|
70
|
-
} else {
|
|
71
|
-
try {
|
|
72
|
-
serialized = JSON.stringify(input) ?? String(input);
|
|
73
|
-
} catch {
|
|
74
|
-
serialized = String(input);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (serialized.length > MAX_TOOL_CALL_INPUT_LENGTH) {
|
|
78
|
-
return `${serialized.slice(0, MAX_TOOL_CALL_INPUT_LENGTH)}...[truncated]`;
|
|
79
|
-
}
|
|
80
|
-
return serialized;
|
|
81
|
-
}
|
|
82
|
-
function normalizeBase64(base64) {
|
|
83
|
-
return base64.replace(/\s+/g, "");
|
|
84
|
-
}
|
|
85
|
-
function isImageMimeType(mimeType) {
|
|
86
|
-
return typeof mimeType === "string" && mimeType.trim().toLowerCase().startsWith("image/");
|
|
87
|
-
}
|
|
88
|
-
function createImageContent(mediaType, data) {
|
|
89
|
-
const trimmedType = mediaType.trim();
|
|
90
|
-
const trimmedData = normalizeBase64(data.trim());
|
|
91
|
-
if (!trimmedType || !trimmedData) {
|
|
92
|
-
return void 0;
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
type: "image",
|
|
96
|
-
source: {
|
|
97
|
-
type: "base64",
|
|
98
|
-
media_type: trimmedType,
|
|
99
|
-
data: trimmedData
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function extractMimeType(candidate) {
|
|
104
|
-
if (typeof candidate === "string" && candidate.trim()) {
|
|
105
|
-
return candidate.trim();
|
|
106
|
-
}
|
|
107
|
-
return void 0;
|
|
108
|
-
}
|
|
109
|
-
function parseObjectImage(imageObj, fallbackMimeType) {
|
|
110
|
-
const data = typeof imageObj.data === "string" ? imageObj.data : void 0;
|
|
111
|
-
const mimeType = extractMimeType(
|
|
112
|
-
imageObj.mimeType ?? imageObj.mediaType ?? imageObj.media_type ?? fallbackMimeType
|
|
113
|
-
);
|
|
114
|
-
if (!data || !mimeType) {
|
|
115
|
-
return void 0;
|
|
116
|
-
}
|
|
117
|
-
return createImageContent(mimeType, data);
|
|
118
|
-
}
|
|
119
|
-
function parseStringImage(value, fallbackMimeType) {
|
|
120
|
-
const trimmed = value.trim();
|
|
121
|
-
if (/^https?:\/\//i.test(trimmed)) {
|
|
122
|
-
return { warning: IMAGE_URL_WARNING };
|
|
123
|
-
}
|
|
124
|
-
const dataUrlMatch = trimmed.match(/^data:([^;]+);base64,(.+)$/i);
|
|
125
|
-
if (dataUrlMatch) {
|
|
126
|
-
const [, mediaType, data] = dataUrlMatch;
|
|
127
|
-
const content = createImageContent(mediaType, data);
|
|
128
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
129
|
-
}
|
|
130
|
-
const base64Match = trimmed.match(/^base64:([^,]+),(.+)$/i);
|
|
131
|
-
if (base64Match) {
|
|
132
|
-
const [, explicitMimeType, data] = base64Match;
|
|
133
|
-
const content = createImageContent(explicitMimeType, data);
|
|
134
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
135
|
-
}
|
|
136
|
-
if (fallbackMimeType) {
|
|
137
|
-
const content = createImageContent(fallbackMimeType, trimmed);
|
|
138
|
-
if (content) {
|
|
139
|
-
return { content };
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
143
|
-
}
|
|
144
|
-
function parseImagePart(part) {
|
|
145
|
-
if (!part || typeof part !== "object") {
|
|
146
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
147
|
-
}
|
|
148
|
-
const imageValue = part.image;
|
|
149
|
-
const mimeType = extractMimeType(part.mimeType);
|
|
150
|
-
if (typeof imageValue === "string") {
|
|
151
|
-
return parseStringImage(imageValue, mimeType);
|
|
152
|
-
}
|
|
153
|
-
if (imageValue && typeof imageValue === "object") {
|
|
154
|
-
const content = parseObjectImage(imageValue, mimeType);
|
|
155
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
156
|
-
}
|
|
157
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
158
|
-
}
|
|
159
|
-
function convertBinaryToBase64(data) {
|
|
160
|
-
if (typeof Buffer !== "undefined") {
|
|
161
|
-
const buffer = data instanceof Uint8Array ? Buffer.from(data) : Buffer.from(new Uint8Array(data));
|
|
162
|
-
return buffer.toString("base64");
|
|
163
|
-
}
|
|
164
|
-
if (typeof btoa === "function") {
|
|
165
|
-
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
166
|
-
let binary = "";
|
|
167
|
-
const chunkSize = 32768;
|
|
168
|
-
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
169
|
-
const chunk = bytes.subarray(i, i + chunkSize);
|
|
170
|
-
binary += String.fromCharCode(...chunk);
|
|
171
|
-
}
|
|
172
|
-
return btoa(binary);
|
|
173
|
-
}
|
|
174
|
-
return void 0;
|
|
175
|
-
}
|
|
176
|
-
function parseFilePart(part) {
|
|
177
|
-
const mimeType = extractMimeType(part.mediaType ?? part.mimeType);
|
|
178
|
-
if (!mimeType || !isImageMimeType(mimeType)) {
|
|
179
|
-
return {};
|
|
180
|
-
}
|
|
181
|
-
const data = part.data;
|
|
182
|
-
if (typeof data === "string") {
|
|
183
|
-
const content = createImageContent(mimeType, data);
|
|
184
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
185
|
-
}
|
|
186
|
-
if (data instanceof Uint8Array || typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
|
|
187
|
-
const base64 = convertBinaryToBase64(data);
|
|
188
|
-
if (!base64) {
|
|
189
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
190
|
-
}
|
|
191
|
-
const content = createImageContent(mimeType, base64);
|
|
192
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
193
|
-
}
|
|
194
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
195
|
-
}
|
|
196
|
-
function convertToClaudeCodeMessages(prompt) {
|
|
197
|
-
const messages = [];
|
|
198
|
-
const warnings = [];
|
|
199
|
-
let systemPrompt;
|
|
200
|
-
const streamingSegments = [];
|
|
201
|
-
const imageMap = /* @__PURE__ */ new Map();
|
|
202
|
-
let hasImageParts = false;
|
|
203
|
-
const addSegment = (formatted) => {
|
|
204
|
-
streamingSegments.push({ formatted });
|
|
205
|
-
return streamingSegments.length - 1;
|
|
206
|
-
};
|
|
207
|
-
const addImageForSegment = (segmentIndex, content) => {
|
|
208
|
-
hasImageParts = true;
|
|
209
|
-
if (!imageMap.has(segmentIndex)) {
|
|
210
|
-
imageMap.set(segmentIndex, []);
|
|
211
|
-
}
|
|
212
|
-
imageMap.get(segmentIndex)?.push(content);
|
|
213
|
-
};
|
|
214
|
-
for (const message of prompt) {
|
|
215
|
-
switch (message.role) {
|
|
216
|
-
case "system":
|
|
217
|
-
systemPrompt = message.content;
|
|
218
|
-
if (typeof message.content === "string" && message.content.trim().length > 0) {
|
|
219
|
-
addSegment(message.content);
|
|
220
|
-
} else {
|
|
221
|
-
addSegment("");
|
|
222
|
-
}
|
|
223
|
-
break;
|
|
224
|
-
case "user":
|
|
225
|
-
if (typeof message.content === "string") {
|
|
226
|
-
messages.push(message.content);
|
|
227
|
-
addSegment(`Human: ${message.content}`);
|
|
228
|
-
} else {
|
|
229
|
-
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
230
|
-
const segmentIndex = addSegment(textParts ? `Human: ${textParts}` : "");
|
|
231
|
-
if (textParts) {
|
|
232
|
-
messages.push(textParts);
|
|
233
|
-
}
|
|
234
|
-
for (const part of message.content) {
|
|
235
|
-
if (part.type === "image") {
|
|
236
|
-
const { content, warning } = parseImagePart(part);
|
|
237
|
-
if (content) {
|
|
238
|
-
addImageForSegment(segmentIndex, content);
|
|
239
|
-
} else if (warning) {
|
|
240
|
-
warnings.push(warning);
|
|
241
|
-
}
|
|
242
|
-
} else if (part.type === "file") {
|
|
243
|
-
const { content, warning } = parseFilePart(part);
|
|
244
|
-
if (content) {
|
|
245
|
-
addImageForSegment(segmentIndex, content);
|
|
246
|
-
} else if (warning) {
|
|
247
|
-
warnings.push(warning);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
break;
|
|
253
|
-
case "assistant": {
|
|
254
|
-
let assistantContent = "";
|
|
255
|
-
if (typeof message.content === "string") {
|
|
256
|
-
assistantContent = message.content;
|
|
257
|
-
} else {
|
|
258
|
-
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
259
|
-
if (textParts) {
|
|
260
|
-
assistantContent = textParts;
|
|
261
|
-
}
|
|
262
|
-
const toolCalls = message.content.filter((part) => part.type === "tool-call");
|
|
263
|
-
if (toolCalls.length > 0) {
|
|
264
|
-
const serializedCalls = toolCalls.map((call) => `[Tool call: ${call.toolName}(${serializeToolCallInput(call.input)})]`).join("\n");
|
|
265
|
-
assistantContent += assistantContent ? `
|
|
266
|
-
${serializedCalls}` : serializedCalls;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
const formattedAssistant = `Assistant: ${assistantContent}`;
|
|
270
|
-
messages.push(formattedAssistant);
|
|
271
|
-
addSegment(formattedAssistant);
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
case "tool":
|
|
275
|
-
for (const tool3 of message.content) {
|
|
276
|
-
if (tool3.type === "tool-approval-response") {
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
let resultText;
|
|
280
|
-
const output = tool3.output;
|
|
281
|
-
if (output.type === "text" || output.type === "error-text") {
|
|
282
|
-
resultText = output.value;
|
|
283
|
-
} else if (output.type === "json" || output.type === "error-json") {
|
|
284
|
-
resultText = JSON.stringify(output.value);
|
|
285
|
-
} else if (output.type === "execution-denied") {
|
|
286
|
-
resultText = `[Execution denied${output.reason ? `: ${output.reason}` : ""}]`;
|
|
287
|
-
} else if (output.type === "content") {
|
|
288
|
-
resultText = output.value.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
289
|
-
} else {
|
|
290
|
-
resultText = "[Unknown output type]";
|
|
291
|
-
}
|
|
292
|
-
const formattedToolResult = `Tool Result (${tool3.toolName}): ${resultText}`;
|
|
293
|
-
messages.push(formattedToolResult);
|
|
294
|
-
addSegment(formattedToolResult);
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
let finalPrompt = "";
|
|
300
|
-
if (systemPrompt) {
|
|
301
|
-
finalPrompt = systemPrompt;
|
|
302
|
-
}
|
|
303
|
-
if (messages.length > 0) {
|
|
304
|
-
const formattedMessages = [];
|
|
305
|
-
for (let i = 0; i < messages.length; i++) {
|
|
306
|
-
const msg = messages[i];
|
|
307
|
-
if (msg.startsWith("Assistant:") || msg.startsWith("Tool Result")) {
|
|
308
|
-
formattedMessages.push(msg);
|
|
309
|
-
} else {
|
|
310
|
-
formattedMessages.push(`Human: ${msg}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
if (finalPrompt) {
|
|
314
|
-
const joinedMessages = formattedMessages.join("\n\n");
|
|
315
|
-
finalPrompt = joinedMessages ? `${finalPrompt}
|
|
316
|
-
|
|
317
|
-
${joinedMessages}` : finalPrompt;
|
|
318
|
-
} else {
|
|
319
|
-
finalPrompt = formattedMessages.join("\n\n");
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
const streamingParts = [];
|
|
323
|
-
const imagePartsInOrder = [];
|
|
324
|
-
const appendImagesForIndex = (index) => {
|
|
325
|
-
const images = imageMap.get(index);
|
|
326
|
-
if (!images) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
images.forEach((image) => {
|
|
330
|
-
streamingParts.push(image);
|
|
331
|
-
imagePartsInOrder.push(image);
|
|
332
|
-
});
|
|
333
|
-
};
|
|
334
|
-
if (streamingSegments.length > 0) {
|
|
335
|
-
let accumulatedText = "";
|
|
336
|
-
let emittedText = false;
|
|
337
|
-
const flushText = () => {
|
|
338
|
-
if (!accumulatedText) {
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
streamingParts.push({ type: "text", text: accumulatedText });
|
|
342
|
-
accumulatedText = "";
|
|
343
|
-
emittedText = true;
|
|
344
|
-
};
|
|
345
|
-
streamingSegments.forEach((segment, index) => {
|
|
346
|
-
const segmentText = segment.formatted;
|
|
347
|
-
if (segmentText) {
|
|
348
|
-
if (!accumulatedText) {
|
|
349
|
-
accumulatedText = emittedText ? `
|
|
350
|
-
|
|
351
|
-
${segmentText}` : segmentText;
|
|
352
|
-
} else {
|
|
353
|
-
accumulatedText += `
|
|
354
|
-
|
|
355
|
-
${segmentText}`;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
if (imageMap.has(index)) {
|
|
359
|
-
flushText();
|
|
360
|
-
appendImagesForIndex(index);
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
flushText();
|
|
364
|
-
}
|
|
365
|
-
return {
|
|
366
|
-
messagesPrompt: finalPrompt,
|
|
367
|
-
systemPrompt,
|
|
368
|
-
...warnings.length > 0 && { warnings },
|
|
369
|
-
streamingContentParts: streamingParts.length > 0 ? streamingParts : [
|
|
370
|
-
{ type: "text", text: finalPrompt },
|
|
371
|
-
...imagePartsInOrder
|
|
372
|
-
],
|
|
373
|
-
hasImageParts
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// src/errors.ts
|
|
378
|
-
var import_provider = require("@ai-sdk/provider");
|
|
379
|
-
function createAPICallError({
|
|
380
|
-
message,
|
|
381
|
-
code,
|
|
382
|
-
exitCode,
|
|
383
|
-
stderr,
|
|
384
|
-
promptExcerpt,
|
|
385
|
-
isRetryable = false
|
|
386
|
-
}) {
|
|
387
|
-
const metadata = {
|
|
388
|
-
code,
|
|
389
|
-
exitCode,
|
|
390
|
-
stderr,
|
|
391
|
-
promptExcerpt
|
|
392
|
-
};
|
|
393
|
-
return new import_provider.APICallError({
|
|
394
|
-
message,
|
|
395
|
-
isRetryable,
|
|
396
|
-
url: "claude-code-cli://command",
|
|
397
|
-
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
|
|
398
|
-
data: metadata
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
function createAuthenticationError({ message }) {
|
|
402
|
-
return new import_provider.LoadAPIKeyError({
|
|
403
|
-
message: message || "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
function createTimeoutError({
|
|
407
|
-
message,
|
|
408
|
-
promptExcerpt,
|
|
409
|
-
timeoutMs
|
|
410
|
-
}) {
|
|
411
|
-
const metadata = {
|
|
412
|
-
code: "TIMEOUT",
|
|
413
|
-
promptExcerpt
|
|
414
|
-
};
|
|
415
|
-
return new import_provider.APICallError({
|
|
416
|
-
message,
|
|
417
|
-
isRetryable: true,
|
|
418
|
-
url: "claude-code-cli://command",
|
|
419
|
-
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
|
|
420
|
-
data: timeoutMs !== void 0 ? { ...metadata, timeoutMs } : metadata
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
function isAuthenticationError(error) {
|
|
424
|
-
if (error instanceof import_provider.LoadAPIKeyError) return true;
|
|
425
|
-
if (error instanceof import_provider.APICallError && error.data?.exitCode === 401)
|
|
426
|
-
return true;
|
|
427
|
-
return false;
|
|
428
|
-
}
|
|
429
|
-
function isTimeoutError(error) {
|
|
430
|
-
if (error instanceof import_provider.APICallError && error.data?.code === "TIMEOUT")
|
|
431
|
-
return true;
|
|
432
|
-
return false;
|
|
433
|
-
}
|
|
434
|
-
function getErrorMetadata(error) {
|
|
435
|
-
if (error instanceof import_provider.APICallError && error.data) {
|
|
436
|
-
return error.data;
|
|
437
|
-
}
|
|
438
|
-
return void 0;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// src/map-claude-code-finish-reason.ts
|
|
442
|
-
function mapClaudeCodeFinishReason(subtype, stopReason) {
|
|
443
|
-
if (stopReason != null) {
|
|
444
|
-
switch (stopReason) {
|
|
445
|
-
case "end_turn":
|
|
446
|
-
return { unified: "stop", raw: "end_turn" };
|
|
447
|
-
case "max_tokens":
|
|
448
|
-
return { unified: "length", raw: "max_tokens" };
|
|
449
|
-
case "stop_sequence":
|
|
450
|
-
return { unified: "stop", raw: "stop_sequence" };
|
|
451
|
-
case "tool_use":
|
|
452
|
-
return { unified: "tool-calls", raw: "tool_use" };
|
|
453
|
-
default:
|
|
454
|
-
break;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
const raw = stopReason ?? subtype;
|
|
458
|
-
switch (subtype) {
|
|
459
|
-
case "success":
|
|
460
|
-
return { unified: "stop", raw };
|
|
461
|
-
case "error_max_turns":
|
|
462
|
-
return { unified: "length", raw };
|
|
463
|
-
case "error_during_execution":
|
|
464
|
-
return { unified: "error", raw };
|
|
465
|
-
case void 0:
|
|
466
|
-
return { unified: "stop", raw };
|
|
467
|
-
default:
|
|
468
|
-
return { unified: "other", raw };
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// src/validation.ts
|
|
473
|
-
var import_zod = require("zod");
|
|
474
|
-
var import_fs = require("fs");
|
|
475
|
-
function isBlankResume(value) {
|
|
476
|
-
return typeof value === "string" && value.trim() === "";
|
|
477
|
-
}
|
|
478
|
-
var loggerFunctionSchema = import_zod.z.object({
|
|
479
|
-
debug: import_zod.z.any().refine((val) => typeof val === "function", {
|
|
480
|
-
message: "debug must be a function"
|
|
481
|
-
}),
|
|
482
|
-
info: import_zod.z.any().refine((val) => typeof val === "function", {
|
|
483
|
-
message: "info must be a function"
|
|
484
|
-
}),
|
|
485
|
-
warn: import_zod.z.any().refine((val) => typeof val === "function", {
|
|
486
|
-
message: "warn must be a function"
|
|
487
|
-
}),
|
|
488
|
-
error: import_zod.z.any().refine((val) => typeof val === "function", {
|
|
489
|
-
message: "error must be a function"
|
|
490
|
-
})
|
|
491
|
-
});
|
|
492
|
-
var claudeCodeSettingsSchema = import_zod.z.object({
|
|
493
|
-
pathToClaudeCodeExecutable: import_zod.z.string().optional(),
|
|
494
|
-
customSystemPrompt: import_zod.z.string().optional(),
|
|
495
|
-
appendSystemPrompt: import_zod.z.string().optional(),
|
|
496
|
-
systemPrompt: import_zod.z.union([
|
|
497
|
-
import_zod.z.string(),
|
|
498
|
-
import_zod.z.array(import_zod.z.string()),
|
|
499
|
-
import_zod.z.object({
|
|
500
|
-
type: import_zod.z.literal("preset"),
|
|
501
|
-
preset: import_zod.z.literal("claude_code"),
|
|
502
|
-
append: import_zod.z.string().optional(),
|
|
503
|
-
excludeDynamicSections: import_zod.z.boolean().optional()
|
|
504
|
-
})
|
|
505
|
-
]).optional(),
|
|
506
|
-
maxTurns: import_zod.z.number().int().min(1).max(100).optional(),
|
|
507
|
-
maxThinkingTokens: import_zod.z.number().int().positive().max(1e5).optional(),
|
|
508
|
-
thinking: import_zod.z.union([
|
|
509
|
-
import_zod.z.object({
|
|
510
|
-
type: import_zod.z.literal("adaptive"),
|
|
511
|
-
display: import_zod.z.enum(["summarized", "omitted"]).optional()
|
|
512
|
-
}).strict(),
|
|
513
|
-
import_zod.z.object({
|
|
514
|
-
type: import_zod.z.literal("enabled"),
|
|
515
|
-
budgetTokens: import_zod.z.number().int().positive().optional(),
|
|
516
|
-
display: import_zod.z.enum(["summarized", "omitted"]).optional()
|
|
517
|
-
}).strict(),
|
|
518
|
-
import_zod.z.object({ type: import_zod.z.literal("disabled") }).strict()
|
|
519
|
-
]).optional(),
|
|
520
|
-
effort: import_zod.z.enum(["low", "medium", "high", "xhigh", "max"]).optional(),
|
|
521
|
-
promptSuggestions: import_zod.z.boolean().optional(),
|
|
522
|
-
cwd: import_zod.z.string().refine(
|
|
523
|
-
(val) => {
|
|
524
|
-
if (typeof process === "undefined" || !process.versions?.node) {
|
|
525
|
-
return true;
|
|
526
|
-
}
|
|
527
|
-
return !val || (0, import_fs.existsSync)(val);
|
|
528
|
-
},
|
|
529
|
-
{ message: "Working directory must exist" }
|
|
530
|
-
).optional(),
|
|
531
|
-
executable: import_zod.z.enum(["bun", "deno", "node"]).optional(),
|
|
532
|
-
executableArgs: import_zod.z.array(import_zod.z.string()).optional(),
|
|
533
|
-
// Mirrors the SDK 0.3.x PermissionMode union ('auto' and 'dontAsk' were
|
|
534
|
-
// added in 0.3.x; 'delegate' was dropped AND is rejected by the CLI's
|
|
535
|
-
// --permission-mode flag parser, so it is rejected here too).
|
|
536
|
-
permissionMode: import_zod.z.enum(["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"]).optional(),
|
|
537
|
-
permissionPromptToolName: import_zod.z.string().optional(),
|
|
538
|
-
continue: import_zod.z.boolean().optional(),
|
|
539
|
-
resume: import_zod.z.string().optional(),
|
|
540
|
-
// The CLI rejects --session-id values that are not valid UUIDs, so
|
|
541
|
-
// enforce the UUID shape here instead of failing at query time.
|
|
542
|
-
sessionId: import_zod.z.string().refine(
|
|
543
|
-
(val) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(val),
|
|
544
|
-
{ message: "sessionId must be a valid UUID (the CLI rejects non-UUID session IDs)" }
|
|
545
|
-
).optional(),
|
|
546
|
-
allowedTools: import_zod.z.array(import_zod.z.string()).optional(),
|
|
547
|
-
disallowedTools: import_zod.z.array(import_zod.z.string()).optional(),
|
|
548
|
-
betas: import_zod.z.array(import_zod.z.string()).optional(),
|
|
549
|
-
allowDangerouslySkipPermissions: import_zod.z.boolean().optional(),
|
|
550
|
-
enableFileCheckpointing: import_zod.z.boolean().optional(),
|
|
551
|
-
maxBudgetUsd: import_zod.z.number().min(0).optional(),
|
|
552
|
-
plugins: import_zod.z.array(
|
|
553
|
-
import_zod.z.object({
|
|
554
|
-
// SDK SdkPluginConfig: only 'local' is supported; the SDK throws
|
|
555
|
-
// 'Unsupported plugin type' at query time for anything else.
|
|
556
|
-
type: import_zod.z.literal("local"),
|
|
557
|
-
path: import_zod.z.string()
|
|
558
|
-
}).passthrough()
|
|
559
|
-
).optional(),
|
|
560
|
-
resumeSessionAt: import_zod.z.string().optional(),
|
|
561
|
-
sandbox: import_zod.z.any().refine((val) => val === void 0 || typeof val === "object", {
|
|
562
|
-
message: "sandbox must be an object"
|
|
563
|
-
}).optional(),
|
|
564
|
-
tools: import_zod.z.union([
|
|
565
|
-
import_zod.z.array(import_zod.z.string()),
|
|
566
|
-
import_zod.z.object({
|
|
567
|
-
type: import_zod.z.literal("preset"),
|
|
568
|
-
preset: import_zod.z.literal("claude_code")
|
|
569
|
-
})
|
|
570
|
-
]).optional(),
|
|
571
|
-
skills: import_zod.z.union([import_zod.z.array(import_zod.z.string()), import_zod.z.literal("all")]).optional(),
|
|
572
|
-
settings: import_zod.z.union([
|
|
573
|
-
import_zod.z.string(),
|
|
574
|
-
import_zod.z.record(import_zod.z.string(), import_zod.z.any())
|
|
575
|
-
// inline Settings object
|
|
576
|
-
]).optional(),
|
|
577
|
-
managedSettings: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional(),
|
|
578
|
-
toolAliases: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
579
|
-
toolConfig: import_zod.z.object({
|
|
580
|
-
askUserQuestion: import_zod.z.object({
|
|
581
|
-
previewFormat: import_zod.z.enum(["markdown", "html"]).optional()
|
|
582
|
-
}).passthrough().optional()
|
|
583
|
-
}).passthrough().optional(),
|
|
584
|
-
planModeInstructions: import_zod.z.string().optional(),
|
|
585
|
-
title: import_zod.z.string().optional(),
|
|
586
|
-
forwardSubagentText: import_zod.z.boolean().optional(),
|
|
587
|
-
agentProgressSummaries: import_zod.z.boolean().optional(),
|
|
588
|
-
includeHookEvents: import_zod.z.boolean().optional(),
|
|
589
|
-
taskBudget: import_zod.z.object({ total: import_zod.z.number().positive() }).strict().optional(),
|
|
590
|
-
sessionStore: import_zod.z.any().refine(
|
|
591
|
-
(val) => val === void 0 || typeof val === "object" && val !== null && typeof val.append === "function" && typeof val.load === "function",
|
|
592
|
-
{ message: "sessionStore must be an object with append() and load() functions" }
|
|
593
|
-
).optional(),
|
|
594
|
-
sessionStoreFlush: import_zod.z.enum(["batched", "eager"]).optional(),
|
|
595
|
-
loadTimeoutMs: import_zod.z.number().int().positive().optional(),
|
|
596
|
-
settingSources: import_zod.z.array(import_zod.z.enum(["user", "project", "local"])).optional(),
|
|
597
|
-
streamingInput: import_zod.z.enum(["auto", "always", "off"]).optional(),
|
|
598
|
-
// Hooks and tool-permission callback (permissive validation of shapes)
|
|
599
|
-
canUseTool: import_zod.z.any().refine((v) => v === void 0 || typeof v === "function", {
|
|
600
|
-
message: "canUseTool must be a function"
|
|
601
|
-
}).optional(),
|
|
602
|
-
onUserDialog: import_zod.z.any().refine((v) => v === void 0 || typeof v === "function", {
|
|
603
|
-
message: "onUserDialog must be a function"
|
|
604
|
-
}).optional(),
|
|
605
|
-
supportedDialogKinds: import_zod.z.array(import_zod.z.string()).optional(),
|
|
606
|
-
hooks: import_zod.z.record(
|
|
607
|
-
import_zod.z.string(),
|
|
608
|
-
import_zod.z.array(
|
|
609
|
-
import_zod.z.object({
|
|
610
|
-
matcher: import_zod.z.string().optional(),
|
|
611
|
-
hooks: import_zod.z.array(import_zod.z.any()).nonempty()
|
|
612
|
-
})
|
|
613
|
-
)
|
|
614
|
-
).optional(),
|
|
615
|
-
mcpServers: import_zod.z.record(
|
|
616
|
-
import_zod.z.string(),
|
|
617
|
-
import_zod.z.union([
|
|
618
|
-
// McpStdioServerConfig
|
|
619
|
-
import_zod.z.object({
|
|
620
|
-
type: import_zod.z.literal("stdio").optional(),
|
|
621
|
-
command: import_zod.z.string(),
|
|
622
|
-
args: import_zod.z.array(import_zod.z.string()).optional(),
|
|
623
|
-
env: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
|
|
624
|
-
}),
|
|
625
|
-
// McpSSEServerConfig
|
|
626
|
-
import_zod.z.object({
|
|
627
|
-
type: import_zod.z.literal("sse"),
|
|
628
|
-
url: import_zod.z.string(),
|
|
629
|
-
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
|
|
630
|
-
}),
|
|
631
|
-
// McpHttpServerConfig
|
|
632
|
-
import_zod.z.object({
|
|
633
|
-
type: import_zod.z.literal("http"),
|
|
634
|
-
url: import_zod.z.string(),
|
|
635
|
-
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
|
|
636
|
-
}),
|
|
637
|
-
// McpSdkServerConfig (in-process custom tools)
|
|
638
|
-
import_zod.z.object({
|
|
639
|
-
type: import_zod.z.literal("sdk"),
|
|
640
|
-
name: import_zod.z.string(),
|
|
641
|
-
instance: import_zod.z.any()
|
|
642
|
-
})
|
|
643
|
-
])
|
|
644
|
-
).optional(),
|
|
645
|
-
verbose: import_zod.z.boolean().optional(),
|
|
646
|
-
debug: import_zod.z.boolean().optional(),
|
|
647
|
-
debugFile: import_zod.z.string().optional(),
|
|
648
|
-
logger: import_zod.z.union([import_zod.z.literal(false), loggerFunctionSchema]).optional(),
|
|
649
|
-
env: import_zod.z.record(import_zod.z.string(), import_zod.z.string().optional()).optional(),
|
|
650
|
-
additionalDirectories: import_zod.z.array(import_zod.z.string()).optional(),
|
|
651
|
-
agents: import_zod.z.record(
|
|
652
|
-
import_zod.z.string(),
|
|
653
|
-
import_zod.z.object({
|
|
654
|
-
description: import_zod.z.string(),
|
|
655
|
-
tools: import_zod.z.array(import_zod.z.string()).optional(),
|
|
656
|
-
disallowedTools: import_zod.z.array(import_zod.z.string()).optional(),
|
|
657
|
-
prompt: import_zod.z.string(),
|
|
658
|
-
// SDK 0.3.x AgentDefinition accepts any model alias or full model ID
|
|
659
|
-
model: import_zod.z.string().optional(),
|
|
660
|
-
mcpServers: import_zod.z.array(
|
|
661
|
-
import_zod.z.union([
|
|
662
|
-
import_zod.z.string(),
|
|
663
|
-
import_zod.z.record(import_zod.z.string(), import_zod.z.any())
|
|
664
|
-
// McpServerConfigForProcessTransport
|
|
665
|
-
])
|
|
666
|
-
).optional(),
|
|
667
|
-
criticalSystemReminder_EXPERIMENTAL: import_zod.z.string().optional()
|
|
668
|
-
}).passthrough()
|
|
669
|
-
).optional(),
|
|
670
|
-
includePartialMessages: import_zod.z.boolean().optional(),
|
|
671
|
-
fallbackModel: import_zod.z.string().optional(),
|
|
672
|
-
forkSession: import_zod.z.boolean().optional(),
|
|
673
|
-
stderr: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
674
|
-
message: "stderr must be a function"
|
|
675
|
-
}).optional(),
|
|
676
|
-
strictMcpConfig: import_zod.z.boolean().optional(),
|
|
677
|
-
extraArgs: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.null()])).optional(),
|
|
678
|
-
persistSession: import_zod.z.boolean().optional(),
|
|
679
|
-
spawnClaudeCodeProcess: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
680
|
-
message: "spawnClaudeCodeProcess must be a function"
|
|
681
|
-
}).optional(),
|
|
682
|
-
sdkOptions: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional(),
|
|
683
|
-
maxToolResultSize: import_zod.z.number().int().min(100).max(1e6).optional(),
|
|
684
|
-
// Callback invoked when Query object is created - for mid-stream injection via streamInput()
|
|
685
|
-
onQueryCreated: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
686
|
-
message: "onQueryCreated must be a function"
|
|
687
|
-
}).optional(),
|
|
688
|
-
onStreamStart: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
689
|
-
message: "onStreamStart must be a function"
|
|
690
|
-
}).optional(),
|
|
691
|
-
// Callback invoked with the predicted next user prompt (active unless promptSuggestions: false)
|
|
692
|
-
onPromptSuggestion: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
693
|
-
message: "onPromptSuggestion must be a function"
|
|
694
|
-
}).optional()
|
|
695
|
-
}).strict();
|
|
696
|
-
function validateModelId(modelId) {
|
|
697
|
-
const knownModels = ["opus", "sonnet", "haiku", "fable"];
|
|
698
|
-
if (!modelId || modelId.trim() === "") {
|
|
699
|
-
throw new Error("Model ID cannot be empty");
|
|
700
|
-
}
|
|
701
|
-
if (!knownModels.includes(modelId)) {
|
|
702
|
-
return `Unknown model ID: '${modelId}'. Proceeding with custom model. Known models are: ${knownModels.join(", ")}`;
|
|
703
|
-
}
|
|
704
|
-
return void 0;
|
|
705
|
-
}
|
|
706
|
-
function validateSettings(settings) {
|
|
707
|
-
const warnings = [];
|
|
708
|
-
const errors = [];
|
|
709
|
-
try {
|
|
710
|
-
const result = claudeCodeSettingsSchema.safeParse(settings);
|
|
711
|
-
if (!result.success) {
|
|
712
|
-
const errorObject = result.error;
|
|
713
|
-
const issues = errorObject.errors || errorObject.issues || [];
|
|
714
|
-
issues.forEach((err) => {
|
|
715
|
-
const path = err.path.join(".");
|
|
716
|
-
errors.push(`${path ? `${path}: ` : ""}${err.message}`);
|
|
717
|
-
});
|
|
718
|
-
return { valid: false, warnings, errors };
|
|
719
|
-
}
|
|
720
|
-
const validSettings = result.data;
|
|
721
|
-
const sdkOptionsRecord = validSettings.sdkOptions;
|
|
722
|
-
const effective = (key) => {
|
|
723
|
-
const override = sdkOptionsRecord?.[key];
|
|
724
|
-
return override !== void 0 ? override : validSettings[key];
|
|
725
|
-
};
|
|
726
|
-
const effSessionStore = effective("sessionStore");
|
|
727
|
-
const effectiveResumeId = () => {
|
|
728
|
-
for (const candidate of [sdkOptionsRecord?.resume, validSettings.resume]) {
|
|
729
|
-
if (typeof candidate === "string" && !isBlankResume(candidate)) {
|
|
730
|
-
return candidate;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
return void 0;
|
|
734
|
-
};
|
|
735
|
-
if (effSessionStore !== void 0 && effective("persistSession") === false) {
|
|
736
|
-
errors.push(
|
|
737
|
-
"sessionStore cannot be combined with persistSession: false. Transcript mirroring requires local session writes; remove persistSession: false or drop sessionStore."
|
|
738
|
-
);
|
|
739
|
-
return { valid: false, warnings, errors };
|
|
740
|
-
}
|
|
741
|
-
if (effSessionStore !== void 0 && effective("enableFileCheckpointing") === true) {
|
|
742
|
-
errors.push(
|
|
743
|
-
"sessionStore cannot be combined with enableFileCheckpointing: true. Checkpoint backup blobs are not mirrored to the store (rewindFiles() fails after a store-backed resume); remove enableFileCheckpointing or drop sessionStore."
|
|
744
|
-
);
|
|
745
|
-
return { valid: false, warnings, errors };
|
|
746
|
-
}
|
|
747
|
-
if (effective("continue") === true && effSessionStore !== void 0 && effectiveResumeId() === void 0 && typeof effSessionStore.listSessions !== "function") {
|
|
748
|
-
errors.push(
|
|
749
|
-
"continue: true with sessionStore requires the store to implement listSessions() (used to discover the most recent session). Implement listSessions(), pass resume with an explicit session ID, or drop continue."
|
|
750
|
-
);
|
|
751
|
-
return { valid: false, warnings, errors };
|
|
752
|
-
}
|
|
753
|
-
const effSettingsOption = effective("settings");
|
|
754
|
-
if (effective("sandbox") !== void 0 && typeof effSettingsOption === "string" && !(effSettingsOption.trim().startsWith("{") && effSettingsOption.trim().endsWith("}"))) {
|
|
755
|
-
errors.push(
|
|
756
|
-
"sandbox cannot be combined with a settings file path. Pass settings as an inline Settings object, or move the sandbox configuration into the settings file and drop the sandbox option."
|
|
757
|
-
);
|
|
758
|
-
return { valid: false, warnings, errors };
|
|
759
|
-
}
|
|
760
|
-
if (effective("sessionId") !== void 0 && effective("forkSession") !== true && (effective("continue") === true || effectiveResumeId() !== void 0)) {
|
|
761
|
-
errors.push(
|
|
762
|
-
"sessionId cannot be combined with continue or resume unless forkSession: true is also set (it then names the forked session's ID). Remove sessionId, remove continue/resume, or add forkSession: true."
|
|
763
|
-
);
|
|
764
|
-
return { valid: false, warnings, errors };
|
|
765
|
-
}
|
|
766
|
-
if (validSettings.maxTurns && validSettings.maxTurns > 20) {
|
|
767
|
-
warnings.push(
|
|
768
|
-
`High maxTurns value (${validSettings.maxTurns}) may lead to long-running conversations`
|
|
769
|
-
);
|
|
770
|
-
}
|
|
771
|
-
if (validSettings.maxThinkingTokens && validSettings.maxThinkingTokens > 5e4) {
|
|
772
|
-
warnings.push(
|
|
773
|
-
`Very high maxThinkingTokens (${validSettings.maxThinkingTokens}) may increase response time`
|
|
774
|
-
);
|
|
775
|
-
}
|
|
776
|
-
if (validSettings.allowedTools && validSettings.disallowedTools) {
|
|
777
|
-
warnings.push(
|
|
778
|
-
"Both allowedTools and disallowedTools are specified. Only allowedTools will be used."
|
|
779
|
-
);
|
|
780
|
-
}
|
|
781
|
-
const validateToolNames = (tools, type) => {
|
|
782
|
-
tools.forEach((tool3) => {
|
|
783
|
-
if (!/^[a-zA-Z_][a-zA-Z0-9_]*(\([^)]*\))?$/.test(tool3) && !tool3.startsWith("mcp__")) {
|
|
784
|
-
warnings.push(`Unusual ${type} tool name format: '${tool3}'`);
|
|
785
|
-
}
|
|
786
|
-
});
|
|
787
|
-
};
|
|
788
|
-
if (validSettings.allowedTools) {
|
|
789
|
-
validateToolNames(validSettings.allowedTools, "allowed");
|
|
790
|
-
}
|
|
791
|
-
if (validSettings.disallowedTools) {
|
|
792
|
-
validateToolNames(validSettings.disallowedTools, "disallowed");
|
|
793
|
-
}
|
|
794
|
-
const effDialogKinds = effective("supportedDialogKinds");
|
|
795
|
-
if (Array.isArray(effDialogKinds) && effDialogKinds.length > 0 && effective("onUserDialog") == null) {
|
|
796
|
-
errors.push(
|
|
797
|
-
"supportedDialogKinds is set without onUserDialog. The SDK requires the onUserDialog callback to render declared dialog kinds and throws when a non-empty list is passed without it; provide onUserDialog or remove supportedDialogKinds."
|
|
798
|
-
);
|
|
799
|
-
return { valid: false, warnings, errors };
|
|
800
|
-
}
|
|
801
|
-
const effAllowedTools = effective("allowedTools");
|
|
802
|
-
if (Array.isArray(effAllowedTools) && effAllowedTools.includes("Skill") && !effective("settingSources")) {
|
|
803
|
-
warnings.push(
|
|
804
|
-
"allowedTools includes 'Skill' but settingSources is not set. Skills require settingSources (e.g., ['user', 'project']) to load skill definitions."
|
|
805
|
-
);
|
|
806
|
-
}
|
|
807
|
-
if (validSettings.agents) {
|
|
808
|
-
const knownAgentModelAliases = ["sonnet", "opus", "haiku", "fable", "inherit"];
|
|
809
|
-
for (const [agentName, agent] of Object.entries(validSettings.agents)) {
|
|
810
|
-
const agentModel = agent.model;
|
|
811
|
-
if (agentModel !== void 0 && !knownAgentModelAliases.includes(agentModel) && !agentModel.includes("-")) {
|
|
812
|
-
warnings.push(
|
|
813
|
-
`Unknown model alias '${agentModel}' for agent '${agentName}'. Known aliases are: ${knownAgentModelAliases.join(", ")}; full model IDs (e.g. 'claude-sonnet-4-5') are also accepted.`
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
return { valid: true, warnings, errors };
|
|
819
|
-
} catch (error) {
|
|
820
|
-
errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
821
|
-
return { valid: false, warnings, errors };
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
function validatePrompt(prompt) {
|
|
825
|
-
const MAX_PROMPT_LENGTH = 1e5;
|
|
826
|
-
if (prompt.length > MAX_PROMPT_LENGTH) {
|
|
827
|
-
return `Very long prompt (${prompt.length} characters) may cause performance issues or timeouts`;
|
|
828
|
-
}
|
|
829
|
-
return void 0;
|
|
830
|
-
}
|
|
831
|
-
function validateSessionId(sessionId) {
|
|
832
|
-
if (sessionId && !/^[a-zA-Z0-9-_]+$/.test(sessionId)) {
|
|
833
|
-
return `Unusual session ID format. This may cause issues with session resumption.`;
|
|
834
|
-
}
|
|
835
|
-
return void 0;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// src/sanitize-json-schema.ts
|
|
839
|
-
var SUBSCHEMA_MAP_KEYWORDS = [
|
|
840
|
-
"properties",
|
|
841
|
-
"patternProperties",
|
|
842
|
-
"$defs",
|
|
843
|
-
"definitions",
|
|
844
|
-
"dependentSchemas",
|
|
845
|
-
// draft-07 `dependencies` values are either subschemas (schema form) or
|
|
846
|
-
// arrays of property-name strings (array form); sanitizeNode passes
|
|
847
|
-
// string arrays through untouched, so walking both forms is safe.
|
|
848
|
-
"dependencies"
|
|
849
|
-
];
|
|
850
|
-
var SUBSCHEMA_KEYWORDS = [
|
|
851
|
-
"items",
|
|
852
|
-
// may also be an array of subschemas (draft-07 tuple form)
|
|
853
|
-
"additionalItems",
|
|
854
|
-
"additionalProperties",
|
|
855
|
-
"unevaluatedItems",
|
|
856
|
-
"unevaluatedProperties",
|
|
857
|
-
"not",
|
|
858
|
-
"contains",
|
|
859
|
-
"propertyNames",
|
|
860
|
-
"contentSchema",
|
|
861
|
-
"if",
|
|
862
|
-
"then",
|
|
863
|
-
"else"
|
|
864
|
-
];
|
|
865
|
-
var SUBSCHEMA_LIST_KEYWORDS = ["prefixItems", "anyOf", "oneOf", "allOf"];
|
|
866
|
-
function sanitizeJsonSchemaForOutputFormat(schema) {
|
|
867
|
-
const strippedFormatPaths = [];
|
|
868
|
-
const sanitized = sanitizeNode(schema, "#", /* @__PURE__ */ new WeakSet(), strippedFormatPaths);
|
|
869
|
-
return {
|
|
870
|
-
schema: sanitized ?? schema,
|
|
871
|
-
strippedFormatPaths
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
function sanitizeNode(node, path, visiting, strippedFormatPaths) {
|
|
875
|
-
if (typeof node !== "object" || node === null) {
|
|
876
|
-
return node;
|
|
877
|
-
}
|
|
878
|
-
if (visiting.has(node)) {
|
|
879
|
-
return node;
|
|
880
|
-
}
|
|
881
|
-
visiting.add(node);
|
|
882
|
-
try {
|
|
883
|
-
if (Array.isArray(node)) {
|
|
884
|
-
return sanitizeList(node, path, visiting, strippedFormatPaths);
|
|
885
|
-
}
|
|
886
|
-
const record = node;
|
|
887
|
-
let result = record;
|
|
888
|
-
const setKey = (key, value) => {
|
|
889
|
-
if (result === record) {
|
|
890
|
-
result = { ...record };
|
|
891
|
-
}
|
|
892
|
-
result[key] = value;
|
|
893
|
-
};
|
|
894
|
-
if (typeof record.format === "string") {
|
|
895
|
-
const format = record.format;
|
|
896
|
-
const existingDescription = record.description;
|
|
897
|
-
result = { ...record };
|
|
898
|
-
delete result.format;
|
|
899
|
-
if (typeof existingDescription === "string" && existingDescription.length > 0) {
|
|
900
|
-
result.description = `${existingDescription} (expected format: ${format})`;
|
|
901
|
-
} else if (existingDescription === void 0 || existingDescription === "") {
|
|
902
|
-
result.description = `Expected format: ${format}`;
|
|
903
|
-
}
|
|
904
|
-
strippedFormatPaths.push(path);
|
|
905
|
-
}
|
|
906
|
-
for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
|
|
907
|
-
const map = record[keyword];
|
|
908
|
-
if (typeof map !== "object" || map === null || Array.isArray(map)) continue;
|
|
909
|
-
const mapRecord = map;
|
|
910
|
-
let newMap = mapRecord;
|
|
911
|
-
for (const [name, child] of Object.entries(mapRecord)) {
|
|
912
|
-
const sanitizedChild = sanitizeNode(
|
|
913
|
-
child,
|
|
914
|
-
`${path}/${keyword}/${name}`,
|
|
915
|
-
visiting,
|
|
916
|
-
strippedFormatPaths
|
|
917
|
-
);
|
|
918
|
-
if (sanitizedChild !== child) {
|
|
919
|
-
if (newMap === mapRecord) {
|
|
920
|
-
newMap = { ...mapRecord };
|
|
921
|
-
}
|
|
922
|
-
newMap[name] = sanitizedChild;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
if (newMap !== mapRecord) {
|
|
926
|
-
setKey(keyword, newMap);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
for (const keyword of SUBSCHEMA_KEYWORDS) {
|
|
930
|
-
const child = record[keyword];
|
|
931
|
-
if (typeof child !== "object" || child === null) continue;
|
|
932
|
-
const sanitizedChild = sanitizeNode(
|
|
933
|
-
child,
|
|
934
|
-
`${path}/${keyword}`,
|
|
935
|
-
visiting,
|
|
936
|
-
strippedFormatPaths
|
|
937
|
-
);
|
|
938
|
-
if (sanitizedChild !== child) {
|
|
939
|
-
setKey(keyword, sanitizedChild);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
|
|
943
|
-
const list = record[keyword];
|
|
944
|
-
if (!Array.isArray(list)) continue;
|
|
945
|
-
const sanitizedList = sanitizeList(list, `${path}/${keyword}`, visiting, strippedFormatPaths);
|
|
946
|
-
if (sanitizedList !== list) {
|
|
947
|
-
setKey(keyword, sanitizedList);
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
return result;
|
|
951
|
-
} finally {
|
|
952
|
-
visiting.delete(node);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
function sanitizeList(list, path, visiting, strippedFormatPaths) {
|
|
956
|
-
let result = list;
|
|
957
|
-
for (let i = 0; i < list.length; i++) {
|
|
958
|
-
const sanitizedChild = sanitizeNode(list[i], `${path}/${i}`, visiting, strippedFormatPaths);
|
|
959
|
-
if (sanitizedChild !== list[i]) {
|
|
960
|
-
if (result === list) {
|
|
961
|
-
result = [...list];
|
|
962
|
-
}
|
|
963
|
-
result[i] = sanitizedChild;
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
return result;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// src/logger.ts
|
|
970
|
-
var defaultLogger = {
|
|
971
|
-
// eslint-disable-next-line no-console
|
|
972
|
-
debug: (message) => console.debug(`[DEBUG] ${message}`),
|
|
973
|
-
// eslint-disable-next-line no-console
|
|
974
|
-
info: (message) => console.info(`[INFO] ${message}`),
|
|
975
|
-
warn: (message) => console.warn(`[WARN] ${message}`),
|
|
976
|
-
error: (message) => console.error(`[ERROR] ${message}`)
|
|
977
|
-
};
|
|
978
|
-
var noopLogger = {
|
|
979
|
-
debug: () => {
|
|
980
|
-
},
|
|
981
|
-
info: () => {
|
|
982
|
-
},
|
|
983
|
-
warn: () => {
|
|
984
|
-
},
|
|
985
|
-
error: () => {
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
function getLogger(logger) {
|
|
989
|
-
if (logger === false) {
|
|
990
|
-
return noopLogger;
|
|
991
|
-
}
|
|
992
|
-
if (logger === void 0) {
|
|
993
|
-
return defaultLogger;
|
|
994
|
-
}
|
|
995
|
-
return logger;
|
|
996
|
-
}
|
|
997
|
-
function createVerboseLogger(logger, verbose = false) {
|
|
998
|
-
if (verbose) {
|
|
999
|
-
return logger;
|
|
1000
|
-
}
|
|
1001
|
-
return {
|
|
1002
|
-
debug: () => {
|
|
1003
|
-
},
|
|
1004
|
-
// No-op when not verbose
|
|
1005
|
-
info: () => {
|
|
1006
|
-
},
|
|
1007
|
-
// No-op when not verbose
|
|
1008
|
-
warn: logger.warn.bind(logger),
|
|
1009
|
-
error: logger.error.bind(logger)
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// src/claude-code-language-model.ts
|
|
1014
|
-
var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
|
|
1015
|
-
var PROVIDER_VERSION = "3.5.2";
|
|
1016
|
-
var DEFAULT_CLIENT_APP = `ai-sdk-provider-claude-code/${PROVIDER_VERSION}`;
|
|
1017
|
-
var CLAUDE_CODE_TRUNCATION_WARNING = "Claude Code SDK output ended unexpectedly; returning truncated response from buffered text. Await upstream fix to avoid data loss.";
|
|
1018
|
-
var MIN_TRUNCATION_LENGTH = 512;
|
|
1019
|
-
function isClaudeCodeTruncationError(error, bufferedText) {
|
|
1020
|
-
const isSyntaxError = error instanceof SyntaxError || // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1021
|
-
typeof error?.name === "string" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1022
|
-
error.name.toLowerCase() === "syntaxerror";
|
|
1023
|
-
if (!isSyntaxError) {
|
|
1024
|
-
return false;
|
|
1025
|
-
}
|
|
1026
|
-
if (!bufferedText) {
|
|
1027
|
-
return false;
|
|
1028
|
-
}
|
|
1029
|
-
const rawMessage = typeof error?.message === "string" ? error.message : "";
|
|
1030
|
-
const message = rawMessage.toLowerCase();
|
|
1031
|
-
const truncationIndicators = [
|
|
1032
|
-
"unexpected end of json input",
|
|
1033
|
-
"unexpected end of input",
|
|
1034
|
-
"unexpected end of string",
|
|
1035
|
-
"unexpected eof",
|
|
1036
|
-
"end of file",
|
|
1037
|
-
"unterminated string",
|
|
1038
|
-
"unterminated string constant"
|
|
1039
|
-
];
|
|
1040
|
-
if (!truncationIndicators.some((indicator) => message.includes(indicator))) {
|
|
1041
|
-
return false;
|
|
1042
|
-
}
|
|
1043
|
-
if (bufferedText.length < MIN_TRUNCATION_LENGTH) {
|
|
1044
|
-
return false;
|
|
1045
|
-
}
|
|
1046
|
-
return true;
|
|
1047
|
-
}
|
|
1048
|
-
var MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE = "Structured output was requested (responseFormat with a JSON schema) but the Claude Code CLI returned no structured_output, and the prose response could not be parsed as JSON. This usually means the schema contains constructs the CLI cannot enforce (e.g. complex regex patterns with lookaheads/backreferences), causing it to silently fall back to prose. Simplify the generation schema and validate strictly client-side. See the 'Structured Outputs' section of the ai-sdk-provider-claude-code README for the list of known limitations.";
|
|
1049
|
-
function extractJsonObjectText(text) {
|
|
1050
|
-
const trimmed = text.trim();
|
|
1051
|
-
if (!trimmed) {
|
|
1052
|
-
return void 0;
|
|
1053
|
-
}
|
|
1054
|
-
const candidates = [trimmed];
|
|
1055
|
-
const fencedBlocks = Array.from(trimmed.matchAll(/```(?:json)?\s*\n?([\s\S]*?)```/gi)).map((match) => match[1]?.trim()).filter((block) => block !== void 0 && block.length > 0);
|
|
1056
|
-
candidates.push(...fencedBlocks.reverse());
|
|
1057
|
-
for (const candidate of candidates) {
|
|
1058
|
-
if (!candidate) continue;
|
|
1059
|
-
try {
|
|
1060
|
-
const parsed = JSON.parse(candidate);
|
|
1061
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
1062
|
-
return candidate;
|
|
1063
|
-
}
|
|
1064
|
-
} catch {
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
return void 0;
|
|
1068
|
-
}
|
|
1069
|
-
function getStructuredErrorKind(error) {
|
|
1070
|
-
if (typeof error === "object" && error !== null && "errorKind" in error) {
|
|
1071
|
-
const kind = error.errorKind;
|
|
1072
|
-
if (typeof kind === "string") return kind;
|
|
1073
|
-
}
|
|
1074
|
-
return void 0;
|
|
1075
|
-
}
|
|
1076
|
-
function isAbortError(err) {
|
|
1077
|
-
if (err && typeof err === "object") {
|
|
1078
|
-
const e = err;
|
|
1079
|
-
if (typeof e.name === "string" && e.name === "AbortError") return true;
|
|
1080
|
-
if (typeof e.code === "string" && e.code.toUpperCase() === "ABORT_ERR") return true;
|
|
1081
|
-
}
|
|
1082
|
-
return false;
|
|
1083
|
-
}
|
|
1084
|
-
var DEFAULT_INHERITED_ENV_VARS = process.platform === "win32" ? [
|
|
1085
|
-
"APPDATA",
|
|
1086
|
-
"COMSPEC",
|
|
1087
|
-
"HOMEDRIVE",
|
|
1088
|
-
"HOMEPATH",
|
|
1089
|
-
"LOCALAPPDATA",
|
|
1090
|
-
"PATH",
|
|
1091
|
-
"PATHEXT",
|
|
1092
|
-
"SYSTEMDRIVE",
|
|
1093
|
-
"SYSTEMROOT",
|
|
1094
|
-
"TEMP",
|
|
1095
|
-
"TMP",
|
|
1096
|
-
"USERNAME",
|
|
1097
|
-
"USERPROFILE",
|
|
1098
|
-
"WINDIR"
|
|
1099
|
-
] : ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER", "LANG", "LC_ALL", "TMPDIR"];
|
|
1100
|
-
var CLAUDE_ENV_VARS = ["CLAUDE_CONFIG_DIR"];
|
|
1101
|
-
var NETWORK_ENV_VARS = [
|
|
1102
|
-
"HTTP_PROXY",
|
|
1103
|
-
"HTTPS_PROXY",
|
|
1104
|
-
"NO_PROXY",
|
|
1105
|
-
"http_proxy",
|
|
1106
|
-
"https_proxy",
|
|
1107
|
-
"no_proxy",
|
|
1108
|
-
"NODE_EXTRA_CA_CERTS",
|
|
1109
|
-
"SSL_CERT_FILE",
|
|
1110
|
-
"SSL_CERT_DIR"
|
|
1111
|
-
];
|
|
1112
|
-
var CLOUD_ENV_VARS = ["GCLOUD_PROJECT", "CLOUD_ML_REGION"];
|
|
1113
|
-
var INHERITED_ENV_PREFIXES = ["ANTHROPIC_", "CLAUDE_", "AWS_", "GOOGLE_"];
|
|
1114
|
-
function getBaseProcessEnv() {
|
|
1115
|
-
const env = {};
|
|
1116
|
-
const allowedKeys = /* @__PURE__ */ new Set([
|
|
1117
|
-
...DEFAULT_INHERITED_ENV_VARS,
|
|
1118
|
-
...CLAUDE_ENV_VARS,
|
|
1119
|
-
...NETWORK_ENV_VARS,
|
|
1120
|
-
...CLOUD_ENV_VARS
|
|
1121
|
-
]);
|
|
1122
|
-
const addIfSafe = (key) => {
|
|
1123
|
-
const value = process.env[key];
|
|
1124
|
-
if (typeof value !== "string") {
|
|
1125
|
-
return;
|
|
1126
|
-
}
|
|
1127
|
-
if (value.startsWith("()")) {
|
|
1128
|
-
return;
|
|
1129
|
-
}
|
|
1130
|
-
env[key] = value;
|
|
1131
|
-
};
|
|
1132
|
-
for (const key of allowedKeys) {
|
|
1133
|
-
addIfSafe(key);
|
|
1134
|
-
}
|
|
1135
|
-
for (const key of Object.keys(process.env)) {
|
|
1136
|
-
if (INHERITED_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
1137
|
-
addIfSafe(key);
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
return env;
|
|
1141
|
-
}
|
|
1142
|
-
var STREAMING_FEATURE_WARNING = "Claude Agent SDK features (hooks/MCP/images) require streaming input. Set `streamingInput: 'always'` or provide `canUseTool` (auto streams only when canUseTool is set).";
|
|
1143
|
-
var SDK_OPTIONS_BLOCKLIST = /* @__PURE__ */ new Set(["model", "abortController", "prompt", "outputFormat"]);
|
|
1144
|
-
var SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
|
|
1145
|
-
function isSubagentToolName(name) {
|
|
1146
|
-
return SUBAGENT_TOOL_NAMES.has(name);
|
|
1147
|
-
}
|
|
1148
|
-
function resolveToolParentId(messageLevel, blockLevel, inferFallback) {
|
|
1149
|
-
if (messageLevel !== void 0) return messageLevel;
|
|
1150
|
-
if (typeof blockLevel === "string") return blockLevel;
|
|
1151
|
-
return inferFallback();
|
|
1152
|
-
}
|
|
1153
|
-
function computeRetractedToolCallIds(retracted, descriptors) {
|
|
1154
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1155
|
-
for (const { toolCallId, uuid } of descriptors) {
|
|
1156
|
-
if (uuid !== void 0 && retracted.has(uuid)) {
|
|
1157
|
-
ids.add(toolCallId);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
return ids;
|
|
1161
|
-
}
|
|
1162
|
-
function applySupersede(message, evict, logger, guard = "array") {
|
|
1163
|
-
const supersedes = message.supersedes;
|
|
1164
|
-
const triggered = guard === "truthy" ? Boolean(supersedes && supersedes.length > 0) : Array.isArray(supersedes) && supersedes.length > 0;
|
|
1165
|
-
if (!triggered) {
|
|
1166
|
-
return false;
|
|
1167
|
-
}
|
|
1168
|
-
logger.debug(`[claude-code] Assistant message supersedes ${supersedes.length} prior message(s)`);
|
|
1169
|
-
evict(new Set(supersedes));
|
|
1170
|
-
return true;
|
|
1171
|
-
}
|
|
1172
|
-
function buildRetractionEvictor(evict) {
|
|
1173
|
-
return (uuids) => evict(new Set(uuids));
|
|
1174
|
-
}
|
|
1175
|
-
var INFORMATIONAL_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set([
|
|
1176
|
-
"notification",
|
|
1177
|
-
"status",
|
|
1178
|
-
"task_updated",
|
|
1179
|
-
"session_state_changed",
|
|
1180
|
-
"commands_changed",
|
|
1181
|
-
"memory_recall",
|
|
1182
|
-
"plugin_install"
|
|
1183
|
-
]);
|
|
1184
|
-
function isContentBlock(item) {
|
|
1185
|
-
return typeof item === "object" && item !== null && "type" in item;
|
|
1186
|
-
}
|
|
1187
|
-
function filterContentBlocks(content, type) {
|
|
1188
|
-
if (!Array.isArray(content)) return [];
|
|
1189
|
-
const blocks = content.filter(
|
|
1190
|
-
(item) => isContentBlock(item) && item.type === type
|
|
1191
|
-
);
|
|
1192
|
-
const mismatch = blocks.find((b) => b.type !== type);
|
|
1193
|
-
if (mismatch) {
|
|
1194
|
-
throw new Error(
|
|
1195
|
-
`filterContentBlocks: block type '${mismatch.type}' passed filter for '${type}'`
|
|
1196
|
-
);
|
|
1197
|
-
}
|
|
1198
|
-
return blocks;
|
|
1199
|
-
}
|
|
1200
|
-
function createEmptyUsage() {
|
|
1201
|
-
return {
|
|
1202
|
-
inputTokens: {
|
|
1203
|
-
total: 0,
|
|
1204
|
-
noCache: 0,
|
|
1205
|
-
cacheRead: 0,
|
|
1206
|
-
cacheWrite: 0
|
|
1207
|
-
},
|
|
1208
|
-
outputTokens: {
|
|
1209
|
-
total: 0,
|
|
1210
|
-
text: void 0,
|
|
1211
|
-
reasoning: void 0
|
|
1212
|
-
},
|
|
1213
|
-
raw: void 0
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
function convertClaudeCodeUsage(usage) {
|
|
1217
|
-
const inputTokens = usage.input_tokens ?? 0;
|
|
1218
|
-
const outputTokens = usage.output_tokens ?? 0;
|
|
1219
|
-
const cacheWrite = usage.cache_creation_input_tokens ?? 0;
|
|
1220
|
-
const cacheRead = usage.cache_read_input_tokens ?? 0;
|
|
1221
|
-
return {
|
|
1222
|
-
inputTokens: {
|
|
1223
|
-
total: inputTokens + cacheWrite + cacheRead,
|
|
1224
|
-
noCache: inputTokens,
|
|
1225
|
-
cacheRead,
|
|
1226
|
-
cacheWrite
|
|
1227
|
-
},
|
|
1228
|
-
outputTokens: {
|
|
1229
|
-
total: outputTokens,
|
|
1230
|
-
text: void 0,
|
|
1231
|
-
reasoning: void 0
|
|
1232
|
-
},
|
|
1233
|
-
raw: usage
|
|
1234
|
-
};
|
|
1235
|
-
}
|
|
1236
|
-
function createMessageInjector() {
|
|
1237
|
-
const queue = [];
|
|
1238
|
-
let closed = false;
|
|
1239
|
-
let resolver = null;
|
|
1240
|
-
const injector = {
|
|
1241
|
-
inject(content, onResult) {
|
|
1242
|
-
if (closed) {
|
|
1243
|
-
onResult?.(false);
|
|
1244
|
-
return;
|
|
1245
|
-
}
|
|
1246
|
-
const item = { content, onResult };
|
|
1247
|
-
if (resolver) {
|
|
1248
|
-
const r = resolver;
|
|
1249
|
-
resolver = null;
|
|
1250
|
-
r(item);
|
|
1251
|
-
} else {
|
|
1252
|
-
queue.push(item);
|
|
1253
|
-
}
|
|
1254
|
-
},
|
|
1255
|
-
close() {
|
|
1256
|
-
closed = true;
|
|
1257
|
-
if (resolver && queue.length === 0) {
|
|
1258
|
-
resolver(null);
|
|
1259
|
-
resolver = null;
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
};
|
|
1263
|
-
const getNextItem = () => {
|
|
1264
|
-
if (queue.length > 0) {
|
|
1265
|
-
const item = queue.shift();
|
|
1266
|
-
if (!item) {
|
|
1267
|
-
return Promise.resolve(null);
|
|
1268
|
-
}
|
|
1269
|
-
return Promise.resolve(item);
|
|
1270
|
-
}
|
|
1271
|
-
if (closed) {
|
|
1272
|
-
return Promise.resolve(null);
|
|
1273
|
-
}
|
|
1274
|
-
return new Promise((resolve) => {
|
|
1275
|
-
resolver = (item) => {
|
|
1276
|
-
resolve(item);
|
|
1277
|
-
};
|
|
1278
|
-
});
|
|
1279
|
-
};
|
|
1280
|
-
const notifySessionEnded = () => {
|
|
1281
|
-
for (const item of queue) {
|
|
1282
|
-
item.onResult?.(false);
|
|
1283
|
-
}
|
|
1284
|
-
queue.length = 0;
|
|
1285
|
-
closed = true;
|
|
1286
|
-
if (resolver) {
|
|
1287
|
-
resolver(null);
|
|
1288
|
-
resolver = null;
|
|
1289
|
-
}
|
|
1290
|
-
};
|
|
1291
|
-
return { injector, getNextItem, notifySessionEnded };
|
|
1292
|
-
}
|
|
1293
|
-
function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, contentParts, onStreamStart) {
|
|
1294
|
-
const content = contentParts && contentParts.length > 0 ? contentParts : [{ type: "text", text: messagesPrompt }];
|
|
1295
|
-
const initialMsg = {
|
|
1296
|
-
type: "user",
|
|
1297
|
-
message: {
|
|
1298
|
-
role: "user",
|
|
1299
|
-
content
|
|
1300
|
-
},
|
|
1301
|
-
parent_tool_use_id: null,
|
|
1302
|
-
session_id: sessionId ?? ""
|
|
1303
|
-
};
|
|
1304
|
-
if (!onStreamStart) {
|
|
1305
|
-
return {
|
|
1306
|
-
async *[Symbol.asyncIterator]() {
|
|
1307
|
-
yield initialMsg;
|
|
1308
|
-
await outputStreamEnded;
|
|
1309
|
-
}
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
const { injector, getNextItem, notifySessionEnded } = createMessageInjector();
|
|
1313
|
-
return {
|
|
1314
|
-
async *[Symbol.asyncIterator]() {
|
|
1315
|
-
yield initialMsg;
|
|
1316
|
-
onStreamStart(injector);
|
|
1317
|
-
let streamEnded = false;
|
|
1318
|
-
void outputStreamEnded.then(() => {
|
|
1319
|
-
streamEnded = true;
|
|
1320
|
-
notifySessionEnded();
|
|
1321
|
-
});
|
|
1322
|
-
while (!streamEnded) {
|
|
1323
|
-
const item = await Promise.race([getNextItem(), outputStreamEnded.then(() => null)]);
|
|
1324
|
-
if (item === null) {
|
|
1325
|
-
await outputStreamEnded;
|
|
1326
|
-
break;
|
|
1327
|
-
}
|
|
1328
|
-
const sdkMsg = {
|
|
1329
|
-
type: "user",
|
|
1330
|
-
message: {
|
|
1331
|
-
role: "user",
|
|
1332
|
-
content: [{ type: "text", text: item.content }]
|
|
1333
|
-
},
|
|
1334
|
-
parent_tool_use_id: null,
|
|
1335
|
-
session_id: sessionId ?? ""
|
|
1336
|
-
};
|
|
1337
|
-
yield sdkMsg;
|
|
1338
|
-
item.onResult?.(true);
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
};
|
|
1342
|
-
}
|
|
1343
|
-
var modelMap = {
|
|
1344
|
-
opus: "opus",
|
|
1345
|
-
sonnet: "sonnet",
|
|
1346
|
-
haiku: "haiku"
|
|
1347
|
-
};
|
|
1348
|
-
var MAX_TOOL_RESULT_SIZE = 1e4;
|
|
1349
|
-
function truncateToolResultForStream(result, maxSize = MAX_TOOL_RESULT_SIZE) {
|
|
1350
|
-
if (typeof result === "string") {
|
|
1351
|
-
if (result.length <= maxSize) return result;
|
|
1352
|
-
return result.slice(0, maxSize) + `
|
|
1353
|
-
...[truncated ${result.length - maxSize} chars]`;
|
|
1354
|
-
}
|
|
1355
|
-
if (typeof result !== "object" || result === null) return result;
|
|
1356
|
-
if (Array.isArray(result)) {
|
|
1357
|
-
let largestIndex = -1;
|
|
1358
|
-
let largestSize2 = 0;
|
|
1359
|
-
for (let i = 0; i < result.length; i++) {
|
|
1360
|
-
const value = result[i];
|
|
1361
|
-
if (typeof value === "string" && value.length > largestSize2) {
|
|
1362
|
-
largestIndex = i;
|
|
1363
|
-
largestSize2 = value.length;
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
if (largestIndex >= 0 && largestSize2 > maxSize) {
|
|
1367
|
-
const truncatedValue = result[largestIndex].slice(0, maxSize) + `
|
|
1368
|
-
...[truncated ${largestSize2 - maxSize} chars]`;
|
|
1369
|
-
const cloned = [...result];
|
|
1370
|
-
cloned[largestIndex] = truncatedValue;
|
|
1371
|
-
return cloned;
|
|
1372
|
-
}
|
|
1373
|
-
return result;
|
|
1374
|
-
}
|
|
1375
|
-
const obj = result;
|
|
1376
|
-
let largestKey = null;
|
|
1377
|
-
let largestSize = 0;
|
|
1378
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
1379
|
-
if (typeof value === "string" && value.length > largestSize) {
|
|
1380
|
-
largestKey = key;
|
|
1381
|
-
largestSize = value.length;
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
if (largestKey && largestSize > maxSize) {
|
|
1385
|
-
const truncatedValue = obj[largestKey].slice(0, maxSize) + `
|
|
1386
|
-
...[truncated ${largestSize - maxSize} chars]`;
|
|
1387
|
-
return { ...obj, [largestKey]: truncatedValue };
|
|
1388
|
-
}
|
|
1389
|
-
return result;
|
|
1390
|
-
}
|
|
1391
|
-
var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
1392
|
-
specificationVersion = "v3";
|
|
1393
|
-
defaultObjectGenerationMode = "json";
|
|
1394
|
-
supportsImageUrls = false;
|
|
1395
|
-
supportedUrls = {};
|
|
1396
|
-
supportsStructuredOutputs = true;
|
|
1397
|
-
// Fallback/magic string constants
|
|
1398
|
-
static UNKNOWN_TOOL_NAME = "unknown-tool";
|
|
1399
|
-
// Tool input safety limits
|
|
1400
|
-
static MAX_TOOL_INPUT_SIZE = 1048576;
|
|
1401
|
-
// 1MB hard limit
|
|
1402
|
-
static MAX_TOOL_INPUT_WARN = 102400;
|
|
1403
|
-
// 100KB warning threshold
|
|
1404
|
-
static MAX_DELTA_CALC_SIZE = 1e4;
|
|
1405
|
-
// 10KB delta computation threshold
|
|
1406
|
-
// Upper bound for draining post-result messages (prompt_suggestion) so a
|
|
1407
|
-
// lingering CLI subprocess cannot be held open indefinitely after finish.
|
|
1408
|
-
static PROMPT_SUGGESTION_DRAIN_TIMEOUT_MS = 1e4;
|
|
1409
|
-
modelId;
|
|
1410
|
-
settings;
|
|
1411
|
-
sessionId;
|
|
1412
|
-
modelValidationWarning;
|
|
1413
|
-
settingsValidationWarnings;
|
|
1414
|
-
logger;
|
|
1415
|
-
constructor(options) {
|
|
1416
|
-
this.modelId = options.id;
|
|
1417
|
-
this.settings = options.settings ?? {};
|
|
1418
|
-
this.settingsValidationWarnings = options.settingsValidationWarnings ?? [];
|
|
1419
|
-
const baseLogger = getLogger(this.settings.logger);
|
|
1420
|
-
this.logger = createVerboseLogger(baseLogger, this.settings.verbose ?? false);
|
|
1421
|
-
if (!this.modelId || typeof this.modelId !== "string" || this.modelId.trim() === "") {
|
|
1422
|
-
throw new import_provider2.NoSuchModelError({
|
|
1423
|
-
modelId: this.modelId,
|
|
1424
|
-
modelType: "languageModel"
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
this.modelValidationWarning = validateModelId(this.modelId);
|
|
1428
|
-
if (this.modelValidationWarning) {
|
|
1429
|
-
this.logger.warn(`Claude Code Model: ${this.modelValidationWarning}`);
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
get provider() {
|
|
1433
|
-
return "claude-code";
|
|
1434
|
-
}
|
|
1435
|
-
getModel() {
|
|
1436
|
-
const mapped = modelMap[this.modelId];
|
|
1437
|
-
return mapped ?? this.modelId;
|
|
1438
|
-
}
|
|
1439
|
-
getSanitizedSdkOptions() {
|
|
1440
|
-
if (!this.settings.sdkOptions || typeof this.settings.sdkOptions !== "object") {
|
|
1441
|
-
return void 0;
|
|
1442
|
-
}
|
|
1443
|
-
const sanitized = { ...this.settings.sdkOptions };
|
|
1444
|
-
const blockedKeys = Array.from(SDK_OPTIONS_BLOCKLIST).filter((key) => key in sanitized);
|
|
1445
|
-
if (blockedKeys.length > 0) {
|
|
1446
|
-
this.logger.warn(
|
|
1447
|
-
`[claude-code] sdkOptions includes provider-managed fields (${blockedKeys.join(
|
|
1448
|
-
", "
|
|
1449
|
-
)}); these will be ignored.`
|
|
1450
|
-
);
|
|
1451
|
-
blockedKeys.forEach((key) => delete sanitized[key]);
|
|
1452
|
-
}
|
|
1453
|
-
return sanitized;
|
|
1454
|
-
}
|
|
1455
|
-
getEffectiveResume(sdkOptions) {
|
|
1456
|
-
for (const candidate of [sdkOptions?.resume, this.settings.resume, this.sessionId]) {
|
|
1457
|
-
if (typeof candidate === "string" && !isBlankResume(candidate)) {
|
|
1458
|
-
return candidate;
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
return void 0;
|
|
1462
|
-
}
|
|
1463
|
-
/**
|
|
1464
|
-
* Single source of truth for the CLI's `--session-id` exclusivity rule.
|
|
1465
|
-
*
|
|
1466
|
-
* The CLI rejects `--session-id` together with `--resume`/`--continue`
|
|
1467
|
-
* unless `--fork-session` is also set (forkSession then names the forked
|
|
1468
|
-
* session's own ID). This predicate captures "there IS a resume/continue
|
|
1469
|
-
* target AND we are not forking", i.e. the case where a session id must NOT
|
|
1470
|
-
* coexist. It is referenced by both:
|
|
1471
|
-
* - the pre-merge forwarding guard (via its inverse), which decides whether
|
|
1472
|
-
* to forward `settings.sessionId` onto the base options, and
|
|
1473
|
-
* - the post-merge exclusivity drop, which removes any session id that the
|
|
1474
|
-
* generic sdkOptions overlay (or the auto-resume turn) re-introduced.
|
|
1475
|
-
*
|
|
1476
|
-
* Keeping one definition guarantees both sites agree on what "conflicts with
|
|
1477
|
-
* a session id" means. The mirror in validation.ts (construction-time)
|
|
1478
|
-
* intentionally stays separate: it reads settings+sdkOptions, not a built
|
|
1479
|
-
* opts object.
|
|
1480
|
-
*/
|
|
1481
|
-
static sessionIdConflictsWithResumeOrContinue(args) {
|
|
1482
|
-
return (args.resumePresent || args.continue) && !args.forkSession;
|
|
1483
|
-
}
|
|
1484
|
-
/**
|
|
1485
|
-
* Owns ALL session-id / resume cross-option resolution on the FINAL merged
|
|
1486
|
-
* options, in the single correct order. Called once, immediately after the
|
|
1487
|
-
* generic sdkOptions overlay in createQueryOptions.
|
|
1488
|
-
*
|
|
1489
|
-
* Two concerns, in this exact order (order matters: step 1 can change whether
|
|
1490
|
-
* step 2 sees a resume target):
|
|
1491
|
-
*
|
|
1492
|
-
* 1. Blank-resume restoration. The overlay copies the raw `sdkOptions.resume`
|
|
1493
|
-
* verbatim, which can re-introduce a blank/whitespace value over the
|
|
1494
|
-
* base `resume` that getEffectiveResume already normalized. The SDK treats
|
|
1495
|
-
* a blank resume as absent, so a blank must NOT clobber the computed
|
|
1496
|
-
* fallback — restore `effectiveResume` (already blank-stripped; may itself
|
|
1497
|
-
* be undefined for a genuinely new session) rather than leaving '' or
|
|
1498
|
-
* forcing undefined, which would erase a real settings.resume / captured
|
|
1499
|
-
* session id.
|
|
1500
|
-
*
|
|
1501
|
-
* 2. Session-id exclusivity. Drop `opts.sessionId` whenever it conflicts with
|
|
1502
|
-
* a resume/continue target (see sessionIdConflictsWithResumeOrContinue).
|
|
1503
|
-
* This runs on the merged opts so it catches a sessionId re-added by the
|
|
1504
|
-
* sdkOptions overlay AND the auto-resumed second turn (where resume was
|
|
1505
|
-
* populated from the captured session id). It complements — does not
|
|
1506
|
-
* replace — the pre-merge forwarding guard, which governs whether
|
|
1507
|
-
* settings.sessionId was forwarded BEFORE the overlay could mutate
|
|
1508
|
-
* forkSession/continue/resume.
|
|
1509
|
-
*/
|
|
1510
|
-
applySessionResolution(opts, effectiveResume) {
|
|
1511
|
-
if (isBlankResume(opts.resume)) {
|
|
1512
|
-
opts.resume = effectiveResume;
|
|
1513
|
-
}
|
|
1514
|
-
if (opts.sessionId !== void 0 && _ClaudeCodeLanguageModel.sessionIdConflictsWithResumeOrContinue({
|
|
1515
|
-
resumePresent: opts.resume !== void 0,
|
|
1516
|
-
continue: opts.continue === true,
|
|
1517
|
-
forkSession: opts.forkSession === true
|
|
1518
|
-
})) {
|
|
1519
|
-
opts.sessionId = void 0;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
extractToolUses(content) {
|
|
1523
|
-
return filterContentBlocks(content, "tool_use").map((block) => {
|
|
1524
|
-
const { id, name, input, parent_tool_use_id } = block;
|
|
1525
|
-
return {
|
|
1526
|
-
id: typeof id === "string" && id.length > 0 ? id : (0, import_provider_utils.generateId)(),
|
|
1527
|
-
name: typeof name === "string" && name.length > 0 ? name : _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME,
|
|
1528
|
-
input,
|
|
1529
|
-
parentToolUseId: typeof parent_tool_use_id === "string" ? parent_tool_use_id : null
|
|
1530
|
-
};
|
|
1531
|
-
});
|
|
1532
|
-
}
|
|
1533
|
-
extractToolResults(content) {
|
|
1534
|
-
return filterContentBlocks(content, "tool_result").map((block) => {
|
|
1535
|
-
const { tool_use_id, content: content2, is_error, name } = block;
|
|
1536
|
-
return {
|
|
1537
|
-
id: typeof tool_use_id === "string" && tool_use_id.length > 0 ? tool_use_id : (0, import_provider_utils.generateId)(),
|
|
1538
|
-
name: typeof name === "string" && name.length > 0 ? name : void 0,
|
|
1539
|
-
result: content2,
|
|
1540
|
-
isError: Boolean(is_error)
|
|
1541
|
-
};
|
|
1542
|
-
});
|
|
1543
|
-
}
|
|
1544
|
-
extractToolErrors(content) {
|
|
1545
|
-
return filterContentBlocks(content, "tool_error").map((block) => {
|
|
1546
|
-
const { tool_use_id, error, name } = block;
|
|
1547
|
-
return {
|
|
1548
|
-
id: typeof tool_use_id === "string" && tool_use_id.length > 0 ? tool_use_id : (0, import_provider_utils.generateId)(),
|
|
1549
|
-
name: typeof name === "string" && name.length > 0 ? name : void 0,
|
|
1550
|
-
error
|
|
1551
|
-
};
|
|
1552
|
-
});
|
|
1553
|
-
}
|
|
1554
|
-
serializeToolInput(input) {
|
|
1555
|
-
if (typeof input === "string") {
|
|
1556
|
-
return this.checkInputSize(input);
|
|
1557
|
-
}
|
|
1558
|
-
if (input === void 0) {
|
|
1559
|
-
return "";
|
|
1560
|
-
}
|
|
1561
|
-
try {
|
|
1562
|
-
const serialized = JSON.stringify(input);
|
|
1563
|
-
return this.checkInputSize(serialized);
|
|
1564
|
-
} catch {
|
|
1565
|
-
const fallback = String(input);
|
|
1566
|
-
return this.checkInputSize(fallback);
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
checkInputSize(str) {
|
|
1570
|
-
const length = str.length;
|
|
1571
|
-
if (length > _ClaudeCodeLanguageModel.MAX_TOOL_INPUT_SIZE) {
|
|
1572
|
-
throw new Error(
|
|
1573
|
-
`Tool input exceeds maximum size of ${_ClaudeCodeLanguageModel.MAX_TOOL_INPUT_SIZE} bytes (got ${length} bytes). This may indicate a malformed request or an attempt to process excessively large data.`
|
|
1574
|
-
);
|
|
1575
|
-
}
|
|
1576
|
-
if (length > _ClaudeCodeLanguageModel.MAX_TOOL_INPUT_WARN) {
|
|
1577
|
-
this.logger.warn(
|
|
1578
|
-
`[claude-code] Large tool input detected: ${length} bytes. Performance may be impacted. Consider chunking or reducing input size.`
|
|
1579
|
-
);
|
|
1580
|
-
}
|
|
1581
|
-
return str;
|
|
1582
|
-
}
|
|
1583
|
-
normalizeToolResult(result) {
|
|
1584
|
-
if (typeof result === "string") {
|
|
1585
|
-
try {
|
|
1586
|
-
return JSON.parse(result);
|
|
1587
|
-
} catch {
|
|
1588
|
-
return result;
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
if (Array.isArray(result) && result.length > 0) {
|
|
1592
|
-
const textBlocks = result.filter(
|
|
1593
|
-
(block) => block?.type === "text" && typeof block.text === "string"
|
|
1594
|
-
).map((block) => block.text);
|
|
1595
|
-
if (textBlocks.length !== result.length) {
|
|
1596
|
-
return result;
|
|
1597
|
-
}
|
|
1598
|
-
if (textBlocks.length === 1) {
|
|
1599
|
-
try {
|
|
1600
|
-
return JSON.parse(textBlocks[0]);
|
|
1601
|
-
} catch {
|
|
1602
|
-
return textBlocks[0];
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
const combined = textBlocks.join("\n");
|
|
1606
|
-
try {
|
|
1607
|
-
return JSON.parse(combined);
|
|
1608
|
-
} catch {
|
|
1609
|
-
return combined;
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
return result;
|
|
1613
|
-
}
|
|
1614
|
-
/**
|
|
1615
|
-
* Builds a provider-executed `tool-call` part from an assistant `tool_use`
|
|
1616
|
-
* block. Shared by doGenerate (content part) and doStream (stream part) so
|
|
1617
|
-
* the two paths cannot drift in field shape.
|
|
1618
|
-
*/
|
|
1619
|
-
buildToolCallPart(toolCallId, toolName, input, parentToolCallId) {
|
|
1620
|
-
return {
|
|
1621
|
-
type: "tool-call",
|
|
1622
|
-
toolCallId,
|
|
1623
|
-
toolName,
|
|
1624
|
-
input,
|
|
1625
|
-
providerExecuted: true,
|
|
1626
|
-
dynamic: true,
|
|
1627
|
-
// V3 field: indicates tool is provider-defined (not in user's tools map)
|
|
1628
|
-
providerMetadata: {
|
|
1629
|
-
"claude-code": {
|
|
1630
|
-
// rawInput preserves the original serialized format before AI SDK normalization.
|
|
1631
|
-
// Use this if you need the exact string sent to the Claude CLI, which may differ
|
|
1632
|
-
// from the `input` field after AI SDK processing.
|
|
1633
|
-
rawInput: input,
|
|
1634
|
-
parentToolCallId: parentToolCallId ?? null
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
};
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Builds a provider-executed `tool-result` part from a user-message
|
|
1641
|
-
* `tool_result` block, applying normalization and `maxToolResultSize`
|
|
1642
|
-
* truncation. Shared by doGenerate and doStream.
|
|
1643
|
-
*/
|
|
1644
|
-
buildToolResultPart(toolCallId, toolName, result, isError, parentToolCallId) {
|
|
1645
|
-
const normalizedResult = this.normalizeToolResult(result);
|
|
1646
|
-
const rawResult = typeof result === "string" ? result : result === void 0 ? (
|
|
1647
|
-
// tool_result blocks may omit `content` entirely; '' keeps both
|
|
1648
|
-
// `result` (NonNullable<JSONValue>) and rawResult (JSONValue) valid.
|
|
1649
|
-
""
|
|
1650
|
-
) : (() => {
|
|
1651
|
-
try {
|
|
1652
|
-
return JSON.stringify(result) ?? String(result);
|
|
1653
|
-
} catch {
|
|
1654
|
-
return String(result);
|
|
1655
|
-
}
|
|
1656
|
-
})();
|
|
1657
|
-
const maxToolResultSize = this.settings.maxToolResultSize;
|
|
1658
|
-
const truncatedResult = truncateToolResultForStream(normalizedResult, maxToolResultSize);
|
|
1659
|
-
const truncatedRawResult = truncateToolResultForStream(rawResult, maxToolResultSize);
|
|
1660
|
-
const rawResultTruncated = truncatedRawResult !== rawResult;
|
|
1661
|
-
return {
|
|
1662
|
-
type: "tool-result",
|
|
1663
|
-
toolCallId,
|
|
1664
|
-
toolName,
|
|
1665
|
-
// `?? ''`: absent `content` (undefined) and string results that
|
|
1666
|
-
// normalize to JSON null (e.g. the string "null") must not violate the
|
|
1667
|
-
// NonNullable<JSONValue> contract of LanguageModelV3ToolResult.result.
|
|
1668
|
-
result: truncatedResult ?? "",
|
|
1669
|
-
isError,
|
|
1670
|
-
providerExecuted: true,
|
|
1671
|
-
dynamic: true,
|
|
1672
|
-
// V3 field: indicates tool is provider-defined
|
|
1673
|
-
providerMetadata: {
|
|
1674
|
-
"claude-code": {
|
|
1675
|
-
// rawResult preserves the original CLI output string before JSON parsing.
|
|
1676
|
-
// Use this when you need the exact string returned by the tool, especially
|
|
1677
|
-
// if the `result` field has been parsed/normalized and you need the original format.
|
|
1678
|
-
rawResult: truncatedRawResult,
|
|
1679
|
-
rawResultTruncated,
|
|
1680
|
-
parentToolCallId: parentToolCallId ?? null
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
};
|
|
1684
|
-
}
|
|
1685
|
-
serializeToolError(error) {
|
|
1686
|
-
return typeof error === "string" ? error : typeof error === "object" && error !== null ? (() => {
|
|
1687
|
-
try {
|
|
1688
|
-
return JSON.stringify(error) ?? String(error);
|
|
1689
|
-
} catch {
|
|
1690
|
-
return String(error);
|
|
1691
|
-
}
|
|
1692
|
-
})() : String(error);
|
|
1693
|
-
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Builds a provider-executed `tool-error` STREAM part from a user-message
|
|
1696
|
-
* `tool_error` block (doStream only; AI SDK core handles tool-error stream
|
|
1697
|
-
* parts natively).
|
|
1698
|
-
*/
|
|
1699
|
-
buildToolErrorPart(toolCallId, toolName, error, parentToolCallId) {
|
|
1700
|
-
const rawError = this.serializeToolError(error);
|
|
1701
|
-
return {
|
|
1702
|
-
type: "tool-error",
|
|
1703
|
-
toolCallId,
|
|
1704
|
-
toolName,
|
|
1705
|
-
error: rawError,
|
|
1706
|
-
providerExecuted: true,
|
|
1707
|
-
dynamic: true,
|
|
1708
|
-
// V3 field: indicates tool is provider-defined
|
|
1709
|
-
providerMetadata: {
|
|
1710
|
-
"claude-code": {
|
|
1711
|
-
rawError,
|
|
1712
|
-
parentToolCallId: parentToolCallId ?? null
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
/**
|
|
1718
|
-
* Builds a V3 `tool-result` CONTENT part with `isError: true` from a
|
|
1719
|
-
* user-message `tool_error` block (doGenerate only). The V3 content union
|
|
1720
|
-
* has no `tool-error` member and AI SDK core's asContent() silently drops
|
|
1721
|
-
* unknown content part types, so an extension tool-error part would never
|
|
1722
|
-
* reach `generateText` users — an isError tool-result, by contrast,
|
|
1723
|
-
* round-trips into a proper tool-error part in steps content.
|
|
1724
|
-
*/
|
|
1725
|
-
buildToolErrorResultPart(toolCallId, toolName, error, parentToolCallId) {
|
|
1726
|
-
const rawError = this.serializeToolError(error);
|
|
1727
|
-
return {
|
|
1728
|
-
type: "tool-result",
|
|
1729
|
-
toolCallId,
|
|
1730
|
-
toolName,
|
|
1731
|
-
result: rawError,
|
|
1732
|
-
isError: true,
|
|
1733
|
-
providerExecuted: true,
|
|
1734
|
-
dynamic: true,
|
|
1735
|
-
// V3 field: indicates tool is provider-defined
|
|
1736
|
-
providerMetadata: {
|
|
1737
|
-
"claude-code": {
|
|
1738
|
-
rawError,
|
|
1739
|
-
parentToolCallId: parentToolCallId ?? null
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
};
|
|
1743
|
-
}
|
|
1744
|
-
/**
|
|
1745
|
-
* Policy (P3): late-frame drop guard, shared by all four tool_result/tool_error
|
|
1746
|
-
* sites (doGenerate result+error, doStream result+error). When a frame's tool
|
|
1747
|
-
* id is tombstoned (its tool-call was retracted by a supersede/refusal-fallback
|
|
1748
|
-
* signal), the frame must be DROPPED rather than re-synthesized into an orphan
|
|
1749
|
-
* tool-call. Centralizing the predicate + debug message keeps the four sites in
|
|
1750
|
-
* lockstep so a future tombstone change can't be applied to only some of them.
|
|
1751
|
-
*/
|
|
1752
|
-
isRetractedToolFrame(id, tombstone, frameKind) {
|
|
1753
|
-
if (!tombstone.has(id)) {
|
|
1754
|
-
return false;
|
|
1755
|
-
}
|
|
1756
|
-
this.logger.debug(
|
|
1757
|
-
`[claude-code] Dropping tool ${frameKind} for retracted (superseded) tool ID: ${id}`
|
|
1758
|
-
);
|
|
1759
|
-
return true;
|
|
1760
|
-
}
|
|
1761
|
-
generateAllWarnings(options, prompt) {
|
|
1762
|
-
const warnings = [];
|
|
1763
|
-
const unsupportedParams = [];
|
|
1764
|
-
if (options.temperature !== void 0) unsupportedParams.push("temperature");
|
|
1765
|
-
if (options.topP !== void 0) unsupportedParams.push("topP");
|
|
1766
|
-
if (options.topK !== void 0) unsupportedParams.push("topK");
|
|
1767
|
-
if (options.presencePenalty !== void 0) unsupportedParams.push("presencePenalty");
|
|
1768
|
-
if (options.frequencyPenalty !== void 0) unsupportedParams.push("frequencyPenalty");
|
|
1769
|
-
if (options.stopSequences !== void 0 && options.stopSequences.length > 0)
|
|
1770
|
-
unsupportedParams.push("stopSequences");
|
|
1771
|
-
if (options.seed !== void 0) unsupportedParams.push("seed");
|
|
1772
|
-
if (unsupportedParams.length > 0) {
|
|
1773
|
-
for (const param of unsupportedParams) {
|
|
1774
|
-
warnings.push({
|
|
1775
|
-
type: "unsupported",
|
|
1776
|
-
feature: param,
|
|
1777
|
-
details: `Claude Code SDK does not support the ${param} parameter. It will be ignored.`
|
|
1778
|
-
});
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
if (options.tools !== void 0 && options.tools.length > 0) {
|
|
1782
|
-
warnings.push({
|
|
1783
|
-
type: "unsupported",
|
|
1784
|
-
feature: "tools",
|
|
1785
|
-
details: "The Claude Code CLI executes its own tools; AI SDK tools cannot be auto-bridged at the provider layer and will be ignored. To expose custom tools to the CLI, build an in-process MCP server with the createAiSdkMcpServer helper (exported by this package) and pass it via the mcpServers setting (plus allowedTools)."
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1788
|
-
if (options.toolChoice !== void 0 && options.toolChoice.type !== "auto") {
|
|
1789
|
-
warnings.push({
|
|
1790
|
-
type: "unsupported",
|
|
1791
|
-
feature: "toolChoice",
|
|
1792
|
-
details: `Claude Code CLI does not support toolChoice '${options.toolChoice.type}'. Only automatic tool selection is available; the toolChoice parameter will be ignored.`
|
|
1793
|
-
});
|
|
1794
|
-
}
|
|
1795
|
-
if (options.maxOutputTokens !== void 0) {
|
|
1796
|
-
warnings.push({
|
|
1797
|
-
type: "unsupported",
|
|
1798
|
-
feature: "maxOutputTokens",
|
|
1799
|
-
details: "Claude Code CLI does not accept an output token cap. The maxOutputTokens parameter will be ignored."
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
|
-
if (this.modelValidationWarning) {
|
|
1803
|
-
warnings.push({
|
|
1804
|
-
type: "other",
|
|
1805
|
-
message: this.modelValidationWarning
|
|
1806
|
-
});
|
|
1807
|
-
}
|
|
1808
|
-
this.settingsValidationWarnings.forEach((warning) => {
|
|
1809
|
-
warnings.push({
|
|
1810
|
-
type: "other",
|
|
1811
|
-
message: warning
|
|
1812
|
-
});
|
|
1813
|
-
});
|
|
1814
|
-
if (options.responseFormat?.type === "json" && !options.responseFormat.schema) {
|
|
1815
|
-
warnings.push({
|
|
1816
|
-
type: "unsupported",
|
|
1817
|
-
feature: "responseFormat",
|
|
1818
|
-
details: "JSON response format requires a schema for the Claude Code provider. The JSON responseFormat is ignored and the call is treated as plain text."
|
|
1819
|
-
});
|
|
1820
|
-
}
|
|
1821
|
-
const promptWarning = validatePrompt(prompt);
|
|
1822
|
-
if (promptWarning) {
|
|
1823
|
-
warnings.push({
|
|
1824
|
-
type: "other",
|
|
1825
|
-
message: promptWarning
|
|
1826
|
-
});
|
|
1827
|
-
}
|
|
1828
|
-
return warnings;
|
|
1829
|
-
}
|
|
1830
|
-
createQueryOptions(abortController, responseFormat, stderrCollector, sdkOptions, effectiveResume) {
|
|
1831
|
-
const opts = {
|
|
1832
|
-
model: this.getModel(),
|
|
1833
|
-
abortController,
|
|
1834
|
-
resume: effectiveResume,
|
|
1835
|
-
pathToClaudeCodeExecutable: this.settings.pathToClaudeCodeExecutable,
|
|
1836
|
-
maxTurns: this.settings.maxTurns,
|
|
1837
|
-
maxThinkingTokens: this.settings.maxThinkingTokens,
|
|
1838
|
-
thinking: this.settings.thinking,
|
|
1839
|
-
effort: this.settings.effort,
|
|
1840
|
-
promptSuggestions: this.settings.promptSuggestions,
|
|
1841
|
-
cwd: this.settings.cwd,
|
|
1842
|
-
executable: this.settings.executable,
|
|
1843
|
-
executableArgs: this.settings.executableArgs,
|
|
1844
|
-
permissionMode: this.settings.permissionMode,
|
|
1845
|
-
permissionPromptToolName: this.settings.permissionPromptToolName,
|
|
1846
|
-
continue: this.settings.continue,
|
|
1847
|
-
allowedTools: this.settings.allowedTools,
|
|
1848
|
-
disallowedTools: this.settings.disallowedTools,
|
|
1849
|
-
betas: this.settings.betas,
|
|
1850
|
-
allowDangerouslySkipPermissions: this.settings.allowDangerouslySkipPermissions,
|
|
1851
|
-
enableFileCheckpointing: this.settings.enableFileCheckpointing,
|
|
1852
|
-
maxBudgetUsd: this.settings.maxBudgetUsd,
|
|
1853
|
-
plugins: this.settings.plugins,
|
|
1854
|
-
resumeSessionAt: this.settings.resumeSessionAt,
|
|
1855
|
-
sandbox: this.settings.sandbox,
|
|
1856
|
-
tools: this.settings.tools,
|
|
1857
|
-
mcpServers: this.settings.mcpServers,
|
|
1858
|
-
canUseTool: this.settings.canUseTool
|
|
1859
|
-
};
|
|
1860
|
-
if (this.settings.onUserDialog !== void 0) {
|
|
1861
|
-
opts.onUserDialog = this.settings.onUserDialog;
|
|
1862
|
-
}
|
|
1863
|
-
if (this.settings.supportedDialogKinds !== void 0) {
|
|
1864
|
-
opts.supportedDialogKinds = this.settings.supportedDialogKinds;
|
|
1865
|
-
}
|
|
1866
|
-
if (this.settings.systemPrompt !== void 0) {
|
|
1867
|
-
opts.systemPrompt = this.settings.systemPrompt;
|
|
1868
|
-
} else if (this.settings.customSystemPrompt !== void 0) {
|
|
1869
|
-
this.logger.warn(
|
|
1870
|
-
"[claude-code] 'customSystemPrompt' is deprecated and will be removed in a future major release. Please use 'systemPrompt' instead (string or { type: 'preset', preset: 'claude_code', append? })."
|
|
1871
|
-
);
|
|
1872
|
-
opts.systemPrompt = this.settings.customSystemPrompt;
|
|
1873
|
-
} else if (this.settings.appendSystemPrompt !== void 0) {
|
|
1874
|
-
this.logger.warn(
|
|
1875
|
-
"[claude-code] 'appendSystemPrompt' is deprecated and will be removed in a future major release. Please use 'systemPrompt: { type: 'preset', preset: 'claude_code', append: <text> }' instead."
|
|
1876
|
-
);
|
|
1877
|
-
opts.systemPrompt = {
|
|
1878
|
-
type: "preset",
|
|
1879
|
-
preset: "claude_code",
|
|
1880
|
-
append: this.settings.appendSystemPrompt
|
|
1881
|
-
};
|
|
1882
|
-
}
|
|
1883
|
-
if (this.settings.settingSources !== void 0) {
|
|
1884
|
-
opts.settingSources = this.settings.settingSources;
|
|
1885
|
-
} else {
|
|
1886
|
-
opts.settingSources = [];
|
|
1887
|
-
}
|
|
1888
|
-
if (this.settings.additionalDirectories !== void 0) {
|
|
1889
|
-
opts.additionalDirectories = this.settings.additionalDirectories;
|
|
1890
|
-
}
|
|
1891
|
-
if (this.settings.agents !== void 0) {
|
|
1892
|
-
opts.agents = this.settings.agents;
|
|
1893
|
-
}
|
|
1894
|
-
if (this.settings.skills !== void 0) {
|
|
1895
|
-
opts.skills = this.settings.skills;
|
|
1896
|
-
}
|
|
1897
|
-
if (this.settings.settings !== void 0) {
|
|
1898
|
-
opts.settings = this.settings.settings;
|
|
1899
|
-
}
|
|
1900
|
-
if (this.settings.managedSettings !== void 0) {
|
|
1901
|
-
opts.managedSettings = this.settings.managedSettings;
|
|
1902
|
-
}
|
|
1903
|
-
if (this.settings.toolAliases !== void 0) {
|
|
1904
|
-
opts.toolAliases = this.settings.toolAliases;
|
|
1905
|
-
}
|
|
1906
|
-
if (this.settings.toolConfig !== void 0) {
|
|
1907
|
-
opts.toolConfig = this.settings.toolConfig;
|
|
1908
|
-
}
|
|
1909
|
-
if (this.settings.planModeInstructions !== void 0) {
|
|
1910
|
-
opts.planModeInstructions = this.settings.planModeInstructions;
|
|
1911
|
-
}
|
|
1912
|
-
if (this.settings.title !== void 0) {
|
|
1913
|
-
opts.title = this.settings.title;
|
|
1914
|
-
}
|
|
1915
|
-
if (this.settings.forwardSubagentText !== void 0) {
|
|
1916
|
-
opts.forwardSubagentText = this.settings.forwardSubagentText;
|
|
1917
|
-
}
|
|
1918
|
-
if (this.settings.agentProgressSummaries !== void 0) {
|
|
1919
|
-
opts.agentProgressSummaries = this.settings.agentProgressSummaries;
|
|
1920
|
-
}
|
|
1921
|
-
if (this.settings.includeHookEvents !== void 0) {
|
|
1922
|
-
opts.includeHookEvents = this.settings.includeHookEvents;
|
|
1923
|
-
}
|
|
1924
|
-
if (this.settings.taskBudget !== void 0) {
|
|
1925
|
-
opts.taskBudget = this.settings.taskBudget;
|
|
1926
|
-
}
|
|
1927
|
-
if (this.settings.sessionStore !== void 0) {
|
|
1928
|
-
opts.sessionStore = this.settings.sessionStore;
|
|
1929
|
-
}
|
|
1930
|
-
if (this.settings.sessionStoreFlush !== void 0) {
|
|
1931
|
-
opts.sessionStoreFlush = this.settings.sessionStoreFlush;
|
|
1932
|
-
}
|
|
1933
|
-
if (this.settings.loadTimeoutMs !== void 0) {
|
|
1934
|
-
opts.loadTimeoutMs = this.settings.loadTimeoutMs;
|
|
1935
|
-
}
|
|
1936
|
-
if (this.settings.includePartialMessages !== void 0) {
|
|
1937
|
-
opts.includePartialMessages = this.settings.includePartialMessages;
|
|
1938
|
-
}
|
|
1939
|
-
if (this.settings.fallbackModel !== void 0) {
|
|
1940
|
-
opts.fallbackModel = this.settings.fallbackModel;
|
|
1941
|
-
}
|
|
1942
|
-
if (this.settings.forkSession !== void 0) {
|
|
1943
|
-
opts.forkSession = this.settings.forkSession;
|
|
1944
|
-
}
|
|
1945
|
-
if (this.settings.strictMcpConfig !== void 0) {
|
|
1946
|
-
opts.strictMcpConfig = this.settings.strictMcpConfig;
|
|
1947
|
-
}
|
|
1948
|
-
if (this.settings.extraArgs !== void 0) {
|
|
1949
|
-
opts.extraArgs = this.settings.extraArgs;
|
|
1950
|
-
}
|
|
1951
|
-
if (this.settings.persistSession !== void 0) {
|
|
1952
|
-
opts.persistSession = this.settings.persistSession;
|
|
1953
|
-
}
|
|
1954
|
-
if (this.settings.spawnClaudeCodeProcess !== void 0) {
|
|
1955
|
-
opts.spawnClaudeCodeProcess = this.settings.spawnClaudeCodeProcess;
|
|
1956
|
-
}
|
|
1957
|
-
if (this.settings.hooks) {
|
|
1958
|
-
opts.hooks = this.settings.hooks;
|
|
1959
|
-
}
|
|
1960
|
-
const effectiveForkSession = sdkOptions?.forkSession ?? this.settings.forkSession;
|
|
1961
|
-
const effectiveContinue = sdkOptions?.continue ?? this.settings.continue;
|
|
1962
|
-
if (this.settings.sessionId !== void 0 && !_ClaudeCodeLanguageModel.sessionIdConflictsWithResumeOrContinue({
|
|
1963
|
-
resumePresent: opts.resume !== void 0,
|
|
1964
|
-
continue: effectiveContinue === true,
|
|
1965
|
-
forkSession: effectiveForkSession === true
|
|
1966
|
-
})) {
|
|
1967
|
-
opts.sessionId = this.settings.sessionId;
|
|
1968
|
-
}
|
|
1969
|
-
if (this.settings.debug !== void 0) {
|
|
1970
|
-
opts.debug = this.settings.debug;
|
|
1971
|
-
}
|
|
1972
|
-
if (this.settings.debugFile !== void 0) {
|
|
1973
|
-
opts.debugFile = this.settings.debugFile;
|
|
1974
|
-
}
|
|
1975
|
-
const sdkOverrides = sdkOptions ? sdkOptions : void 0;
|
|
1976
|
-
const sdkEnv = sdkOverrides && typeof sdkOverrides.env === "object" && sdkOverrides.env !== null ? sdkOverrides.env : void 0;
|
|
1977
|
-
const sdkStderr = sdkOverrides && typeof sdkOverrides.stderr === "function" ? sdkOverrides.stderr : void 0;
|
|
1978
|
-
if (sdkOverrides) {
|
|
1979
|
-
const rest = { ...sdkOverrides };
|
|
1980
|
-
delete rest.env;
|
|
1981
|
-
delete rest.stderr;
|
|
1982
|
-
for (const [key, value] of Object.entries(rest)) {
|
|
1983
|
-
if (value !== void 0) {
|
|
1984
|
-
opts[key] = value;
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
this.applySessionResolution(opts, effectiveResume);
|
|
1989
|
-
if (typeof opts.fallbackModel === "string" && opts.fallbackModel === opts.model) {
|
|
1990
|
-
throw new Error(
|
|
1991
|
-
`fallbackModel cannot be the same as the model ('${String(opts.model)}'). Specify a different model for fallbackModel, or remove it.`
|
|
1992
|
-
);
|
|
1993
|
-
}
|
|
1994
|
-
const userStderrCallback = sdkStderr ?? this.settings.stderr;
|
|
1995
|
-
if (stderrCollector || userStderrCallback) {
|
|
1996
|
-
opts.stderr = (data) => {
|
|
1997
|
-
if (stderrCollector) stderrCollector(data);
|
|
1998
|
-
if (userStderrCallback) userStderrCallback(data);
|
|
1999
|
-
};
|
|
2000
|
-
}
|
|
2001
|
-
const mergedEnv = {
|
|
2002
|
-
...getBaseProcessEnv(),
|
|
2003
|
-
...this.settings.env,
|
|
2004
|
-
...sdkEnv
|
|
2005
|
-
};
|
|
2006
|
-
if (!("CLAUDE_AGENT_SDK_CLIENT_APP" in mergedEnv)) {
|
|
2007
|
-
mergedEnv.CLAUDE_AGENT_SDK_CLIENT_APP = DEFAULT_CLIENT_APP;
|
|
2008
|
-
}
|
|
2009
|
-
opts.env = mergedEnv;
|
|
2010
|
-
if (responseFormat?.type === "json" && responseFormat.schema) {
|
|
2011
|
-
const { schema: sanitizedSchema, strippedFormatPaths } = sanitizeJsonSchemaForOutputFormat(
|
|
2012
|
-
responseFormat.schema
|
|
2013
|
-
);
|
|
2014
|
-
if (strippedFormatPaths.length > 0) {
|
|
2015
|
-
this.logger.debug(
|
|
2016
|
-
`[claude-code] Stripped unsupported 'format' keywords from outputFormat schema (hints folded into descriptions; client-side Zod validation still enforces them) at: ${strippedFormatPaths.join(", ")}`
|
|
2017
|
-
);
|
|
2018
|
-
}
|
|
2019
|
-
opts.outputFormat = {
|
|
2020
|
-
type: "json_schema",
|
|
2021
|
-
schema: sanitizedSchema
|
|
2022
|
-
};
|
|
2023
|
-
}
|
|
2024
|
-
return opts;
|
|
2025
|
-
}
|
|
2026
|
-
handleClaudeCodeError(error, messagesPrompt, collectedStderr) {
|
|
2027
|
-
if (isAbortError(error)) {
|
|
2028
|
-
throw error;
|
|
2029
|
-
}
|
|
2030
|
-
if (error instanceof import_provider2.APICallError || error instanceof import_provider2.LoadAPIKeyError) {
|
|
2031
|
-
return error;
|
|
2032
|
-
}
|
|
2033
|
-
const isErrorWithMessage = (err) => {
|
|
2034
|
-
return typeof err === "object" && err !== null && "message" in err;
|
|
2035
|
-
};
|
|
2036
|
-
const isErrorWithCode = (err) => {
|
|
2037
|
-
return typeof err === "object" && err !== null;
|
|
2038
|
-
};
|
|
2039
|
-
const authErrorPatterns = [
|
|
2040
|
-
"not logged in",
|
|
2041
|
-
"authentication",
|
|
2042
|
-
"unauthorized",
|
|
2043
|
-
"auth failed",
|
|
2044
|
-
"please login",
|
|
2045
|
-
"claude login",
|
|
2046
|
-
"claude auth login",
|
|
2047
|
-
"/login",
|
|
2048
|
-
// CLI returns "Please run /login"
|
|
2049
|
-
"invalid api key",
|
|
2050
|
-
"oauth_org_not_allowed"
|
|
2051
|
-
// SDK 0.3.x assistant error kind: OAuth org not permitted
|
|
2052
|
-
];
|
|
2053
|
-
const errorMessage = isErrorWithMessage(error) && error.message ? error.message.toLowerCase() : "";
|
|
2054
|
-
const errorKind = getStructuredErrorKind(error);
|
|
2055
|
-
const exitCode = isErrorWithCode(error) && typeof error.exitCode === "number" ? error.exitCode : void 0;
|
|
2056
|
-
const isAuthError = errorKind === "authentication_failed" || errorKind === "oauth_org_not_allowed" || authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
|
|
2057
|
-
if (isAuthError) {
|
|
2058
|
-
return createAuthenticationError({
|
|
2059
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
|
|
2060
|
-
});
|
|
2061
|
-
}
|
|
2062
|
-
const errorCode = isErrorWithCode(error) && typeof error.code === "string" ? error.code : "";
|
|
2063
|
-
if (errorCode === "ETIMEDOUT" || errorMessage.includes("timeout")) {
|
|
2064
|
-
return createTimeoutError({
|
|
2065
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Request timed out",
|
|
2066
|
-
promptExcerpt: messagesPrompt.substring(0, 200)
|
|
2067
|
-
// Don't specify timeoutMs since we don't know the actual timeout value
|
|
2068
|
-
// It's controlled by the consumer via AbortSignal
|
|
2069
|
-
});
|
|
2070
|
-
}
|
|
2071
|
-
const stderrFromError = isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0;
|
|
2072
|
-
const stderr = stderrFromError || collectedStderr || void 0;
|
|
2073
|
-
if (errorKind === "overloaded" || errorKind === "rate_limit" || errorMessage.includes("overloaded")) {
|
|
2074
|
-
return createAPICallError({
|
|
2075
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Anthropic API is overloaded. Please retry.",
|
|
2076
|
-
code: errorCode || void 0,
|
|
2077
|
-
exitCode,
|
|
2078
|
-
stderr,
|
|
2079
|
-
promptExcerpt: messagesPrompt.substring(0, 200),
|
|
2080
|
-
isRetryable: true
|
|
2081
|
-
});
|
|
2082
|
-
}
|
|
2083
|
-
if (errorKind === "model_not_found" || errorMessage.includes("model_not_found") || errorMessage.includes("no such model")) {
|
|
2084
|
-
const originalMessage = isErrorWithMessage(error) && error.message ? error.message : "Model not found";
|
|
2085
|
-
return createAPICallError({
|
|
2086
|
-
message: `${originalMessage}. The requested model was not found. Verify the model id passed to the provider (e.g. 'opus', 'sonnet', 'haiku', or a full model name) and that your account has access to it.`,
|
|
2087
|
-
code: errorCode || void 0,
|
|
2088
|
-
exitCode,
|
|
2089
|
-
stderr,
|
|
2090
|
-
promptExcerpt: messagesPrompt.substring(0, 200),
|
|
2091
|
-
isRetryable: false
|
|
2092
|
-
});
|
|
2093
|
-
}
|
|
2094
|
-
const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
|
|
2095
|
-
return createAPICallError({
|
|
2096
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
|
|
2097
|
-
code: errorCode || void 0,
|
|
2098
|
-
exitCode,
|
|
2099
|
-
stderr,
|
|
2100
|
-
promptExcerpt: messagesPrompt.substring(0, 200),
|
|
2101
|
-
isRetryable
|
|
2102
|
-
});
|
|
2103
|
-
}
|
|
2104
|
-
setSessionId(sessionId) {
|
|
2105
|
-
this.sessionId = sessionId;
|
|
2106
|
-
const warning = validateSessionId(sessionId);
|
|
2107
|
-
if (warning) {
|
|
2108
|
-
this.logger.warn(`Claude Code Session: ${warning}`);
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
logMcpConnectionIssues(mcpServers) {
|
|
2112
|
-
if (!Array.isArray(mcpServers) || mcpServers.length === 0) {
|
|
2113
|
-
return;
|
|
2114
|
-
}
|
|
2115
|
-
const serversNeedingAttention = mcpServers.filter((server) => {
|
|
2116
|
-
const status = typeof server.status === "string" ? server.status.toLowerCase() : "";
|
|
2117
|
-
return status === "failed" || status === "needs-auth";
|
|
2118
|
-
});
|
|
2119
|
-
if (serversNeedingAttention.length === 0) {
|
|
2120
|
-
return;
|
|
2121
|
-
}
|
|
2122
|
-
const details = serversNeedingAttention.map((server) => {
|
|
2123
|
-
const name = typeof server.name === "string" && server.name.trim().length > 0 ? server.name : "<unknown>";
|
|
2124
|
-
const status = typeof server.status === "string" && server.status.trim().length > 0 ? server.status : "unknown";
|
|
2125
|
-
const error = typeof server.error === "string" && server.error.trim().length > 0 ? ` (${server.error})` : "";
|
|
2126
|
-
return `${name}:${status}${error}`;
|
|
2127
|
-
}).join(", ");
|
|
2128
|
-
this.logger.warn(`[claude-code] MCP servers not connected: ${details}`);
|
|
2129
|
-
}
|
|
2130
|
-
/**
|
|
2131
|
-
* Handles SDK 0.3.x system messages other than 'init', shared by doGenerate
|
|
2132
|
-
* and doStream:
|
|
2133
|
-
* - 'api_retry' is counted into providerMetadata (`apiRetries`) and debug-logged.
|
|
2134
|
-
* - 'permission_denied' is warn-logged and recorded into providerMetadata
|
|
2135
|
-
* (`permissionDenials`); without this a denial is invisible until the
|
|
2136
|
-
* model talks about it.
|
|
2137
|
-
* - 'model_refusal_fallback' is debug-logged (the superseding assistant
|
|
2138
|
-
* message is handled by the text-dedup guard in the message loops).
|
|
2139
|
-
* - 'thinking_tokens' deltas are accumulated into providerMetadata
|
|
2140
|
-
* (`estimatedThinkingTokens`); the estimate is explicitly not the
|
|
2141
|
-
* authoritative billed output tokens, so it is surfaced as metadata
|
|
2142
|
-
* instead of feeding `usage.outputTokens.reasoning`.
|
|
2143
|
-
* - The subtypes in {@link INFORMATIONAL_SYSTEM_SUBTYPES} are intentionally
|
|
2144
|
-
* informational and only debug-logged.
|
|
2145
|
-
*/
|
|
2146
|
-
handleSystemMessage(message, tracking, onRetractedUuids) {
|
|
2147
|
-
switch (message.subtype) {
|
|
2148
|
-
case "api_retry":
|
|
2149
|
-
tracking.apiRetries += 1;
|
|
2150
|
-
this.logger.debug(
|
|
2151
|
-
`[claude-code] API retry ${message.attempt}/${message.max_retries} in ${message.retry_delay_ms}ms - Status: ${message.error_status ?? "unknown"}, Error: ${message.error}`
|
|
2152
|
-
);
|
|
2153
|
-
break;
|
|
2154
|
-
case "permission_denied": {
|
|
2155
|
-
const reason = message.decision_reason ?? message.message;
|
|
2156
|
-
tracking.permissionDenials.push({
|
|
2157
|
-
toolName: message.tool_name,
|
|
2158
|
-
toolUseId: message.tool_use_id,
|
|
2159
|
-
...reason !== void 0 && { reason }
|
|
2160
|
-
});
|
|
2161
|
-
this.logger.warn(
|
|
2162
|
-
`[claude-code] Permission denied - Tool: ${message.tool_name}${reason ? `, Reason: ${reason}` : ""}`
|
|
2163
|
-
);
|
|
2164
|
-
break;
|
|
2165
|
-
}
|
|
2166
|
-
case "mirror_error": {
|
|
2167
|
-
const mirrorError = message.error ?? "unknown error";
|
|
2168
|
-
const mirrorSessionId = message.key?.sessionId ?? message.session_id ?? "unknown";
|
|
2169
|
-
tracking.mirrorErrors.push({ error: mirrorError, sessionId: mirrorSessionId });
|
|
2170
|
-
this.logger.warn(
|
|
2171
|
-
`[claude-code] SessionStore mirror error (transcript batch dropped) - Session: ${mirrorSessionId}, Error: ${mirrorError}`
|
|
2172
|
-
);
|
|
2173
|
-
break;
|
|
2174
|
-
}
|
|
2175
|
-
case "model_refusal_fallback": {
|
|
2176
|
-
this.logger.debug(
|
|
2177
|
-
`[claude-code] Model refusal fallback - ${message.original_model} -> ${message.fallback_model} (direction: ${message.direction})`
|
|
2178
|
-
);
|
|
2179
|
-
const retractedUuids = message.retracted_message_uuids;
|
|
2180
|
-
if (onRetractedUuids && retractedUuids && retractedUuids.length > 0) {
|
|
2181
|
-
this.logger.debug(
|
|
2182
|
-
`[claude-code] Refusal fallback retracts ${retractedUuids.length} message uuid(s)`
|
|
2183
|
-
);
|
|
2184
|
-
onRetractedUuids(retractedUuids);
|
|
2185
|
-
}
|
|
2186
|
-
break;
|
|
2187
|
-
}
|
|
2188
|
-
case "thinking_tokens":
|
|
2189
|
-
tracking.estimatedThinkingTokens += message.estimated_tokens_delta;
|
|
2190
|
-
this.logger.debug(
|
|
2191
|
-
`[claude-code] Thinking tokens estimate - block total: ${message.estimated_tokens}, delta: ${message.estimated_tokens_delta}, accumulated: ${tracking.estimatedThinkingTokens}`
|
|
2192
|
-
);
|
|
2193
|
-
break;
|
|
2194
|
-
default:
|
|
2195
|
-
if (INFORMATIONAL_SYSTEM_SUBTYPES.has(message.subtype)) {
|
|
2196
|
-
this.logger.debug(
|
|
2197
|
-
`[claude-code] Ignoring informational system message: ${message.subtype}`
|
|
2198
|
-
);
|
|
2199
|
-
} else {
|
|
2200
|
-
this.logger.debug(`[claude-code] Unhandled system message subtype: ${message.subtype}`);
|
|
2201
|
-
}
|
|
2202
|
-
break;
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
/**
|
|
2206
|
-
* Merges the result message's `permission_denials` list into the tracked
|
|
2207
|
-
* denials. PreToolUse-hook denies bypass canUseTool and emit no
|
|
2208
|
-
* `permission_denied` system event (per the SDK docs on
|
|
2209
|
-
* SDKPermissionDeniedMessage), so the result list is the only place they
|
|
2210
|
-
* surface. Entries already recorded from stream-time events are deduped by
|
|
2211
|
-
* `tool_use_id`.
|
|
2212
|
-
*/
|
|
2213
|
-
mergeResultPermissionDenials(message, tracking) {
|
|
2214
|
-
for (const denial of message.permission_denials ?? []) {
|
|
2215
|
-
const alreadyTracked = tracking.permissionDenials.some(
|
|
2216
|
-
(d) => d.toolUseId !== void 0 && d.toolUseId === denial.tool_use_id
|
|
2217
|
-
);
|
|
2218
|
-
if (!alreadyTracked) {
|
|
2219
|
-
tracking.permissionDenials.push({
|
|
2220
|
-
toolName: denial.tool_name,
|
|
2221
|
-
toolUseId: denial.tool_use_id
|
|
2222
|
-
});
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
/**
|
|
2227
|
-
* Bounded post-result drain for the `prompt_suggestion` message
|
|
2228
|
-
* (promptSuggestions: true), shared by doGenerate and doStream. The
|
|
2229
|
-
* suggestion arrives AFTER the result message; the SDK emits at most one
|
|
2230
|
-
* per turn, so stop once it is delivered, and a timeout closes the
|
|
2231
|
-
* iterator (tearing down the subprocess) if the CLI lingers after the
|
|
2232
|
-
* result without emitting one. Advances the response's own generator, so
|
|
2233
|
-
* the caller's surrounding loop resumes to a finished iterator.
|
|
2234
|
-
*/
|
|
2235
|
-
async drainPromptSuggestion(response, onPromptSuggestion) {
|
|
2236
|
-
const iterator = response[Symbol.asyncIterator]();
|
|
2237
|
-
let drainTimer;
|
|
2238
|
-
const drainTimeout = new Promise((resolve) => {
|
|
2239
|
-
drainTimer = setTimeout(
|
|
2240
|
-
() => resolve("timeout"),
|
|
2241
|
-
_ClaudeCodeLanguageModel.PROMPT_SUGGESTION_DRAIN_TIMEOUT_MS
|
|
2242
|
-
);
|
|
2243
|
-
drainTimer.unref?.();
|
|
2244
|
-
});
|
|
2245
|
-
try {
|
|
2246
|
-
while (true) {
|
|
2247
|
-
const winner = await Promise.race([iterator.next(), drainTimeout]);
|
|
2248
|
-
if (winner === "timeout") {
|
|
2249
|
-
this.logger.debug("[claude-code] Post-result drain timed out; closing SDK iterator");
|
|
2250
|
-
void iterator.return?.().catch(() => {
|
|
2251
|
-
});
|
|
2252
|
-
break;
|
|
2253
|
-
}
|
|
2254
|
-
if (winner.done) {
|
|
2255
|
-
break;
|
|
2256
|
-
}
|
|
2257
|
-
const trailingMessage = winner.value;
|
|
2258
|
-
this.logger.debug(`[claude-code] Post-result message type: ${trailingMessage.type}`);
|
|
2259
|
-
if (trailingMessage.type === "prompt_suggestion") {
|
|
2260
|
-
onPromptSuggestion(trailingMessage.suggestion);
|
|
2261
|
-
void iterator.return?.().catch(() => {
|
|
2262
|
-
});
|
|
2263
|
-
break;
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
} catch (drainError) {
|
|
2267
|
-
this.logger.debug(
|
|
2268
|
-
`[claude-code] Error draining post-result messages: ${drainError instanceof Error ? drainError.message : String(drainError)}`
|
|
2269
|
-
);
|
|
2270
|
-
} finally {
|
|
2271
|
-
if (drainTimer !== void 0) {
|
|
2272
|
-
clearTimeout(drainTimer);
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
}
|
|
2276
|
-
async doGenerate(options) {
|
|
2277
|
-
this.logger.debug(`[claude-code] Starting doGenerate request with model: ${this.modelId}`);
|
|
2278
|
-
this.logger.debug(`[claude-code] Response format: ${options.responseFormat?.type ?? "none"}`);
|
|
2279
|
-
const {
|
|
2280
|
-
messagesPrompt,
|
|
2281
|
-
warnings: messageWarnings,
|
|
2282
|
-
streamingContentParts,
|
|
2283
|
-
hasImageParts
|
|
2284
|
-
} = convertToClaudeCodeMessages(options.prompt);
|
|
2285
|
-
this.logger.debug(
|
|
2286
|
-
`[claude-code] Converted ${options.prompt.length} messages, hasImageParts: ${hasImageParts}`
|
|
2287
|
-
);
|
|
2288
|
-
const abortController = new AbortController();
|
|
2289
|
-
let abortListener;
|
|
2290
|
-
if (options.abortSignal?.aborted) {
|
|
2291
|
-
abortController.abort(options.abortSignal.reason);
|
|
2292
|
-
}
|
|
2293
|
-
let collectedStderr = "";
|
|
2294
|
-
const stderrCollector = (data) => {
|
|
2295
|
-
collectedStderr += data;
|
|
2296
|
-
};
|
|
2297
|
-
const sdkOptions = this.getSanitizedSdkOptions();
|
|
2298
|
-
const effectiveResume = this.getEffectiveResume(sdkOptions);
|
|
2299
|
-
const queryOptions = this.createQueryOptions(
|
|
2300
|
-
abortController,
|
|
2301
|
-
options.responseFormat,
|
|
2302
|
-
stderrCollector,
|
|
2303
|
-
sdkOptions,
|
|
2304
|
-
effectiveResume
|
|
2305
|
-
);
|
|
2306
|
-
if (options.abortSignal && !options.abortSignal.aborted) {
|
|
2307
|
-
abortListener = () => abortController.abort(options.abortSignal?.reason);
|
|
2308
|
-
options.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
2309
|
-
}
|
|
2310
|
-
let text = "";
|
|
2311
|
-
const contentSegments = [];
|
|
2312
|
-
const joinTextSegments = () => contentSegments.filter((segment) => segment.kind === "text").map((segment) => segment.text).join("");
|
|
2313
|
-
const joinFinalTurnTextSegments = () => {
|
|
2314
|
-
let start = 0;
|
|
2315
|
-
for (let i = 0; i < contentSegments.length; i++) {
|
|
2316
|
-
const kind = contentSegments[i]?.kind;
|
|
2317
|
-
if (kind === "tool-result" || kind === "tool-error") {
|
|
2318
|
-
start = i + 1;
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
return contentSegments.slice(start).filter((segment) => segment.kind === "text").map((segment) => segment.text).join("");
|
|
2322
|
-
};
|
|
2323
|
-
const knownTools = /* @__PURE__ */ new Map();
|
|
2324
|
-
const retractedToolIds = /* @__PURE__ */ new Set();
|
|
2325
|
-
const evictBuffered = (retracted) => {
|
|
2326
|
-
if (retracted.size === 0) return;
|
|
2327
|
-
const retractedToolCallIds = computeRetractedToolCallIds(
|
|
2328
|
-
retracted,
|
|
2329
|
-
contentSegments.filter(
|
|
2330
|
-
(segment) => segment.kind === "tool-call" || segment.kind === "tool-result" || segment.kind === "tool-error"
|
|
2331
|
-
).map((segment) => ({
|
|
2332
|
-
toolCallId: segment.toolCallId,
|
|
2333
|
-
uuid: segment.uuid
|
|
2334
|
-
}))
|
|
2335
|
-
);
|
|
2336
|
-
for (const toolCallId of retractedToolCallIds) {
|
|
2337
|
-
retractedToolIds.add(toolCallId);
|
|
2338
|
-
knownTools.delete(toolCallId);
|
|
2339
|
-
activeTaskTools.delete(toolCallId);
|
|
2340
|
-
}
|
|
2341
|
-
for (let i = contentSegments.length - 1; i >= 0; i--) {
|
|
2342
|
-
const segment = contentSegments[i];
|
|
2343
|
-
if (segment === void 0) continue;
|
|
2344
|
-
const segmentUuid = "uuid" in segment ? segment.uuid : void 0;
|
|
2345
|
-
const retractsSegment = segmentUuid !== void 0 && retracted.has(segmentUuid) || (segment.kind === "tool-result" || segment.kind === "tool-error") && retractedToolCallIds.has(segment.toolCallId);
|
|
2346
|
-
if (retractsSegment) {
|
|
2347
|
-
contentSegments.splice(i, 1);
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
};
|
|
2351
|
-
const activeTaskTools = /* @__PURE__ */ new Map();
|
|
2352
|
-
const getFallbackParentId = () => {
|
|
2353
|
-
if (activeTaskTools.size === 1) {
|
|
2354
|
-
return activeTaskTools.keys().next().value ?? null;
|
|
2355
|
-
}
|
|
2356
|
-
return null;
|
|
2357
|
-
};
|
|
2358
|
-
let structuredOutput;
|
|
2359
|
-
let receivedResultMessage = false;
|
|
2360
|
-
let usage = createEmptyUsage();
|
|
2361
|
-
let finishReason = { unified: "stop", raw: void 0 };
|
|
2362
|
-
let wasTruncated = false;
|
|
2363
|
-
let costUsd;
|
|
2364
|
-
let durationMs;
|
|
2365
|
-
let modelUsage;
|
|
2366
|
-
let ttftMs;
|
|
2367
|
-
let ttftStreamMs;
|
|
2368
|
-
let timeToRequestMs;
|
|
2369
|
-
let warmSpareClaimed;
|
|
2370
|
-
let terminalReason;
|
|
2371
|
-
const metadataTracking = {
|
|
2372
|
-
apiRetries: 0,
|
|
2373
|
-
permissionDenials: [],
|
|
2374
|
-
mirrorErrors: [],
|
|
2375
|
-
estimatedThinkingTokens: 0
|
|
2376
|
-
};
|
|
2377
|
-
const warnings = this.generateAllWarnings(options, messagesPrompt);
|
|
2378
|
-
if (messageWarnings) {
|
|
2379
|
-
messageWarnings.forEach((warning) => {
|
|
2380
|
-
warnings.push({
|
|
2381
|
-
type: "other",
|
|
2382
|
-
message: warning
|
|
2383
|
-
});
|
|
2384
|
-
});
|
|
2385
|
-
}
|
|
2386
|
-
const modeSetting = this.settings.streamingInput ?? "auto";
|
|
2387
|
-
const effectiveCanUseTool = sdkOptions?.canUseTool ?? this.settings.canUseTool;
|
|
2388
|
-
const effectivePermissionPromptToolName = sdkOptions?.permissionPromptToolName ?? this.settings.permissionPromptToolName;
|
|
2389
|
-
const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!effectiveCanUseTool;
|
|
2390
|
-
if (!wantsStreamInput && hasImageParts) {
|
|
2391
|
-
warnings.push({
|
|
2392
|
-
type: "other",
|
|
2393
|
-
message: STREAMING_FEATURE_WARNING
|
|
2394
|
-
});
|
|
2395
|
-
}
|
|
2396
|
-
let done = () => {
|
|
2397
|
-
};
|
|
2398
|
-
const outputStreamEnded = new Promise((resolve) => {
|
|
2399
|
-
done = () => resolve(void 0);
|
|
2400
|
-
});
|
|
2401
|
-
try {
|
|
2402
|
-
if (effectiveCanUseTool && effectivePermissionPromptToolName) {
|
|
2403
|
-
throw new Error(
|
|
2404
|
-
"canUseTool requires streamingInput mode ('auto' or 'always') and cannot be used with permissionPromptToolName (SDK constraint). Set streamingInput: 'auto' (or 'always') and remove permissionPromptToolName, or remove canUseTool."
|
|
2405
|
-
);
|
|
2406
|
-
}
|
|
2407
|
-
const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
|
|
2408
|
-
messagesPrompt,
|
|
2409
|
-
outputStreamEnded,
|
|
2410
|
-
effectiveResume,
|
|
2411
|
-
streamingContentParts,
|
|
2412
|
-
this.settings.onStreamStart
|
|
2413
|
-
) : messagesPrompt;
|
|
2414
|
-
this.logger.debug(
|
|
2415
|
-
`[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
|
|
2416
|
-
);
|
|
2417
|
-
const response = (0, import_claude_agent_sdk.query)({
|
|
2418
|
-
prompt: sdkPrompt,
|
|
2419
|
-
options: queryOptions
|
|
2420
|
-
});
|
|
2421
|
-
this.settings.onQueryCreated?.(response);
|
|
2422
|
-
let lastAssistantErrorKind;
|
|
2423
|
-
const sdkIterator = response[Symbol.asyncIterator]();
|
|
2424
|
-
const detachableResponse = {
|
|
2425
|
-
[Symbol.asyncIterator]: () => ({
|
|
2426
|
-
next: () => sdkIterator.next(),
|
|
2427
|
-
return: () => {
|
|
2428
|
-
void sdkIterator.return?.().catch(() => {
|
|
2429
|
-
});
|
|
2430
|
-
return Promise.resolve({ done: true, value: void 0 });
|
|
2431
|
-
}
|
|
2432
|
-
})
|
|
2433
|
-
};
|
|
2434
|
-
for await (const message of detachableResponse) {
|
|
2435
|
-
this.logger.debug(`[claude-code] Received message type: ${message.type}`);
|
|
2436
|
-
if (message.type === "assistant") {
|
|
2437
|
-
if (typeof message.error === "string") {
|
|
2438
|
-
lastAssistantErrorKind = message.error;
|
|
2439
|
-
}
|
|
2440
|
-
applySupersede(message, evictBuffered, this.logger, "truthy");
|
|
2441
|
-
const messageUuid = typeof message.uuid === "string" ? message.uuid : void 0;
|
|
2442
|
-
const sdkParentToolUseId = message.parent_tool_use_id;
|
|
2443
|
-
const content = message.message.content;
|
|
2444
|
-
if (Array.isArray(content)) {
|
|
2445
|
-
for (const block of content) {
|
|
2446
|
-
if (!isContentBlock(block)) continue;
|
|
2447
|
-
if (block.type === "text" && typeof block.text === "string") {
|
|
2448
|
-
if (block.text.length > 0) {
|
|
2449
|
-
contentSegments.push({
|
|
2450
|
-
kind: "text",
|
|
2451
|
-
...messageUuid !== void 0 && { uuid: messageUuid },
|
|
2452
|
-
text: block.text
|
|
2453
|
-
});
|
|
2454
|
-
}
|
|
2455
|
-
} else if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
2456
|
-
contentSegments.push({
|
|
2457
|
-
kind: "reasoning",
|
|
2458
|
-
...messageUuid !== void 0 && { uuid: messageUuid },
|
|
2459
|
-
text: block.thinking
|
|
2460
|
-
});
|
|
2461
|
-
} else if (block.type === "tool_use") {
|
|
2462
|
-
const [tool3] = this.extractToolUses([block]);
|
|
2463
|
-
if (!tool3) continue;
|
|
2464
|
-
const parentToolCallId = isSubagentToolName(tool3.name) ? null : resolveToolParentId(
|
|
2465
|
-
sdkParentToolUseId,
|
|
2466
|
-
tool3.parentToolUseId,
|
|
2467
|
-
getFallbackParentId
|
|
2468
|
-
);
|
|
2469
|
-
this.logger.debug(
|
|
2470
|
-
`[claude-code] Tool use detected - Tool: ${tool3.name}, ID: ${tool3.id}, SDK parent: ${sdkParentToolUseId}, resolved parent: ${parentToolCallId}`
|
|
2471
|
-
);
|
|
2472
|
-
knownTools.set(tool3.id, { name: tool3.name, parentToolCallId });
|
|
2473
|
-
if (isSubagentToolName(tool3.name)) {
|
|
2474
|
-
activeTaskTools.set(tool3.id, { startTime: Date.now() });
|
|
2475
|
-
}
|
|
2476
|
-
contentSegments.push({
|
|
2477
|
-
kind: "tool-call",
|
|
2478
|
-
...messageUuid !== void 0 && { uuid: messageUuid },
|
|
2479
|
-
toolCallId: tool3.id,
|
|
2480
|
-
part: this.buildToolCallPart(
|
|
2481
|
-
tool3.id,
|
|
2482
|
-
tool3.name,
|
|
2483
|
-
this.serializeToolInput(tool3.input),
|
|
2484
|
-
parentToolCallId
|
|
2485
|
-
)
|
|
2486
|
-
});
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
}
|
|
2490
|
-
text = joinTextSegments();
|
|
2491
|
-
} else if (message.type === "user") {
|
|
2492
|
-
if (!message.message?.content) {
|
|
2493
|
-
this.logger.warn(
|
|
2494
|
-
`[claude-code] Unexpected user message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
|
|
2495
|
-
);
|
|
2496
|
-
continue;
|
|
2497
|
-
}
|
|
2498
|
-
const sdkParentToolUseIdForResults = message.parent_tool_use_id;
|
|
2499
|
-
const resultMessageUuid = typeof message.uuid === "string" ? message.uuid : void 0;
|
|
2500
|
-
const content = message.message.content;
|
|
2501
|
-
for (const result of this.extractToolResults(content)) {
|
|
2502
|
-
if (this.isRetractedToolFrame(result.id, retractedToolIds, "result")) {
|
|
2503
|
-
continue;
|
|
2504
|
-
}
|
|
2505
|
-
const known = knownTools.get(result.id);
|
|
2506
|
-
const toolName = result.name ?? known?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
2507
|
-
this.logger.debug(
|
|
2508
|
-
`[claude-code] Tool result received - Tool: ${toolName}, ID: ${result.id}`
|
|
2509
|
-
);
|
|
2510
|
-
let parentToolCallId;
|
|
2511
|
-
if (known) {
|
|
2512
|
-
known.name = toolName;
|
|
2513
|
-
parentToolCallId = known.parentToolCallId;
|
|
2514
|
-
} else {
|
|
2515
|
-
this.logger.warn(
|
|
2516
|
-
`[claude-code] Received tool result for unknown tool ID: ${result.id}`
|
|
2517
|
-
);
|
|
2518
|
-
parentToolCallId = isSubagentToolName(toolName) ? null : resolveToolParentId(sdkParentToolUseIdForResults, void 0, getFallbackParentId);
|
|
2519
|
-
knownTools.set(result.id, { name: toolName, parentToolCallId });
|
|
2520
|
-
contentSegments.push({
|
|
2521
|
-
kind: "tool-call",
|
|
2522
|
-
toolCallId: result.id,
|
|
2523
|
-
part: this.buildToolCallPart(result.id, toolName, "", parentToolCallId)
|
|
2524
|
-
});
|
|
2525
|
-
}
|
|
2526
|
-
if (isSubagentToolName(toolName)) {
|
|
2527
|
-
activeTaskTools.delete(result.id);
|
|
2528
|
-
}
|
|
2529
|
-
contentSegments.push({
|
|
2530
|
-
kind: "tool-result",
|
|
2531
|
-
...resultMessageUuid !== void 0 && { uuid: resultMessageUuid },
|
|
2532
|
-
toolCallId: result.id,
|
|
2533
|
-
part: this.buildToolResultPart(
|
|
2534
|
-
result.id,
|
|
2535
|
-
toolName,
|
|
2536
|
-
result.result,
|
|
2537
|
-
result.isError,
|
|
2538
|
-
parentToolCallId
|
|
2539
|
-
)
|
|
2540
|
-
});
|
|
2541
|
-
}
|
|
2542
|
-
for (const error of this.extractToolErrors(content)) {
|
|
2543
|
-
if (this.isRetractedToolFrame(error.id, retractedToolIds, "error")) {
|
|
2544
|
-
continue;
|
|
2545
|
-
}
|
|
2546
|
-
const known = knownTools.get(error.id);
|
|
2547
|
-
const toolName = error.name ?? known?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
2548
|
-
this.logger.debug(
|
|
2549
|
-
`[claude-code] Tool error received - Tool: ${toolName}, ID: ${error.id}`
|
|
2550
|
-
);
|
|
2551
|
-
let parentToolCallId;
|
|
2552
|
-
if (known) {
|
|
2553
|
-
known.name = toolName;
|
|
2554
|
-
parentToolCallId = known.parentToolCallId;
|
|
2555
|
-
} else {
|
|
2556
|
-
this.logger.warn(
|
|
2557
|
-
`[claude-code] Received tool error for unknown tool ID: ${error.id}`
|
|
2558
|
-
);
|
|
2559
|
-
parentToolCallId = isSubagentToolName(toolName) ? null : resolveToolParentId(sdkParentToolUseIdForResults, void 0, getFallbackParentId);
|
|
2560
|
-
knownTools.set(error.id, { name: toolName, parentToolCallId });
|
|
2561
|
-
contentSegments.push({
|
|
2562
|
-
kind: "tool-call",
|
|
2563
|
-
toolCallId: error.id,
|
|
2564
|
-
part: this.buildToolCallPart(error.id, toolName, "", parentToolCallId)
|
|
2565
|
-
});
|
|
2566
|
-
}
|
|
2567
|
-
if (isSubagentToolName(toolName)) {
|
|
2568
|
-
activeTaskTools.delete(error.id);
|
|
2569
|
-
}
|
|
2570
|
-
contentSegments.push({
|
|
2571
|
-
kind: "tool-error",
|
|
2572
|
-
...resultMessageUuid !== void 0 && { uuid: resultMessageUuid },
|
|
2573
|
-
toolCallId: error.id,
|
|
2574
|
-
part: this.buildToolErrorResultPart(
|
|
2575
|
-
error.id,
|
|
2576
|
-
toolName,
|
|
2577
|
-
error.error,
|
|
2578
|
-
parentToolCallId
|
|
2579
|
-
)
|
|
2580
|
-
});
|
|
2581
|
-
}
|
|
2582
|
-
} else if (message.type === "result") {
|
|
2583
|
-
done();
|
|
2584
|
-
receivedResultMessage = true;
|
|
2585
|
-
this.setSessionId(message.session_id);
|
|
2586
|
-
costUsd = message.total_cost_usd;
|
|
2587
|
-
durationMs = message.duration_ms;
|
|
2588
|
-
modelUsage = message.modelUsage;
|
|
2589
|
-
if ("ttft_ms" in message) {
|
|
2590
|
-
ttftMs = message.ttft_ms;
|
|
2591
|
-
}
|
|
2592
|
-
if ("ttft_stream_ms" in message) {
|
|
2593
|
-
ttftStreamMs = message.ttft_stream_ms;
|
|
2594
|
-
}
|
|
2595
|
-
if ("time_to_request_ms" in message) {
|
|
2596
|
-
timeToRequestMs = message.time_to_request_ms;
|
|
2597
|
-
}
|
|
2598
|
-
if ("warm_spare_claimed" in message) {
|
|
2599
|
-
warmSpareClaimed = message.warm_spare_claimed;
|
|
2600
|
-
}
|
|
2601
|
-
terminalReason = message.terminal_reason;
|
|
2602
|
-
this.mergeResultPermissionDenials(message, metadataTracking);
|
|
2603
|
-
if ("is_error" in message && message.is_error === true) {
|
|
2604
|
-
const resultText = "result" in message && typeof message.result === "string" ? message.result : void 0;
|
|
2605
|
-
const errorsText = "errors" in message && Array.isArray(message.errors) ? message.errors.filter((e) => typeof e === "string").join("; ") : "";
|
|
2606
|
-
const errorMessage = resultText ?? (errorsText || "Claude Code CLI returned an error");
|
|
2607
|
-
throw Object.assign(new Error(errorMessage), {
|
|
2608
|
-
exitCode: 1,
|
|
2609
|
-
errorKind: lastAssistantErrorKind
|
|
2610
|
-
});
|
|
2611
|
-
}
|
|
2612
|
-
if (message.subtype === "error_max_structured_output_retries") {
|
|
2613
|
-
throw new Error(
|
|
2614
|
-
"Failed to generate valid structured output after maximum retries. The model could not produce a response matching the required schema."
|
|
2615
|
-
);
|
|
2616
|
-
}
|
|
2617
|
-
if ("structured_output" in message && message.structured_output !== void 0) {
|
|
2618
|
-
structuredOutput = message.structured_output;
|
|
2619
|
-
this.logger.debug("[claude-code] Received structured output from SDK");
|
|
2620
|
-
}
|
|
2621
|
-
this.logger.info(
|
|
2622
|
-
`[claude-code] Request completed - Session: ${message.session_id}, Cost: $${costUsd?.toFixed(4) ?? "N/A"}, Duration: ${durationMs ?? "N/A"}ms`
|
|
2623
|
-
);
|
|
2624
|
-
if ("usage" in message) {
|
|
2625
|
-
usage = convertClaudeCodeUsage(message.usage);
|
|
2626
|
-
this.logger.debug(
|
|
2627
|
-
`[claude-code] Token usage - Input: ${usage.inputTokens.total}, Output: ${usage.outputTokens.total}`
|
|
2628
|
-
);
|
|
2629
|
-
}
|
|
2630
|
-
const stopReason = "stop_reason" in message ? message.stop_reason : void 0;
|
|
2631
|
-
finishReason = mapClaudeCodeFinishReason(message.subtype, stopReason);
|
|
2632
|
-
this.logger.debug(`[claude-code] Finish reason: ${finishReason.unified}`);
|
|
2633
|
-
const effectivePromptSuggestions = sdkOptions?.promptSuggestions ?? this.settings.promptSuggestions;
|
|
2634
|
-
if (this.settings.onPromptSuggestion && effectivePromptSuggestions !== false) {
|
|
2635
|
-
await this.drainPromptSuggestion(response, this.settings.onPromptSuggestion);
|
|
2636
|
-
}
|
|
2637
|
-
break;
|
|
2638
|
-
} else if (message.type === "system" && message.subtype === "init") {
|
|
2639
|
-
this.logMcpConnectionIssues(message.mcp_servers);
|
|
2640
|
-
this.setSessionId(message.session_id);
|
|
2641
|
-
this.logger.info(`[claude-code] Session initialized: ${message.session_id}`);
|
|
2642
|
-
} else if (message.type === "system") {
|
|
2643
|
-
this.handleSystemMessage(
|
|
2644
|
-
message,
|
|
2645
|
-
metadataTracking,
|
|
2646
|
-
buildRetractionEvictor(evictBuffered)
|
|
2647
|
-
);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
} catch (error) {
|
|
2651
|
-
done();
|
|
2652
|
-
this.logger.debug(
|
|
2653
|
-
`[claude-code] Error during doGenerate: ${error instanceof Error ? error.message : String(error)}`
|
|
2654
|
-
);
|
|
2655
|
-
if (isAbortError(error)) {
|
|
2656
|
-
this.logger.debug("[claude-code] Request aborted by user");
|
|
2657
|
-
throw options.abortSignal?.aborted ? options.abortSignal.reason : error;
|
|
2658
|
-
}
|
|
2659
|
-
if (isClaudeCodeTruncationError(error, text)) {
|
|
2660
|
-
this.logger.warn(
|
|
2661
|
-
`[claude-code] Detected truncated response, returning ${text.length} characters of buffered text`
|
|
2662
|
-
);
|
|
2663
|
-
wasTruncated = true;
|
|
2664
|
-
finishReason = { unified: "length", raw: "truncation" };
|
|
2665
|
-
warnings.push({
|
|
2666
|
-
type: "other",
|
|
2667
|
-
message: CLAUDE_CODE_TRUNCATION_WARNING
|
|
2668
|
-
});
|
|
2669
|
-
} else {
|
|
2670
|
-
throw this.handleClaudeCodeError(error, messagesPrompt, collectedStderr);
|
|
2671
|
-
}
|
|
2672
|
-
} finally {
|
|
2673
|
-
if (options.abortSignal && abortListener) {
|
|
2674
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
2675
|
-
}
|
|
2676
|
-
}
|
|
2677
|
-
if (options.responseFormat?.type === "json" && options.responseFormat.schema !== void 0 && receivedResultMessage && structuredOutput === void 0 && !wasTruncated && finishReason.unified === "stop") {
|
|
2678
|
-
const recoveredJsonText = extractJsonObjectText(joinFinalTurnTextSegments()) ?? extractJsonObjectText(joinTextSegments());
|
|
2679
|
-
if (recoveredJsonText !== void 0) {
|
|
2680
|
-
this.logger.warn(
|
|
2681
|
-
"[claude-code] outputFormat was requested but the CLI returned no structured_output; recovered JSON by parsing the prose response. The schema likely contains constructs the CLI cannot enforce - see the README structured-output limitations."
|
|
2682
|
-
);
|
|
2683
|
-
structuredOutput = JSON.parse(recoveredJsonText);
|
|
2684
|
-
} else {
|
|
2685
|
-
throw createAPICallError({
|
|
2686
|
-
message: MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE,
|
|
2687
|
-
promptExcerpt: messagesPrompt.substring(0, 200),
|
|
2688
|
-
isRetryable: false
|
|
2689
|
-
});
|
|
2690
|
-
}
|
|
2691
|
-
}
|
|
2692
|
-
const thinkingTraces = contentSegments.filter((segment) => segment.kind === "reasoning").map((segment) => segment.text);
|
|
2693
|
-
const contentParts = [];
|
|
2694
|
-
for (const segment of contentSegments) {
|
|
2695
|
-
if (segment.kind === "reasoning") {
|
|
2696
|
-
contentParts.push({ type: "reasoning", text: segment.text });
|
|
2697
|
-
} else if (segment.kind === "text") {
|
|
2698
|
-
if (structuredOutput !== void 0) continue;
|
|
2699
|
-
const last = contentParts[contentParts.length - 1];
|
|
2700
|
-
if (last !== void 0 && last.type === "text") {
|
|
2701
|
-
last.text += segment.text;
|
|
2702
|
-
} else {
|
|
2703
|
-
contentParts.push({ type: "text", text: segment.text });
|
|
2704
|
-
}
|
|
2705
|
-
} else {
|
|
2706
|
-
contentParts.push(segment.part);
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
|
-
if (structuredOutput !== void 0) {
|
|
2710
|
-
contentParts.push({ type: "text", text: JSON.stringify(structuredOutput) });
|
|
2711
|
-
} else if (!contentParts.some((part) => part.type === "text")) {
|
|
2712
|
-
contentParts.push({ type: "text", text: "" });
|
|
2713
|
-
}
|
|
2714
|
-
return {
|
|
2715
|
-
content: contentParts,
|
|
2716
|
-
usage,
|
|
2717
|
-
finishReason,
|
|
2718
|
-
warnings,
|
|
2719
|
-
response: {
|
|
2720
|
-
id: (0, import_provider_utils.generateId)(),
|
|
2721
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
2722
|
-
modelId: this.modelId
|
|
2723
|
-
},
|
|
2724
|
-
request: {
|
|
2725
|
-
body: messagesPrompt
|
|
2726
|
-
},
|
|
2727
|
-
providerMetadata: {
|
|
2728
|
-
"claude-code": {
|
|
2729
|
-
...this.sessionId !== void 0 && { sessionId: this.sessionId },
|
|
2730
|
-
...costUsd !== void 0 && { costUsd },
|
|
2731
|
-
...durationMs !== void 0 && { durationMs },
|
|
2732
|
-
...modelUsage !== void 0 && { modelUsage },
|
|
2733
|
-
...ttftMs !== void 0 && { ttftMs },
|
|
2734
|
-
...ttftStreamMs !== void 0 && { ttftStreamMs },
|
|
2735
|
-
...timeToRequestMs !== void 0 && { timeToRequestMs },
|
|
2736
|
-
...warmSpareClaimed !== void 0 && { warmSpareClaimed },
|
|
2737
|
-
...terminalReason !== void 0 && { terminalReason },
|
|
2738
|
-
...metadataTracking.apiRetries > 0 && { apiRetries: metadataTracking.apiRetries },
|
|
2739
|
-
...metadataTracking.permissionDenials.length > 0 && {
|
|
2740
|
-
permissionDenials: metadataTracking.permissionDenials
|
|
2741
|
-
},
|
|
2742
|
-
...metadataTracking.mirrorErrors.length > 0 && {
|
|
2743
|
-
mirrorErrors: metadataTracking.mirrorErrors
|
|
2744
|
-
},
|
|
2745
|
-
...metadataTracking.estimatedThinkingTokens > 0 && {
|
|
2746
|
-
estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
|
|
2747
|
-
},
|
|
2748
|
-
...wasTruncated && { truncated: true },
|
|
2749
|
-
...thinkingTraces.length > 0 && { thinkingTraces }
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
};
|
|
2753
|
-
}
|
|
2754
|
-
async doStream(options) {
|
|
2755
|
-
this.logger.debug(`[claude-code] Starting doStream request with model: ${this.modelId}`);
|
|
2756
|
-
this.logger.debug(`[claude-code] Response format: ${options.responseFormat?.type ?? "none"}`);
|
|
2757
|
-
const {
|
|
2758
|
-
messagesPrompt,
|
|
2759
|
-
warnings: messageWarnings,
|
|
2760
|
-
streamingContentParts,
|
|
2761
|
-
hasImageParts
|
|
2762
|
-
} = convertToClaudeCodeMessages(options.prompt);
|
|
2763
|
-
this.logger.debug(
|
|
2764
|
-
`[claude-code] Converted ${options.prompt.length} messages for streaming, hasImageParts: ${hasImageParts}`
|
|
2765
|
-
);
|
|
2766
|
-
const abortController = new AbortController();
|
|
2767
|
-
let abortListener;
|
|
2768
|
-
if (options.abortSignal?.aborted) {
|
|
2769
|
-
abortController.abort(options.abortSignal.reason);
|
|
2770
|
-
}
|
|
2771
|
-
let collectedStderr = "";
|
|
2772
|
-
const stderrCollector = (data) => {
|
|
2773
|
-
collectedStderr += data;
|
|
2774
|
-
};
|
|
2775
|
-
const sdkOptions = this.getSanitizedSdkOptions();
|
|
2776
|
-
const effectiveResume = this.getEffectiveResume(sdkOptions);
|
|
2777
|
-
const queryOptions = this.createQueryOptions(
|
|
2778
|
-
abortController,
|
|
2779
|
-
options.responseFormat,
|
|
2780
|
-
stderrCollector,
|
|
2781
|
-
sdkOptions,
|
|
2782
|
-
effectiveResume
|
|
2783
|
-
);
|
|
2784
|
-
if (options.abortSignal && !options.abortSignal.aborted) {
|
|
2785
|
-
abortListener = () => abortController.abort(options.abortSignal?.reason);
|
|
2786
|
-
options.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
2787
|
-
}
|
|
2788
|
-
if (queryOptions.includePartialMessages === void 0) {
|
|
2789
|
-
queryOptions.includePartialMessages = true;
|
|
2790
|
-
}
|
|
2791
|
-
const warnings = this.generateAllWarnings(options, messagesPrompt);
|
|
2792
|
-
if (messageWarnings) {
|
|
2793
|
-
messageWarnings.forEach((warning) => {
|
|
2794
|
-
warnings.push({
|
|
2795
|
-
type: "other",
|
|
2796
|
-
message: warning
|
|
2797
|
-
});
|
|
2798
|
-
});
|
|
2799
|
-
}
|
|
2800
|
-
const modeSetting = this.settings.streamingInput ?? "auto";
|
|
2801
|
-
const effectiveCanUseTool = sdkOptions?.canUseTool ?? this.settings.canUseTool;
|
|
2802
|
-
const effectivePermissionPromptToolName = sdkOptions?.permissionPromptToolName ?? this.settings.permissionPromptToolName;
|
|
2803
|
-
const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!effectiveCanUseTool;
|
|
2804
|
-
if (!wantsStreamInput && hasImageParts) {
|
|
2805
|
-
warnings.push({
|
|
2806
|
-
type: "other",
|
|
2807
|
-
message: STREAMING_FEATURE_WARNING
|
|
2808
|
-
});
|
|
2809
|
-
}
|
|
2810
|
-
const stream = new ReadableStream({
|
|
2811
|
-
start: async (controller) => {
|
|
2812
|
-
let done = () => {
|
|
2813
|
-
};
|
|
2814
|
-
const outputStreamEnded = new Promise((resolve) => {
|
|
2815
|
-
done = () => resolve(void 0);
|
|
2816
|
-
});
|
|
2817
|
-
const toolStates = /* @__PURE__ */ new Map();
|
|
2818
|
-
const retractedStreamToolIds = /* @__PURE__ */ new Set();
|
|
2819
|
-
const activeTaskTools = /* @__PURE__ */ new Map();
|
|
2820
|
-
const getFallbackParentId = () => {
|
|
2821
|
-
if (activeTaskTools.size === 1) {
|
|
2822
|
-
return activeTaskTools.keys().next().value ?? null;
|
|
2823
|
-
}
|
|
2824
|
-
return null;
|
|
2825
|
-
};
|
|
2826
|
-
const streamWarnings = [];
|
|
2827
|
-
const closeToolInput = (toolId, state) => {
|
|
2828
|
-
if (!state.inputClosed && state.inputStarted) {
|
|
2829
|
-
controller.enqueue({
|
|
2830
|
-
type: "tool-input-end",
|
|
2831
|
-
id: toolId
|
|
2832
|
-
});
|
|
2833
|
-
state.inputClosed = true;
|
|
2834
|
-
}
|
|
2835
|
-
};
|
|
2836
|
-
const emitToolCall = (toolId, state) => {
|
|
2837
|
-
if (state.callEmitted) {
|
|
2838
|
-
return;
|
|
2839
|
-
}
|
|
2840
|
-
closeToolInput(toolId, state);
|
|
2841
|
-
controller.enqueue(
|
|
2842
|
-
this.buildToolCallPart(
|
|
2843
|
-
toolId,
|
|
2844
|
-
state.name,
|
|
2845
|
-
state.lastSerializedInput ?? "",
|
|
2846
|
-
state.parentToolCallId
|
|
2847
|
-
)
|
|
2848
|
-
);
|
|
2849
|
-
state.callEmitted = true;
|
|
2850
|
-
};
|
|
2851
|
-
const finalizeToolCalls = () => {
|
|
2852
|
-
for (const [toolId, state] of toolStates) {
|
|
2853
|
-
emitToolCall(toolId, state);
|
|
2854
|
-
}
|
|
2855
|
-
toolStates.clear();
|
|
2856
|
-
};
|
|
2857
|
-
let usage = createEmptyUsage();
|
|
2858
|
-
let accumulatedText = "";
|
|
2859
|
-
const textSegments = [];
|
|
2860
|
-
let textPartId;
|
|
2861
|
-
let streamedTextLength = 0;
|
|
2862
|
-
let emittedTextSinceLastAssistant = "";
|
|
2863
|
-
let hasReceivedStreamEvents = false;
|
|
2864
|
-
let hasStreamedJson = false;
|
|
2865
|
-
let lastAssistantErrorKind;
|
|
2866
|
-
const metadataTracking = {
|
|
2867
|
-
apiRetries: 0,
|
|
2868
|
-
permissionDenials: [],
|
|
2869
|
-
mirrorErrors: [],
|
|
2870
|
-
estimatedThinkingTokens: 0
|
|
2871
|
-
};
|
|
2872
|
-
const toolBlocksByIndex = /* @__PURE__ */ new Map();
|
|
2873
|
-
const toolInputAccumulators = /* @__PURE__ */ new Map();
|
|
2874
|
-
const textBlocksByIndex = /* @__PURE__ */ new Map();
|
|
2875
|
-
let textStreamedViaContentBlock = false;
|
|
2876
|
-
const reasoningBlocksByIndex = /* @__PURE__ */ new Map();
|
|
2877
|
-
let currentReasoningPartId;
|
|
2878
|
-
const evictLive = (retracted) => {
|
|
2879
|
-
if (retracted.size === 0) return;
|
|
2880
|
-
for (let i = textSegments.length - 1; i >= 0; i--) {
|
|
2881
|
-
const segmentUuid = textSegments[i]?.uuid;
|
|
2882
|
-
if (segmentUuid !== void 0 && retracted.has(segmentUuid)) {
|
|
2883
|
-
textSegments.splice(i, 1);
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
accumulatedText = textSegments.map((segment) => segment.text).join("");
|
|
2887
|
-
const retractedToolCallIds = computeRetractedToolCallIds(
|
|
2888
|
-
retracted,
|
|
2889
|
-
[...toolStates].map(([toolId, state]) => ({
|
|
2890
|
-
toolCallId: toolId,
|
|
2891
|
-
uuid: state.messageUuid
|
|
2892
|
-
}))
|
|
2893
|
-
);
|
|
2894
|
-
for (const toolId of retractedToolCallIds) {
|
|
2895
|
-
const state = toolStates.get(toolId);
|
|
2896
|
-
if (!state) continue;
|
|
2897
|
-
activeTaskTools.delete(toolId);
|
|
2898
|
-
if (!state.callEmitted) {
|
|
2899
|
-
closeToolInput(toolId, state);
|
|
2900
|
-
toolStates.delete(toolId);
|
|
2901
|
-
retractedStreamToolIds.add(toolId);
|
|
2902
|
-
toolInputAccumulators.delete(toolId);
|
|
2903
|
-
for (const [blockIndex, mappedId] of toolBlocksByIndex) {
|
|
2904
|
-
if (mappedId === toolId) {
|
|
2905
|
-
toolBlocksByIndex.delete(blockIndex);
|
|
2906
|
-
}
|
|
2907
|
-
}
|
|
2908
|
-
this.logger.debug(
|
|
2909
|
-
`[claude-code] Retracted pending tool call from superseded message - ID: ${toolId}`
|
|
2910
|
-
);
|
|
2911
|
-
}
|
|
2912
|
-
}
|
|
2913
|
-
};
|
|
2914
|
-
try {
|
|
2915
|
-
controller.enqueue({ type: "stream-start", warnings });
|
|
2916
|
-
if (effectiveCanUseTool && effectivePermissionPromptToolName) {
|
|
2917
|
-
throw new Error(
|
|
2918
|
-
"canUseTool requires streamingInput mode ('auto' or 'always') and cannot be used with permissionPromptToolName (SDK constraint). Set streamingInput: 'auto' (or 'always') and remove permissionPromptToolName, or remove canUseTool."
|
|
2919
|
-
);
|
|
2920
|
-
}
|
|
2921
|
-
const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
|
|
2922
|
-
messagesPrompt,
|
|
2923
|
-
outputStreamEnded,
|
|
2924
|
-
effectiveResume,
|
|
2925
|
-
streamingContentParts,
|
|
2926
|
-
this.settings.onStreamStart
|
|
2927
|
-
) : messagesPrompt;
|
|
2928
|
-
this.logger.debug(
|
|
2929
|
-
`[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
|
|
2930
|
-
);
|
|
2931
|
-
const response = (0, import_claude_agent_sdk.query)({
|
|
2932
|
-
prompt: sdkPrompt,
|
|
2933
|
-
options: queryOptions
|
|
2934
|
-
});
|
|
2935
|
-
this.settings.onQueryCreated?.(response);
|
|
2936
|
-
for await (const message of response) {
|
|
2937
|
-
this.logger.debug(`[claude-code] Stream received message type: ${message.type}`);
|
|
2938
|
-
if (message.type === "stream_event") {
|
|
2939
|
-
const streamEvent = message;
|
|
2940
|
-
const event = streamEvent.event;
|
|
2941
|
-
if (event.type === "content_block_delta" && event.delta.type === "text_delta" && "text" in event.delta && event.delta.text) {
|
|
2942
|
-
const deltaText = event.delta.text;
|
|
2943
|
-
hasReceivedStreamEvents = true;
|
|
2944
|
-
if (options.responseFormat?.type === "json") {
|
|
2945
|
-
accumulatedText += deltaText;
|
|
2946
|
-
streamedTextLength += deltaText.length;
|
|
2947
|
-
continue;
|
|
2948
|
-
}
|
|
2949
|
-
if (!textPartId) {
|
|
2950
|
-
textPartId = (0, import_provider_utils.generateId)();
|
|
2951
|
-
controller.enqueue({
|
|
2952
|
-
type: "text-start",
|
|
2953
|
-
id: textPartId
|
|
2954
|
-
});
|
|
2955
|
-
}
|
|
2956
|
-
controller.enqueue({
|
|
2957
|
-
type: "text-delta",
|
|
2958
|
-
id: textPartId,
|
|
2959
|
-
delta: deltaText
|
|
2960
|
-
});
|
|
2961
|
-
accumulatedText += deltaText;
|
|
2962
|
-
streamedTextLength += deltaText.length;
|
|
2963
|
-
emittedTextSinceLastAssistant += deltaText;
|
|
2964
|
-
}
|
|
2965
|
-
if (event.type === "content_block_delta" && event.delta.type === "input_json_delta" && "partial_json" in event.delta && event.delta.partial_json) {
|
|
2966
|
-
const jsonDelta = event.delta.partial_json;
|
|
2967
|
-
hasReceivedStreamEvents = true;
|
|
2968
|
-
const blockIndex = "index" in event ? event.index : -1;
|
|
2969
|
-
if (options.responseFormat?.type === "json") {
|
|
2970
|
-
if (!textPartId) {
|
|
2971
|
-
textPartId = (0, import_provider_utils.generateId)();
|
|
2972
|
-
controller.enqueue({
|
|
2973
|
-
type: "text-start",
|
|
2974
|
-
id: textPartId
|
|
2975
|
-
});
|
|
2976
|
-
}
|
|
2977
|
-
controller.enqueue({
|
|
2978
|
-
type: "text-delta",
|
|
2979
|
-
id: textPartId,
|
|
2980
|
-
delta: jsonDelta
|
|
2981
|
-
});
|
|
2982
|
-
accumulatedText += jsonDelta;
|
|
2983
|
-
streamedTextLength += jsonDelta.length;
|
|
2984
|
-
hasStreamedJson = true;
|
|
2985
|
-
continue;
|
|
2986
|
-
}
|
|
2987
|
-
const toolId = toolBlocksByIndex.get(blockIndex);
|
|
2988
|
-
if (toolId) {
|
|
2989
|
-
const accumulated = (toolInputAccumulators.get(toolId) ?? "") + jsonDelta;
|
|
2990
|
-
toolInputAccumulators.set(toolId, accumulated);
|
|
2991
|
-
controller.enqueue({
|
|
2992
|
-
type: "tool-input-delta",
|
|
2993
|
-
id: toolId,
|
|
2994
|
-
delta: jsonDelta
|
|
2995
|
-
});
|
|
2996
|
-
continue;
|
|
2997
|
-
}
|
|
2998
|
-
}
|
|
2999
|
-
if (event.type === "content_block_start" && "content_block" in event && event.content_block?.type === "tool_use") {
|
|
3000
|
-
const blockIndex = "index" in event ? event.index : -1;
|
|
3001
|
-
const toolBlock = event.content_block;
|
|
3002
|
-
const toolId = typeof toolBlock.id === "string" && toolBlock.id.length > 0 ? toolBlock.id : (0, import_provider_utils.generateId)();
|
|
3003
|
-
const toolName = typeof toolBlock.name === "string" && toolBlock.name.length > 0 ? toolBlock.name : _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
3004
|
-
hasReceivedStreamEvents = true;
|
|
3005
|
-
if (textPartId) {
|
|
3006
|
-
const closedTextId = textPartId;
|
|
3007
|
-
controller.enqueue({
|
|
3008
|
-
type: "text-end",
|
|
3009
|
-
id: closedTextId
|
|
3010
|
-
});
|
|
3011
|
-
textPartId = void 0;
|
|
3012
|
-
for (const [idx, blockTextId] of textBlocksByIndex) {
|
|
3013
|
-
if (blockTextId === closedTextId) {
|
|
3014
|
-
textBlocksByIndex.delete(idx);
|
|
3015
|
-
break;
|
|
3016
|
-
}
|
|
3017
|
-
}
|
|
3018
|
-
}
|
|
3019
|
-
toolBlocksByIndex.set(blockIndex, toolId);
|
|
3020
|
-
toolInputAccumulators.set(toolId, "");
|
|
3021
|
-
let state = toolStates.get(toolId);
|
|
3022
|
-
if (!state) {
|
|
3023
|
-
const partialParentId = message.parent_tool_use_id;
|
|
3024
|
-
const currentParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(partialParentId, void 0, getFallbackParentId);
|
|
3025
|
-
const envelopeUuid = message.uuid;
|
|
3026
|
-
state = {
|
|
3027
|
-
name: toolName,
|
|
3028
|
-
inputStarted: false,
|
|
3029
|
-
inputClosed: false,
|
|
3030
|
-
callEmitted: false,
|
|
3031
|
-
parentToolCallId: currentParentId,
|
|
3032
|
-
...typeof envelopeUuid === "string" && { messageUuid: envelopeUuid }
|
|
3033
|
-
};
|
|
3034
|
-
toolStates.set(toolId, state);
|
|
3035
|
-
}
|
|
3036
|
-
if (!state.inputStarted) {
|
|
3037
|
-
this.logger.debug(
|
|
3038
|
-
`[claude-code] Tool input started (content_block) - Tool: ${toolName}, ID: ${toolId}, parent: ${state.parentToolCallId}`
|
|
3039
|
-
);
|
|
3040
|
-
controller.enqueue({
|
|
3041
|
-
type: "tool-input-start",
|
|
3042
|
-
id: toolId,
|
|
3043
|
-
toolName,
|
|
3044
|
-
providerExecuted: true,
|
|
3045
|
-
dynamic: true,
|
|
3046
|
-
providerMetadata: {
|
|
3047
|
-
"claude-code": {
|
|
3048
|
-
parentToolCallId: state.parentToolCallId ?? null
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
});
|
|
3052
|
-
if (isSubagentToolName(toolName)) {
|
|
3053
|
-
activeTaskTools.set(toolId, { startTime: Date.now() });
|
|
3054
|
-
}
|
|
3055
|
-
state.inputStarted = true;
|
|
3056
|
-
}
|
|
3057
|
-
continue;
|
|
3058
|
-
}
|
|
3059
|
-
if (event.type === "content_block_start" && "content_block" in event && event.content_block?.type === "text") {
|
|
3060
|
-
const blockIndex = "index" in event ? event.index : -1;
|
|
3061
|
-
hasReceivedStreamEvents = true;
|
|
3062
|
-
const partId = (0, import_provider_utils.generateId)();
|
|
3063
|
-
textBlocksByIndex.set(blockIndex, partId);
|
|
3064
|
-
textPartId = partId;
|
|
3065
|
-
this.logger.debug(
|
|
3066
|
-
`[claude-code] Text content block started - Index: ${blockIndex}, ID: ${partId}`
|
|
3067
|
-
);
|
|
3068
|
-
controller.enqueue({
|
|
3069
|
-
type: "text-start",
|
|
3070
|
-
id: partId
|
|
3071
|
-
});
|
|
3072
|
-
textStreamedViaContentBlock = true;
|
|
3073
|
-
continue;
|
|
3074
|
-
}
|
|
3075
|
-
if (event.type === "content_block_start" && "content_block" in event && event.content_block?.type === "thinking") {
|
|
3076
|
-
const blockIndex = "index" in event ? event.index : -1;
|
|
3077
|
-
hasReceivedStreamEvents = true;
|
|
3078
|
-
if (textPartId) {
|
|
3079
|
-
const closedTextId = textPartId;
|
|
3080
|
-
controller.enqueue({
|
|
3081
|
-
type: "text-end",
|
|
3082
|
-
id: closedTextId
|
|
3083
|
-
});
|
|
3084
|
-
textPartId = void 0;
|
|
3085
|
-
for (const [idx, blockTextId] of textBlocksByIndex) {
|
|
3086
|
-
if (blockTextId === closedTextId) {
|
|
3087
|
-
textBlocksByIndex.delete(idx);
|
|
3088
|
-
break;
|
|
3089
|
-
}
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
const reasoningPartId = (0, import_provider_utils.generateId)();
|
|
3093
|
-
reasoningBlocksByIndex.set(blockIndex, reasoningPartId);
|
|
3094
|
-
currentReasoningPartId = reasoningPartId;
|
|
3095
|
-
this.logger.debug(
|
|
3096
|
-
`[claude-code] Reasoning started (content_block) - ID: ${reasoningPartId}`
|
|
3097
|
-
);
|
|
3098
|
-
controller.enqueue({
|
|
3099
|
-
type: "reasoning-start",
|
|
3100
|
-
id: reasoningPartId
|
|
3101
|
-
});
|
|
3102
|
-
continue;
|
|
3103
|
-
}
|
|
3104
|
-
if (event.type === "content_block_delta" && event.delta.type === "thinking_delta" && "thinking" in event.delta && event.delta.thinking) {
|
|
3105
|
-
const blockIndex = "index" in event ? event.index : -1;
|
|
3106
|
-
const reasoningPartId = reasoningBlocksByIndex.get(blockIndex) ?? currentReasoningPartId;
|
|
3107
|
-
hasReceivedStreamEvents = true;
|
|
3108
|
-
if (reasoningPartId) {
|
|
3109
|
-
controller.enqueue({
|
|
3110
|
-
type: "reasoning-delta",
|
|
3111
|
-
id: reasoningPartId,
|
|
3112
|
-
delta: event.delta.thinking
|
|
3113
|
-
});
|
|
3114
|
-
}
|
|
3115
|
-
continue;
|
|
3116
|
-
}
|
|
3117
|
-
if (event.type === "content_block_stop") {
|
|
3118
|
-
const blockIndex = "index" in event ? event.index : -1;
|
|
3119
|
-
hasReceivedStreamEvents = true;
|
|
3120
|
-
const toolId = toolBlocksByIndex.get(blockIndex);
|
|
3121
|
-
if (toolId) {
|
|
3122
|
-
const state = toolStates.get(toolId);
|
|
3123
|
-
if (state && !state.inputClosed) {
|
|
3124
|
-
const accumulatedInput = toolInputAccumulators.get(toolId) ?? "";
|
|
3125
|
-
this.logger.debug(
|
|
3126
|
-
`[claude-code] Tool content block stopped - Index: ${blockIndex}, Tool: ${state.name}, ID: ${toolId}`
|
|
3127
|
-
);
|
|
3128
|
-
controller.enqueue({
|
|
3129
|
-
type: "tool-input-end",
|
|
3130
|
-
id: toolId
|
|
3131
|
-
});
|
|
3132
|
-
state.inputClosed = true;
|
|
3133
|
-
const effectiveInput = accumulatedInput || state.lastSerializedInput || "";
|
|
3134
|
-
state.lastSerializedInput = effectiveInput;
|
|
3135
|
-
if (!state.callEmitted) {
|
|
3136
|
-
controller.enqueue(
|
|
3137
|
-
this.buildToolCallPart(
|
|
3138
|
-
toolId,
|
|
3139
|
-
state.name,
|
|
3140
|
-
effectiveInput,
|
|
3141
|
-
state.parentToolCallId
|
|
3142
|
-
)
|
|
3143
|
-
);
|
|
3144
|
-
state.callEmitted = true;
|
|
3145
|
-
}
|
|
3146
|
-
}
|
|
3147
|
-
toolBlocksByIndex.delete(blockIndex);
|
|
3148
|
-
toolInputAccumulators.delete(toolId);
|
|
3149
|
-
continue;
|
|
3150
|
-
}
|
|
3151
|
-
const textId = textBlocksByIndex.get(blockIndex);
|
|
3152
|
-
if (textId) {
|
|
3153
|
-
this.logger.debug(
|
|
3154
|
-
`[claude-code] Text content block stopped - Index: ${blockIndex}, ID: ${textId}`
|
|
3155
|
-
);
|
|
3156
|
-
controller.enqueue({
|
|
3157
|
-
type: "text-end",
|
|
3158
|
-
id: textId
|
|
3159
|
-
});
|
|
3160
|
-
textBlocksByIndex.delete(blockIndex);
|
|
3161
|
-
if (textPartId === textId) {
|
|
3162
|
-
textPartId = void 0;
|
|
3163
|
-
}
|
|
3164
|
-
continue;
|
|
3165
|
-
}
|
|
3166
|
-
const reasoningPartId = reasoningBlocksByIndex.get(blockIndex);
|
|
3167
|
-
if (reasoningPartId) {
|
|
3168
|
-
this.logger.debug(
|
|
3169
|
-
`[claude-code] Reasoning ended (content_block) - ID: ${reasoningPartId}`
|
|
3170
|
-
);
|
|
3171
|
-
controller.enqueue({
|
|
3172
|
-
type: "reasoning-end",
|
|
3173
|
-
id: reasoningPartId
|
|
3174
|
-
});
|
|
3175
|
-
reasoningBlocksByIndex.delete(blockIndex);
|
|
3176
|
-
if (currentReasoningPartId === reasoningPartId) {
|
|
3177
|
-
currentReasoningPartId = void 0;
|
|
3178
|
-
}
|
|
3179
|
-
continue;
|
|
3180
|
-
}
|
|
3181
|
-
}
|
|
3182
|
-
continue;
|
|
3183
|
-
}
|
|
3184
|
-
if (message.type === "assistant") {
|
|
3185
|
-
if (typeof message.error === "string") {
|
|
3186
|
-
lastAssistantErrorKind = message.error;
|
|
3187
|
-
}
|
|
3188
|
-
const supersedesPriorMessages = applySupersede(message, evictLive, this.logger);
|
|
3189
|
-
if (!message.message?.content) {
|
|
3190
|
-
this.logger.warn(
|
|
3191
|
-
`[claude-code] Unexpected assistant message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
|
|
3192
|
-
);
|
|
3193
|
-
continue;
|
|
3194
|
-
}
|
|
3195
|
-
const sdkParentToolUseId = message.parent_tool_use_id;
|
|
3196
|
-
const content = message.message.content;
|
|
3197
|
-
const tools = this.extractToolUses(content);
|
|
3198
|
-
if (textPartId && tools.length > 0) {
|
|
3199
|
-
const closedTextId = textPartId;
|
|
3200
|
-
controller.enqueue({
|
|
3201
|
-
type: "text-end",
|
|
3202
|
-
id: closedTextId
|
|
3203
|
-
});
|
|
3204
|
-
textPartId = void 0;
|
|
3205
|
-
for (const [idx, blockTextId] of textBlocksByIndex) {
|
|
3206
|
-
if (blockTextId === closedTextId) {
|
|
3207
|
-
textBlocksByIndex.delete(idx);
|
|
3208
|
-
break;
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
|
-
}
|
|
3212
|
-
for (const tool3 of tools) {
|
|
3213
|
-
const toolId = tool3.id;
|
|
3214
|
-
let state = toolStates.get(toolId);
|
|
3215
|
-
if (!state) {
|
|
3216
|
-
const currentParentId = isSubagentToolName(tool3.name) ? null : resolveToolParentId(
|
|
3217
|
-
sdkParentToolUseId,
|
|
3218
|
-
tool3.parentToolUseId,
|
|
3219
|
-
getFallbackParentId
|
|
3220
|
-
);
|
|
3221
|
-
state = {
|
|
3222
|
-
name: tool3.name,
|
|
3223
|
-
inputStarted: false,
|
|
3224
|
-
inputClosed: false,
|
|
3225
|
-
callEmitted: false,
|
|
3226
|
-
parentToolCallId: currentParentId,
|
|
3227
|
-
...typeof message.uuid === "string" && { messageUuid: message.uuid }
|
|
3228
|
-
};
|
|
3229
|
-
toolStates.set(toolId, state);
|
|
3230
|
-
this.logger.debug(
|
|
3231
|
-
`[claude-code] New tool use detected - Tool: ${tool3.name}, ID: ${toolId}, SDK parent: ${sdkParentToolUseId}, resolved parent: ${currentParentId}`
|
|
3232
|
-
);
|
|
3233
|
-
} else if (!state.parentToolCallId && sdkParentToolUseId && !isSubagentToolName(tool3.name)) {
|
|
3234
|
-
state.parentToolCallId = sdkParentToolUseId;
|
|
3235
|
-
this.logger.debug(
|
|
3236
|
-
`[claude-code] Retroactive parent context - Tool: ${tool3.name}, ID: ${toolId}, parent: ${sdkParentToolUseId}`
|
|
3237
|
-
);
|
|
3238
|
-
}
|
|
3239
|
-
state.name = tool3.name;
|
|
3240
|
-
if (!state.inputStarted) {
|
|
3241
|
-
this.logger.debug(
|
|
3242
|
-
`[claude-code] Tool input started - Tool: ${tool3.name}, ID: ${toolId}`
|
|
3243
|
-
);
|
|
3244
|
-
controller.enqueue({
|
|
3245
|
-
type: "tool-input-start",
|
|
3246
|
-
id: toolId,
|
|
3247
|
-
toolName: tool3.name,
|
|
3248
|
-
providerExecuted: true,
|
|
3249
|
-
dynamic: true,
|
|
3250
|
-
// V3 field: indicates tool is provider-defined
|
|
3251
|
-
providerMetadata: {
|
|
3252
|
-
"claude-code": {
|
|
3253
|
-
parentToolCallId: state.parentToolCallId ?? null
|
|
3254
|
-
}
|
|
3255
|
-
}
|
|
3256
|
-
});
|
|
3257
|
-
if (isSubagentToolName(tool3.name)) {
|
|
3258
|
-
activeTaskTools.set(toolId, { startTime: Date.now() });
|
|
3259
|
-
}
|
|
3260
|
-
state.inputStarted = true;
|
|
3261
|
-
}
|
|
3262
|
-
const serializedInput = this.serializeToolInput(tool3.input);
|
|
3263
|
-
if (serializedInput) {
|
|
3264
|
-
let deltaPayload = "";
|
|
3265
|
-
if (state.lastSerializedInput === void 0) {
|
|
3266
|
-
if (serializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE) {
|
|
3267
|
-
deltaPayload = serializedInput;
|
|
3268
|
-
}
|
|
3269
|
-
} else if (serializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE && state.lastSerializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE && serializedInput.startsWith(state.lastSerializedInput)) {
|
|
3270
|
-
deltaPayload = serializedInput.slice(state.lastSerializedInput.length);
|
|
3271
|
-
} else if (serializedInput !== state.lastSerializedInput) {
|
|
3272
|
-
deltaPayload = "";
|
|
3273
|
-
}
|
|
3274
|
-
if (deltaPayload) {
|
|
3275
|
-
controller.enqueue({
|
|
3276
|
-
type: "tool-input-delta",
|
|
3277
|
-
id: toolId,
|
|
3278
|
-
delta: deltaPayload
|
|
3279
|
-
});
|
|
3280
|
-
}
|
|
3281
|
-
state.lastSerializedInput = serializedInput;
|
|
3282
|
-
}
|
|
3283
|
-
}
|
|
3284
|
-
const text = content.map((c) => c.type === "text" ? c.text : "").join("");
|
|
3285
|
-
if (text) {
|
|
3286
|
-
if (supersedesPriorMessages) {
|
|
3287
|
-
textSegments.push({
|
|
3288
|
-
...typeof message.uuid === "string" && { uuid: message.uuid },
|
|
3289
|
-
text
|
|
3290
|
-
});
|
|
3291
|
-
accumulatedText = textSegments.map((segment) => segment.text).join("");
|
|
3292
|
-
if (emittedTextSinceLastAssistant === text) {
|
|
3293
|
-
streamedTextLength = Math.max(streamedTextLength, text.length);
|
|
3294
|
-
this.logger.debug(
|
|
3295
|
-
"[claude-code] Skipping text emission for superseding assistant message (replacement already streamed)"
|
|
3296
|
-
);
|
|
3297
|
-
} else if (emittedTextSinceLastAssistant.length > 0 && text.startsWith(emittedTextSinceLastAssistant)) {
|
|
3298
|
-
if (options.responseFormat?.type !== "json") {
|
|
3299
|
-
const suffix = text.slice(emittedTextSinceLastAssistant.length);
|
|
3300
|
-
if (suffix) {
|
|
3301
|
-
if (!textPartId) {
|
|
3302
|
-
textPartId = (0, import_provider_utils.generateId)();
|
|
3303
|
-
controller.enqueue({ type: "text-start", id: textPartId });
|
|
3304
|
-
}
|
|
3305
|
-
controller.enqueue({
|
|
3306
|
-
type: "text-delta",
|
|
3307
|
-
id: textPartId,
|
|
3308
|
-
delta: suffix
|
|
3309
|
-
});
|
|
3310
|
-
emittedTextSinceLastAssistant = text;
|
|
3311
|
-
}
|
|
3312
|
-
}
|
|
3313
|
-
streamedTextLength = Math.max(streamedTextLength, text.length);
|
|
3314
|
-
this.logger.debug(
|
|
3315
|
-
"[claude-code] Emitted unstreamed suffix of superseding assistant message"
|
|
3316
|
-
);
|
|
3317
|
-
} else if (options.responseFormat?.type !== "json") {
|
|
3318
|
-
if (textPartId) {
|
|
3319
|
-
const closedTextId = textPartId;
|
|
3320
|
-
controller.enqueue({
|
|
3321
|
-
type: "text-end",
|
|
3322
|
-
id: closedTextId
|
|
3323
|
-
});
|
|
3324
|
-
textPartId = void 0;
|
|
3325
|
-
for (const [idx, blockTextId] of textBlocksByIndex) {
|
|
3326
|
-
if (blockTextId === closedTextId) {
|
|
3327
|
-
textBlocksByIndex.delete(idx);
|
|
3328
|
-
break;
|
|
3329
|
-
}
|
|
3330
|
-
}
|
|
3331
|
-
}
|
|
3332
|
-
textPartId = (0, import_provider_utils.generateId)();
|
|
3333
|
-
controller.enqueue({
|
|
3334
|
-
type: "text-start",
|
|
3335
|
-
id: textPartId
|
|
3336
|
-
});
|
|
3337
|
-
controller.enqueue({
|
|
3338
|
-
type: "text-delta",
|
|
3339
|
-
id: textPartId,
|
|
3340
|
-
delta: text
|
|
3341
|
-
});
|
|
3342
|
-
streamedTextLength = Math.max(streamedTextLength, text.length);
|
|
3343
|
-
this.logger.debug(
|
|
3344
|
-
"[claude-code] Emitted superseding assistant message as a new text part (canonical replacement)"
|
|
3345
|
-
);
|
|
3346
|
-
}
|
|
3347
|
-
} else if (hasReceivedStreamEvents) {
|
|
3348
|
-
const newTextStart = streamedTextLength;
|
|
3349
|
-
const deltaText = text.length > newTextStart ? text.slice(newTextStart) : "";
|
|
3350
|
-
accumulatedText = text;
|
|
3351
|
-
textSegments.length = 0;
|
|
3352
|
-
textSegments.push({
|
|
3353
|
-
...typeof message.uuid === "string" && { uuid: message.uuid },
|
|
3354
|
-
text
|
|
3355
|
-
});
|
|
3356
|
-
if (options.responseFormat?.type !== "json" && deltaText) {
|
|
3357
|
-
if (!textPartId) {
|
|
3358
|
-
textPartId = (0, import_provider_utils.generateId)();
|
|
3359
|
-
controller.enqueue({
|
|
3360
|
-
type: "text-start",
|
|
3361
|
-
id: textPartId
|
|
3362
|
-
});
|
|
3363
|
-
}
|
|
3364
|
-
controller.enqueue({
|
|
3365
|
-
type: "text-delta",
|
|
3366
|
-
id: textPartId,
|
|
3367
|
-
delta: deltaText
|
|
3368
|
-
});
|
|
3369
|
-
}
|
|
3370
|
-
streamedTextLength = text.length;
|
|
3371
|
-
} else {
|
|
3372
|
-
accumulatedText += text;
|
|
3373
|
-
textSegments.push({
|
|
3374
|
-
...typeof message.uuid === "string" && { uuid: message.uuid },
|
|
3375
|
-
text
|
|
3376
|
-
});
|
|
3377
|
-
if (options.responseFormat?.type !== "json") {
|
|
3378
|
-
if (!textPartId) {
|
|
3379
|
-
textPartId = (0, import_provider_utils.generateId)();
|
|
3380
|
-
controller.enqueue({
|
|
3381
|
-
type: "text-start",
|
|
3382
|
-
id: textPartId
|
|
3383
|
-
});
|
|
3384
|
-
}
|
|
3385
|
-
controller.enqueue({
|
|
3386
|
-
type: "text-delta",
|
|
3387
|
-
id: textPartId,
|
|
3388
|
-
delta: text
|
|
3389
|
-
});
|
|
3390
|
-
}
|
|
3391
|
-
}
|
|
3392
|
-
}
|
|
3393
|
-
emittedTextSinceLastAssistant = "";
|
|
3394
|
-
} else if (message.type === "user") {
|
|
3395
|
-
if (!message.message?.content) {
|
|
3396
|
-
this.logger.warn(
|
|
3397
|
-
`[claude-code] Unexpected user message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
|
|
3398
|
-
);
|
|
3399
|
-
continue;
|
|
3400
|
-
}
|
|
3401
|
-
if (textPartId) {
|
|
3402
|
-
const closedTextId = textPartId;
|
|
3403
|
-
controller.enqueue({
|
|
3404
|
-
type: "text-end",
|
|
3405
|
-
id: closedTextId
|
|
3406
|
-
});
|
|
3407
|
-
textPartId = void 0;
|
|
3408
|
-
for (const [blockIndex, blockTextId] of textBlocksByIndex) {
|
|
3409
|
-
if (blockTextId === closedTextId) {
|
|
3410
|
-
textBlocksByIndex.delete(blockIndex);
|
|
3411
|
-
break;
|
|
3412
|
-
}
|
|
3413
|
-
}
|
|
3414
|
-
this.logger.debug("[claude-code] Closed text part due to user message");
|
|
3415
|
-
}
|
|
3416
|
-
accumulatedText = "";
|
|
3417
|
-
textSegments.length = 0;
|
|
3418
|
-
streamedTextLength = 0;
|
|
3419
|
-
emittedTextSinceLastAssistant = "";
|
|
3420
|
-
const sdkParentToolUseIdForResults = message.parent_tool_use_id;
|
|
3421
|
-
const content = message.message.content;
|
|
3422
|
-
for (const result of this.extractToolResults(content)) {
|
|
3423
|
-
if (this.isRetractedToolFrame(result.id, retractedStreamToolIds, "result")) {
|
|
3424
|
-
continue;
|
|
3425
|
-
}
|
|
3426
|
-
let state = toolStates.get(result.id);
|
|
3427
|
-
const toolName = result.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
3428
|
-
this.logger.debug(
|
|
3429
|
-
`[claude-code] Tool result received - Tool: ${toolName}, ID: ${result.id}`
|
|
3430
|
-
);
|
|
3431
|
-
if (!state) {
|
|
3432
|
-
this.logger.warn(
|
|
3433
|
-
`[claude-code] Received tool result for unknown tool ID: ${result.id}`
|
|
3434
|
-
);
|
|
3435
|
-
const resolvedParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(
|
|
3436
|
-
sdkParentToolUseIdForResults,
|
|
3437
|
-
void 0,
|
|
3438
|
-
getFallbackParentId
|
|
3439
|
-
);
|
|
3440
|
-
state = {
|
|
3441
|
-
name: toolName,
|
|
3442
|
-
inputStarted: false,
|
|
3443
|
-
inputClosed: false,
|
|
3444
|
-
callEmitted: false,
|
|
3445
|
-
parentToolCallId: resolvedParentId
|
|
3446
|
-
};
|
|
3447
|
-
toolStates.set(result.id, state);
|
|
3448
|
-
if (!state.inputStarted) {
|
|
3449
|
-
controller.enqueue({
|
|
3450
|
-
type: "tool-input-start",
|
|
3451
|
-
id: result.id,
|
|
3452
|
-
toolName,
|
|
3453
|
-
providerExecuted: true,
|
|
3454
|
-
dynamic: true,
|
|
3455
|
-
// V3 field: indicates tool is provider-defined
|
|
3456
|
-
providerMetadata: {
|
|
3457
|
-
"claude-code": {
|
|
3458
|
-
parentToolCallId: state.parentToolCallId ?? null
|
|
3459
|
-
}
|
|
3460
|
-
}
|
|
3461
|
-
});
|
|
3462
|
-
state.inputStarted = true;
|
|
3463
|
-
}
|
|
3464
|
-
if (!state.inputClosed) {
|
|
3465
|
-
controller.enqueue({
|
|
3466
|
-
type: "tool-input-end",
|
|
3467
|
-
id: result.id
|
|
3468
|
-
});
|
|
3469
|
-
state.inputClosed = true;
|
|
3470
|
-
}
|
|
3471
|
-
}
|
|
3472
|
-
state.name = toolName;
|
|
3473
|
-
emitToolCall(result.id, state);
|
|
3474
|
-
if (isSubagentToolName(toolName)) {
|
|
3475
|
-
activeTaskTools.delete(result.id);
|
|
3476
|
-
}
|
|
3477
|
-
controller.enqueue(
|
|
3478
|
-
this.buildToolResultPart(
|
|
3479
|
-
result.id,
|
|
3480
|
-
toolName,
|
|
3481
|
-
result.result,
|
|
3482
|
-
result.isError,
|
|
3483
|
-
state.parentToolCallId
|
|
3484
|
-
)
|
|
3485
|
-
);
|
|
3486
|
-
}
|
|
3487
|
-
for (const error of this.extractToolErrors(content)) {
|
|
3488
|
-
if (this.isRetractedToolFrame(error.id, retractedStreamToolIds, "error")) {
|
|
3489
|
-
continue;
|
|
3490
|
-
}
|
|
3491
|
-
let state = toolStates.get(error.id);
|
|
3492
|
-
const toolName = error.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
3493
|
-
this.logger.debug(
|
|
3494
|
-
`[claude-code] Tool error received - Tool: ${toolName}, ID: ${error.id}`
|
|
3495
|
-
);
|
|
3496
|
-
if (!state) {
|
|
3497
|
-
this.logger.warn(
|
|
3498
|
-
`[claude-code] Received tool error for unknown tool ID: ${error.id}`
|
|
3499
|
-
);
|
|
3500
|
-
const errorResolvedParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(
|
|
3501
|
-
sdkParentToolUseIdForResults,
|
|
3502
|
-
void 0,
|
|
3503
|
-
getFallbackParentId
|
|
3504
|
-
);
|
|
3505
|
-
state = {
|
|
3506
|
-
name: toolName,
|
|
3507
|
-
inputStarted: true,
|
|
3508
|
-
inputClosed: true,
|
|
3509
|
-
callEmitted: false,
|
|
3510
|
-
parentToolCallId: errorResolvedParentId
|
|
3511
|
-
};
|
|
3512
|
-
toolStates.set(error.id, state);
|
|
3513
|
-
}
|
|
3514
|
-
emitToolCall(error.id, state);
|
|
3515
|
-
if (isSubagentToolName(toolName)) {
|
|
3516
|
-
activeTaskTools.delete(error.id);
|
|
3517
|
-
}
|
|
3518
|
-
controller.enqueue(
|
|
3519
|
-
this.buildToolErrorPart(error.id, toolName, error.error, state.parentToolCallId)
|
|
3520
|
-
);
|
|
3521
|
-
}
|
|
3522
|
-
} else if (message.type === "result") {
|
|
3523
|
-
done();
|
|
3524
|
-
this.mergeResultPermissionDenials(message, metadataTracking);
|
|
3525
|
-
if ("is_error" in message && message.is_error === true) {
|
|
3526
|
-
const resultText = "result" in message && typeof message.result === "string" ? message.result : void 0;
|
|
3527
|
-
const errorsText = "errors" in message && Array.isArray(message.errors) ? message.errors.filter((e) => typeof e === "string").join("; ") : "";
|
|
3528
|
-
const errorMessage = resultText ?? (errorsText || "Claude Code CLI returned an error");
|
|
3529
|
-
throw Object.assign(new Error(errorMessage), {
|
|
3530
|
-
exitCode: 1,
|
|
3531
|
-
errorKind: lastAssistantErrorKind
|
|
3532
|
-
});
|
|
3533
|
-
}
|
|
3534
|
-
if (message.subtype === "error_max_structured_output_retries") {
|
|
3535
|
-
throw new Error(
|
|
3536
|
-
"Failed to generate valid structured output after maximum retries. The model could not produce a response matching the required schema."
|
|
3537
|
-
);
|
|
3538
|
-
}
|
|
3539
|
-
this.logger.info(
|
|
3540
|
-
`[claude-code] Stream completed - Session: ${message.session_id}, Cost: $${message.total_cost_usd?.toFixed(4) ?? "N/A"}, Duration: ${message.duration_ms ?? "N/A"}ms`
|
|
3541
|
-
);
|
|
3542
|
-
if ("usage" in message) {
|
|
3543
|
-
usage = convertClaudeCodeUsage(message.usage);
|
|
3544
|
-
this.logger.debug(
|
|
3545
|
-
`[claude-code] Stream token usage - Input: ${usage.inputTokens.total}, Output: ${usage.outputTokens.total}`
|
|
3546
|
-
);
|
|
3547
|
-
}
|
|
3548
|
-
const stopReason = "stop_reason" in message ? message.stop_reason : void 0;
|
|
3549
|
-
const finishReason = mapClaudeCodeFinishReason(
|
|
3550
|
-
message.subtype,
|
|
3551
|
-
stopReason
|
|
3552
|
-
);
|
|
3553
|
-
this.logger.debug(`[claude-code] Stream finish reason: ${finishReason.unified}`);
|
|
3554
|
-
this.setSessionId(message.session_id);
|
|
3555
|
-
const structuredOutput = "structured_output" in message ? message.structured_output : void 0;
|
|
3556
|
-
const alreadyStreamedJson = hasStreamedJson && options.responseFormat?.type === "json" && hasReceivedStreamEvents;
|
|
3557
|
-
if (alreadyStreamedJson) {
|
|
3558
|
-
if (textPartId) {
|
|
3559
|
-
controller.enqueue({
|
|
3560
|
-
type: "text-end",
|
|
3561
|
-
id: textPartId
|
|
3562
|
-
});
|
|
3563
|
-
}
|
|
3564
|
-
} else if (structuredOutput !== void 0) {
|
|
3565
|
-
const jsonTextId = (0, import_provider_utils.generateId)();
|
|
3566
|
-
const jsonText = JSON.stringify(structuredOutput);
|
|
3567
|
-
controller.enqueue({
|
|
3568
|
-
type: "text-start",
|
|
3569
|
-
id: jsonTextId
|
|
3570
|
-
});
|
|
3571
|
-
controller.enqueue({
|
|
3572
|
-
type: "text-delta",
|
|
3573
|
-
id: jsonTextId,
|
|
3574
|
-
delta: jsonText
|
|
3575
|
-
});
|
|
3576
|
-
controller.enqueue({
|
|
3577
|
-
type: "text-end",
|
|
3578
|
-
id: jsonTextId
|
|
3579
|
-
});
|
|
3580
|
-
} else if (options.responseFormat?.type === "json" && options.responseFormat.schema !== void 0 && finishReason.unified === "stop") {
|
|
3581
|
-
const recoveredJsonText = extractJsonObjectText(accumulatedText);
|
|
3582
|
-
if (recoveredJsonText === void 0) {
|
|
3583
|
-
throw createAPICallError({
|
|
3584
|
-
message: MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE,
|
|
3585
|
-
promptExcerpt: messagesPrompt.substring(0, 200),
|
|
3586
|
-
isRetryable: false
|
|
3587
|
-
});
|
|
3588
|
-
}
|
|
3589
|
-
this.logger.warn(
|
|
3590
|
-
"[claude-code] outputFormat was requested but the CLI returned no structured_output; recovered JSON by parsing the prose response. The schema likely contains constructs the CLI cannot enforce - see the README structured-output limitations."
|
|
3591
|
-
);
|
|
3592
|
-
if (textPartId) {
|
|
3593
|
-
controller.enqueue({
|
|
3594
|
-
type: "text-end",
|
|
3595
|
-
id: textPartId
|
|
3596
|
-
});
|
|
3597
|
-
}
|
|
3598
|
-
const recoveredTextId = (0, import_provider_utils.generateId)();
|
|
3599
|
-
controller.enqueue({
|
|
3600
|
-
type: "text-start",
|
|
3601
|
-
id: recoveredTextId
|
|
3602
|
-
});
|
|
3603
|
-
controller.enqueue({
|
|
3604
|
-
type: "text-delta",
|
|
3605
|
-
id: recoveredTextId,
|
|
3606
|
-
delta: recoveredJsonText
|
|
3607
|
-
});
|
|
3608
|
-
controller.enqueue({
|
|
3609
|
-
type: "text-end",
|
|
3610
|
-
id: recoveredTextId
|
|
3611
|
-
});
|
|
3612
|
-
} else if (textPartId) {
|
|
3613
|
-
controller.enqueue({
|
|
3614
|
-
type: "text-end",
|
|
3615
|
-
id: textPartId
|
|
3616
|
-
});
|
|
3617
|
-
} else if (accumulatedText && !textStreamedViaContentBlock) {
|
|
3618
|
-
const fallbackTextId = (0, import_provider_utils.generateId)();
|
|
3619
|
-
controller.enqueue({
|
|
3620
|
-
type: "text-start",
|
|
3621
|
-
id: fallbackTextId
|
|
3622
|
-
});
|
|
3623
|
-
controller.enqueue({
|
|
3624
|
-
type: "text-delta",
|
|
3625
|
-
id: fallbackTextId,
|
|
3626
|
-
delta: accumulatedText
|
|
3627
|
-
});
|
|
3628
|
-
controller.enqueue({
|
|
3629
|
-
type: "text-end",
|
|
3630
|
-
id: fallbackTextId
|
|
3631
|
-
});
|
|
3632
|
-
}
|
|
3633
|
-
finalizeToolCalls();
|
|
3634
|
-
const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
|
|
3635
|
-
controller.enqueue({
|
|
3636
|
-
type: "finish",
|
|
3637
|
-
finishReason,
|
|
3638
|
-
usage,
|
|
3639
|
-
providerMetadata: {
|
|
3640
|
-
"claude-code": {
|
|
3641
|
-
sessionId: message.session_id,
|
|
3642
|
-
...message.total_cost_usd !== void 0 && {
|
|
3643
|
-
costUsd: message.total_cost_usd
|
|
3644
|
-
},
|
|
3645
|
-
...message.duration_ms !== void 0 && { durationMs: message.duration_ms },
|
|
3646
|
-
...message.modelUsage !== void 0 && {
|
|
3647
|
-
modelUsage: message.modelUsage
|
|
3648
|
-
},
|
|
3649
|
-
// SDK 0.3.x timing metadata (ttft_* only present on SDKResultSuccess)
|
|
3650
|
-
..."ttft_ms" in message && message.ttft_ms !== void 0 && { ttftMs: message.ttft_ms },
|
|
3651
|
-
..."ttft_stream_ms" in message && message.ttft_stream_ms !== void 0 && {
|
|
3652
|
-
ttftStreamMs: message.ttft_stream_ms
|
|
3653
|
-
},
|
|
3654
|
-
..."time_to_request_ms" in message && message.time_to_request_ms !== void 0 && {
|
|
3655
|
-
timeToRequestMs: message.time_to_request_ms
|
|
3656
|
-
},
|
|
3657
|
-
..."warm_spare_claimed" in message && message.warm_spare_claimed !== void 0 && {
|
|
3658
|
-
warmSpareClaimed: message.warm_spare_claimed
|
|
3659
|
-
},
|
|
3660
|
-
...message.terminal_reason !== void 0 && {
|
|
3661
|
-
terminalReason: message.terminal_reason
|
|
3662
|
-
},
|
|
3663
|
-
...metadataTracking.apiRetries > 0 && {
|
|
3664
|
-
apiRetries: metadataTracking.apiRetries
|
|
3665
|
-
},
|
|
3666
|
-
...metadataTracking.permissionDenials.length > 0 && {
|
|
3667
|
-
permissionDenials: metadataTracking.permissionDenials
|
|
3668
|
-
},
|
|
3669
|
-
...metadataTracking.mirrorErrors.length > 0 && {
|
|
3670
|
-
mirrorErrors: metadataTracking.mirrorErrors
|
|
3671
|
-
},
|
|
3672
|
-
...metadataTracking.estimatedThinkingTokens > 0 && {
|
|
3673
|
-
estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
|
|
3674
|
-
},
|
|
3675
|
-
// JSON validation warnings are collected during streaming and included
|
|
3676
|
-
// in providerMetadata since the AI SDK's finish event doesn't support
|
|
3677
|
-
// a top-level warnings field (unlike stream-start which was already emitted)
|
|
3678
|
-
...streamWarnings.length > 0 && {
|
|
3679
|
-
warnings: warningsJson
|
|
3680
|
-
}
|
|
3681
|
-
}
|
|
3682
|
-
}
|
|
3683
|
-
});
|
|
3684
|
-
controller.close();
|
|
3685
|
-
const effectivePromptSuggestions = sdkOptions?.promptSuggestions ?? this.settings.promptSuggestions;
|
|
3686
|
-
if (this.settings.onPromptSuggestion && effectivePromptSuggestions !== false) {
|
|
3687
|
-
await this.drainPromptSuggestion(response, this.settings.onPromptSuggestion);
|
|
3688
|
-
}
|
|
3689
|
-
return;
|
|
3690
|
-
} else if (message.type === "system" && message.subtype === "init") {
|
|
3691
|
-
this.logMcpConnectionIssues(message.mcp_servers);
|
|
3692
|
-
this.setSessionId(message.session_id);
|
|
3693
|
-
this.logger.info(`[claude-code] Stream session initialized: ${message.session_id}`);
|
|
3694
|
-
controller.enqueue({
|
|
3695
|
-
type: "response-metadata",
|
|
3696
|
-
id: message.session_id,
|
|
3697
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
3698
|
-
modelId: this.modelId
|
|
3699
|
-
});
|
|
3700
|
-
} else if (message.type === "system") {
|
|
3701
|
-
this.handleSystemMessage(
|
|
3702
|
-
message,
|
|
3703
|
-
metadataTracking,
|
|
3704
|
-
buildRetractionEvictor(evictLive)
|
|
3705
|
-
);
|
|
3706
|
-
} else if (message.type === "prompt_suggestion") {
|
|
3707
|
-
this.logger.debug("[claude-code] Received prompt suggestion");
|
|
3708
|
-
this.settings.onPromptSuggestion?.(message.suggestion);
|
|
3709
|
-
}
|
|
3710
|
-
}
|
|
3711
|
-
finalizeToolCalls();
|
|
3712
|
-
this.logger.debug("[claude-code] Stream finalized, closing stream");
|
|
3713
|
-
controller.close();
|
|
3714
|
-
} catch (error) {
|
|
3715
|
-
done();
|
|
3716
|
-
this.logger.debug(
|
|
3717
|
-
`[claude-code] Error during doStream: ${error instanceof Error ? error.message : String(error)}`
|
|
3718
|
-
);
|
|
3719
|
-
if (isClaudeCodeTruncationError(error, accumulatedText)) {
|
|
3720
|
-
this.logger.warn(
|
|
3721
|
-
`[claude-code] Detected truncated stream response, returning ${accumulatedText.length} characters of buffered text`
|
|
3722
|
-
);
|
|
3723
|
-
const truncationWarning = {
|
|
3724
|
-
type: "other",
|
|
3725
|
-
message: CLAUDE_CODE_TRUNCATION_WARNING
|
|
3726
|
-
};
|
|
3727
|
-
streamWarnings.push(truncationWarning);
|
|
3728
|
-
if (textPartId) {
|
|
3729
|
-
controller.enqueue({
|
|
3730
|
-
type: "text-end",
|
|
3731
|
-
id: textPartId
|
|
3732
|
-
});
|
|
3733
|
-
} else if (accumulatedText && !textStreamedViaContentBlock) {
|
|
3734
|
-
const fallbackTextId = (0, import_provider_utils.generateId)();
|
|
3735
|
-
controller.enqueue({
|
|
3736
|
-
type: "text-start",
|
|
3737
|
-
id: fallbackTextId
|
|
3738
|
-
});
|
|
3739
|
-
controller.enqueue({
|
|
3740
|
-
type: "text-delta",
|
|
3741
|
-
id: fallbackTextId,
|
|
3742
|
-
delta: accumulatedText
|
|
3743
|
-
});
|
|
3744
|
-
controller.enqueue({
|
|
3745
|
-
type: "text-end",
|
|
3746
|
-
id: fallbackTextId
|
|
3747
|
-
});
|
|
3748
|
-
}
|
|
3749
|
-
finalizeToolCalls();
|
|
3750
|
-
const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
|
|
3751
|
-
controller.enqueue({
|
|
3752
|
-
type: "finish",
|
|
3753
|
-
finishReason: { unified: "length", raw: "truncation" },
|
|
3754
|
-
usage,
|
|
3755
|
-
providerMetadata: {
|
|
3756
|
-
"claude-code": {
|
|
3757
|
-
...this.sessionId !== void 0 && { sessionId: this.sessionId },
|
|
3758
|
-
truncated: true,
|
|
3759
|
-
...metadataTracking.apiRetries > 0 && {
|
|
3760
|
-
apiRetries: metadataTracking.apiRetries
|
|
3761
|
-
},
|
|
3762
|
-
...metadataTracking.permissionDenials.length > 0 && {
|
|
3763
|
-
permissionDenials: metadataTracking.permissionDenials
|
|
3764
|
-
},
|
|
3765
|
-
...metadataTracking.mirrorErrors.length > 0 && {
|
|
3766
|
-
mirrorErrors: metadataTracking.mirrorErrors
|
|
3767
|
-
},
|
|
3768
|
-
...metadataTracking.estimatedThinkingTokens > 0 && {
|
|
3769
|
-
estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
|
|
3770
|
-
},
|
|
3771
|
-
...streamWarnings.length > 0 && {
|
|
3772
|
-
warnings: warningsJson
|
|
3773
|
-
}
|
|
3774
|
-
}
|
|
3775
|
-
}
|
|
3776
|
-
});
|
|
3777
|
-
controller.close();
|
|
3778
|
-
return;
|
|
3779
|
-
}
|
|
3780
|
-
finalizeToolCalls();
|
|
3781
|
-
let errorToEmit;
|
|
3782
|
-
if (isAbortError(error)) {
|
|
3783
|
-
errorToEmit = options.abortSignal?.aborted ? options.abortSignal.reason : error;
|
|
3784
|
-
} else {
|
|
3785
|
-
errorToEmit = this.handleClaudeCodeError(error, messagesPrompt, collectedStderr);
|
|
3786
|
-
}
|
|
3787
|
-
controller.enqueue({
|
|
3788
|
-
type: "error",
|
|
3789
|
-
error: errorToEmit
|
|
3790
|
-
});
|
|
3791
|
-
controller.close();
|
|
3792
|
-
} finally {
|
|
3793
|
-
if (options.abortSignal && abortListener) {
|
|
3794
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
3795
|
-
}
|
|
3796
|
-
}
|
|
3797
|
-
},
|
|
3798
|
-
cancel: () => {
|
|
3799
|
-
if (options.abortSignal && abortListener) {
|
|
3800
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
3801
|
-
}
|
|
3802
|
-
}
|
|
3803
|
-
});
|
|
3804
|
-
return {
|
|
3805
|
-
stream,
|
|
3806
|
-
request: {
|
|
3807
|
-
body: messagesPrompt
|
|
3808
|
-
}
|
|
3809
|
-
};
|
|
3810
|
-
}
|
|
3811
|
-
serializeWarningsForMetadata(warnings) {
|
|
3812
|
-
const result = warnings.map((w) => {
|
|
3813
|
-
const base = { type: w.type };
|
|
3814
|
-
if ("message" in w) {
|
|
3815
|
-
const m = w.message;
|
|
3816
|
-
if (m !== void 0) base.message = String(m);
|
|
3817
|
-
}
|
|
3818
|
-
if (w.type === "unsupported" || w.type === "compatibility") {
|
|
3819
|
-
const feature = w.feature;
|
|
3820
|
-
if (feature !== void 0) base.feature = String(feature);
|
|
3821
|
-
if ("details" in w) {
|
|
3822
|
-
const d = w.details;
|
|
3823
|
-
if (d !== void 0) base.details = String(d);
|
|
3824
|
-
}
|
|
3825
|
-
}
|
|
3826
|
-
return base;
|
|
3827
|
-
});
|
|
3828
|
-
return result;
|
|
3829
|
-
}
|
|
3830
|
-
};
|
|
3831
|
-
|
|
3832
|
-
// src/claude-code-provider.ts
|
|
3833
|
-
function createClaudeCode(options = {}) {
|
|
3834
|
-
const logger = getLogger(options.defaultSettings?.logger);
|
|
3835
|
-
if (options.defaultSettings) {
|
|
3836
|
-
const validation = validateSettings(options.defaultSettings);
|
|
3837
|
-
if (!validation.valid) {
|
|
3838
|
-
throw new Error(`Invalid default settings: ${validation.errors.join(", ")}`);
|
|
3839
|
-
}
|
|
3840
|
-
if (validation.warnings.length > 0) {
|
|
3841
|
-
validation.warnings.forEach((warning) => logger.warn(`Claude Code Provider: ${warning}`));
|
|
3842
|
-
}
|
|
3843
|
-
}
|
|
3844
|
-
const createModel = (modelId, settings = {}) => {
|
|
3845
|
-
const mergedSettings = {
|
|
3846
|
-
...options.defaultSettings,
|
|
3847
|
-
...settings
|
|
3848
|
-
};
|
|
3849
|
-
const validation = validateSettings(mergedSettings);
|
|
3850
|
-
if (!validation.valid) {
|
|
3851
|
-
throw new Error(`Invalid settings: ${validation.errors.join(", ")}`);
|
|
3852
|
-
}
|
|
3853
|
-
return new ClaudeCodeLanguageModel({
|
|
3854
|
-
id: modelId,
|
|
3855
|
-
settings: mergedSettings,
|
|
3856
|
-
settingsValidationWarnings: validation.warnings
|
|
3857
|
-
});
|
|
3858
|
-
};
|
|
3859
|
-
const provider = function(modelId, settings) {
|
|
3860
|
-
if (new.target) {
|
|
3861
|
-
throw new Error("The Claude Code model function cannot be called with the new keyword.");
|
|
3862
|
-
}
|
|
3863
|
-
return createModel(modelId, settings);
|
|
3864
|
-
};
|
|
3865
|
-
provider.languageModel = createModel;
|
|
3866
|
-
provider.chat = createModel;
|
|
3867
|
-
provider.specificationVersion = "v3";
|
|
3868
|
-
provider.embeddingModel = (modelId) => {
|
|
3869
|
-
throw new import_provider3.NoSuchModelError({
|
|
3870
|
-
modelId,
|
|
3871
|
-
modelType: "embeddingModel"
|
|
3872
|
-
});
|
|
3873
|
-
};
|
|
3874
|
-
provider.imageModel = (modelId) => {
|
|
3875
|
-
throw new import_provider3.NoSuchModelError({
|
|
3876
|
-
modelId,
|
|
3877
|
-
modelType: "imageModel"
|
|
3878
|
-
});
|
|
3879
|
-
};
|
|
3880
|
-
return provider;
|
|
3881
|
-
}
|
|
3882
|
-
var claudeCode = createClaudeCode();
|
|
3883
|
-
|
|
3884
|
-
// src/index.ts
|
|
3885
|
-
var import_claude_agent_sdk3 = require("@anthropic-ai/claude-agent-sdk");
|
|
3886
|
-
var import_claude_agent_sdk4 = require("@anthropic-ai/claude-agent-sdk");
|
|
3887
|
-
var import_claude_agent_sdk5 = require("@anthropic-ai/claude-agent-sdk");
|
|
3888
|
-
|
|
3889
|
-
// src/mcp-helpers.ts
|
|
3890
|
-
var import_claude_agent_sdk2 = require("@anthropic-ai/claude-agent-sdk");
|
|
3891
|
-
var import_zod2 = require("zod");
|
|
3892
|
-
function buildIsErrorResult(text) {
|
|
3893
|
-
return {
|
|
3894
|
-
isError: true,
|
|
3895
|
-
content: [{ type: "text", text }]
|
|
3896
|
-
};
|
|
3897
|
-
}
|
|
3898
|
-
async function normalizeToolResultToText(toolName, result) {
|
|
3899
|
-
if (result != null && typeof result[Symbol.asyncIterator] === "function") {
|
|
3900
|
-
let last;
|
|
3901
|
-
for await (const chunk of result) {
|
|
3902
|
-
last = chunk;
|
|
3903
|
-
}
|
|
3904
|
-
result = last;
|
|
3905
|
-
}
|
|
3906
|
-
if (typeof result === "string") {
|
|
3907
|
-
return { text: result };
|
|
3908
|
-
}
|
|
3909
|
-
try {
|
|
3910
|
-
return { text: JSON.stringify(result) ?? "undefined" };
|
|
3911
|
-
} catch (serializationError) {
|
|
3912
|
-
const reason = serializationError instanceof Error ? serializationError.message : String(serializationError);
|
|
3913
|
-
return {
|
|
3914
|
-
isError: buildIsErrorResult(
|
|
3915
|
-
`Tool "${toolName}" succeeded but its result could not be serialized to JSON: ${reason}`
|
|
3916
|
-
)
|
|
3917
|
-
};
|
|
3918
|
-
}
|
|
3919
|
-
}
|
|
3920
|
-
function createCustomMcpServer(config) {
|
|
3921
|
-
const defs = Object.entries(config.tools).map(
|
|
3922
|
-
([name, def]) => (0, import_claude_agent_sdk2.tool)(
|
|
3923
|
-
name,
|
|
3924
|
-
def.description,
|
|
3925
|
-
def.inputSchema.shape,
|
|
3926
|
-
(args, extra) => def.handler(args, extra),
|
|
3927
|
-
def.annotations ? { annotations: def.annotations } : void 0
|
|
3928
|
-
)
|
|
3929
|
-
);
|
|
3930
|
-
return (0, import_claude_agent_sdk2.createSdkMcpServer)({ name: config.name, version: config.version, tools: defs });
|
|
3931
|
-
}
|
|
3932
|
-
var AI_SDK_SCHEMA_SYMBOL = Symbol.for("vercel.ai.schema");
|
|
3933
|
-
function isAiSdkJsonSchema(schema) {
|
|
3934
|
-
return typeof schema === "object" && schema !== null && AI_SDK_SCHEMA_SYMBOL in schema;
|
|
3935
|
-
}
|
|
3936
|
-
function isZodObjectSchema(schema) {
|
|
3937
|
-
if (typeof schema !== "object" || schema === null) {
|
|
3938
|
-
return false;
|
|
3939
|
-
}
|
|
3940
|
-
const candidate = schema;
|
|
3941
|
-
if (!("_zod" in candidate) && !("_def" in candidate)) {
|
|
3942
|
-
return false;
|
|
3943
|
-
}
|
|
3944
|
-
const typeTag = candidate._zod?.def?.type ?? candidate._def?.typeName;
|
|
3945
|
-
if (typeTag !== "object" && typeTag !== "ZodObject") {
|
|
3946
|
-
return false;
|
|
3947
|
-
}
|
|
3948
|
-
return typeof candidate.shape === "object" && candidate.shape !== null;
|
|
3949
|
-
}
|
|
3950
|
-
function createAiSdkMcpServer(name, tools) {
|
|
3951
|
-
const defs = Object.entries(tools).map(([toolName, def]) => {
|
|
3952
|
-
const execute = def.execute;
|
|
3953
|
-
if (typeof execute !== "function") {
|
|
3954
|
-
throw new Error(
|
|
3955
|
-
`createAiSdkMcpServer: tool "${toolName}" has no execute function. Only tools that execute locally can be bridged to the Claude Code CLI.`
|
|
3956
|
-
);
|
|
3957
|
-
}
|
|
3958
|
-
if (isAiSdkJsonSchema(def.inputSchema)) {
|
|
3959
|
-
throw new Error(
|
|
3960
|
-
`createAiSdkMcpServer: tool "${toolName}" uses a JSON Schema-based inputSchema (e.g. the AI SDK's jsonSchema() helper). Only Zod object schemas are supported because the Agent SDK's tool() requires a Zod shape. Define inputSchema with z.object({...}) instead.`
|
|
3961
|
-
);
|
|
3962
|
-
}
|
|
3963
|
-
if (!isZodObjectSchema(def.inputSchema)) {
|
|
3964
|
-
throw new Error(
|
|
3965
|
-
`createAiSdkMcpServer: tool "${toolName}" has an inputSchema that is not a Zod object schema. Pass the same z.object({...}) schema you would give to the AI SDK tool() helper.`
|
|
3966
|
-
);
|
|
3967
|
-
}
|
|
3968
|
-
const zodSchema = def.inputSchema;
|
|
3969
|
-
return (0, import_claude_agent_sdk2.tool)(
|
|
3970
|
-
toolName,
|
|
3971
|
-
def.description ?? "",
|
|
3972
|
-
zodSchema.shape,
|
|
3973
|
-
async (args, extra) => {
|
|
3974
|
-
try {
|
|
3975
|
-
const extraInfo = extra ?? {};
|
|
3976
|
-
const result = await execute.call(def, args, {
|
|
3977
|
-
toolCallId: extraInfo.requestId !== void 0 ? String(extraInfo.requestId) : void 0,
|
|
3978
|
-
abortSignal: extraInfo.signal
|
|
3979
|
-
});
|
|
3980
|
-
const normalized = await normalizeToolResultToText(toolName, result);
|
|
3981
|
-
if ("isError" in normalized) {
|
|
3982
|
-
return normalized.isError;
|
|
3983
|
-
}
|
|
3984
|
-
return { content: [{ type: "text", text: normalized.text }] };
|
|
3985
|
-
} catch (error) {
|
|
3986
|
-
return buildIsErrorResult(error instanceof Error ? error.message : String(error));
|
|
3987
|
-
}
|
|
3988
|
-
}
|
|
3989
|
-
);
|
|
3990
|
-
});
|
|
3991
|
-
return (0, import_claude_agent_sdk2.createSdkMcpServer)({ name, tools: defs });
|
|
3992
|
-
}
|
|
3993
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
3994
|
-
0 && (module.exports = {
|
|
3995
|
-
AbortError,
|
|
3996
|
-
ClaudeCodeLanguageModel,
|
|
3997
|
-
HOOK_EVENTS,
|
|
3998
|
-
InMemorySessionStore,
|
|
3999
|
-
SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
|
|
4000
|
-
claudeCode,
|
|
4001
|
-
createAPICallError,
|
|
4002
|
-
createAiSdkMcpServer,
|
|
4003
|
-
createAuthenticationError,
|
|
4004
|
-
createClaudeCode,
|
|
4005
|
-
createCustomMcpServer,
|
|
4006
|
-
createSdkMcpServer,
|
|
4007
|
-
createTimeoutError,
|
|
4008
|
-
deleteSession,
|
|
4009
|
-
foldSessionSummary,
|
|
4010
|
-
forkSession,
|
|
4011
|
-
getErrorMetadata,
|
|
4012
|
-
getSessionInfo,
|
|
4013
|
-
getSessionMessages,
|
|
4014
|
-
getSubagentMessages,
|
|
4015
|
-
importSessionToStore,
|
|
4016
|
-
isAuthenticationError,
|
|
4017
|
-
isTimeoutError,
|
|
4018
|
-
listSessions,
|
|
4019
|
-
listSubagents,
|
|
4020
|
-
renameSession,
|
|
4021
|
-
startup,
|
|
4022
|
-
tagSession,
|
|
4023
|
-
tool
|
|
4024
|
-
});
|
|
4025
|
-
//# sourceMappingURL=index.cjs.map
|