@openbuilder/cli 0.50.43 → 0.50.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{Banner-C-FTiOIR.js → Banner-BKC6yG6z.js} +2 -2
- package/dist/chunks/Banner-BKC6yG6z.js.map +1 -0
- package/dist/chunks/auto-update-Dq2PFvjt.js +1 -0
- package/dist/chunks/auto-update-Dq2PFvjt.js.map +1 -0
- package/dist/chunks/build-D0qYqIq0.js +1 -0
- package/dist/chunks/build-D0qYqIq0.js.map +1 -0
- package/dist/chunks/cleanup-qVTsA3tk.js +1 -0
- package/dist/chunks/cleanup-qVTsA3tk.js.map +1 -0
- package/dist/chunks/{cli-auth-ChCnxlFl.js → cli-auth-BgiGSBOt.js} +4 -3
- package/dist/chunks/cli-auth-BgiGSBOt.js.map +1 -0
- package/dist/chunks/cli-error-BjQwvWtK.js +1 -0
- package/dist/chunks/cli-error-BjQwvWtK.js.map +1 -0
- package/dist/chunks/config-BGP1jZJ4.js +1 -0
- package/dist/chunks/config-BGP1jZJ4.js.map +1 -0
- package/dist/chunks/config-manager-BkbjtN-H.js +1 -0
- package/dist/chunks/config-manager-BkbjtN-H.js.map +1 -0
- package/dist/chunks/database-BvAbD4sP.js +1 -0
- package/dist/chunks/database-BvAbD4sP.js.map +1 -0
- package/dist/chunks/database-setup-BYjIRAmT.js +1 -0
- package/dist/chunks/database-setup-BYjIRAmT.js.map +1 -0
- package/dist/chunks/devtools-7A3EXJhY.js +75 -0
- package/dist/chunks/devtools-7A3EXJhY.js.map +1 -0
- package/dist/chunks/index-ZNRLfdj5.js +119 -0
- package/dist/chunks/index-ZNRLfdj5.js.map +1 -0
- package/dist/chunks/{init-DNyPS_SR.js → init-IQRjA1g3.js} +8 -9
- package/dist/chunks/init-IQRjA1g3.js.map +1 -0
- package/dist/chunks/{init-tui-EFMXj-MG.js → init-tui-B4jfmo3U.js} +7 -8
- package/dist/chunks/init-tui-B4jfmo3U.js.map +1 -0
- package/dist/chunks/logger-ZpJi7chw.js +1 -0
- package/dist/chunks/logger-ZpJi7chw.js.map +1 -0
- package/dist/chunks/{login-BhtodVsj.js → login-CrIcDJpS.js} +2 -1
- package/dist/chunks/login-CrIcDJpS.js.map +1 -0
- package/dist/chunks/{logout-CDDASeuQ.js → logout-BxgiczmY.js} +2 -1
- package/dist/chunks/logout-BxgiczmY.js.map +1 -0
- package/dist/chunks/{main-tui-D-SG-Ba5.js → main-tui-NPDVPKol.js} +9 -10
- package/dist/chunks/main-tui-NPDVPKol.js.map +1 -0
- package/dist/chunks/manager-CvGX9qqe.js +1 -0
- package/dist/chunks/manager-CvGX9qqe.js.map +1 -0
- package/dist/chunks/{port-allocator-B0q7xkLs.js → port-allocator-DuAZe2_S.js} +3 -36
- package/dist/chunks/port-allocator-DuAZe2_S.js.map +1 -0
- package/dist/chunks/process-killer-CaUL7Kpl.js +1 -0
- package/dist/chunks/process-killer-CaUL7Kpl.js.map +1 -0
- package/dist/chunks/prompts-1QbE_bRr.js +1 -0
- package/dist/chunks/prompts-1QbE_bRr.js.map +1 -0
- package/dist/chunks/repo-cloner-CpOQjFSo.js +1 -0
- package/dist/chunks/repo-cloner-CpOQjFSo.js.map +1 -0
- package/dist/chunks/repo-detector-B_oj696o.js +1 -0
- package/dist/chunks/repo-detector-B_oj696o.js.map +1 -0
- package/dist/chunks/{run-Cor14S0I.js → run-Yh3YjeLl.js} +17 -36
- package/dist/chunks/run-Yh3YjeLl.js.map +1 -0
- package/dist/chunks/runner-logger-instance-nDWv2h2T.js +1 -0
- package/dist/chunks/runner-logger-instance-nDWv2h2T.js.map +1 -0
- package/dist/chunks/spinner-BJL9zWAJ.js +1 -0
- package/dist/chunks/spinner-BJL9zWAJ.js.map +1 -0
- package/dist/chunks/{start-k9iGDVWo.js → start-B-brfyVy.js} +7 -7
- package/dist/chunks/start-B-brfyVy.js.map +1 -0
- package/dist/chunks/start-traditional-uoLZXdxm.js +1 -0
- package/dist/chunks/start-traditional-uoLZXdxm.js.map +1 -0
- package/dist/chunks/status-cS8YwtUx.js +1 -0
- package/dist/chunks/status-cS8YwtUx.js.map +1 -0
- package/dist/chunks/{vendor-react-CXgiD1Dl.js → theme-DOjeB8BU.js} +50 -225
- package/dist/chunks/theme-DOjeB8BU.js.map +1 -0
- package/dist/chunks/upgrade-CKjl4HlB.js +1 -0
- package/dist/chunks/upgrade-CKjl4HlB.js.map +1 -0
- package/dist/chunks/use-app-DozfqdJj.js +10 -0
- package/dist/chunks/use-app-DozfqdJj.js.map +1 -0
- package/dist/chunks/{useBuildState-DscLOZLl.js → useBuildState-DV6wurQ2.js} +2 -2
- package/dist/chunks/useBuildState-DV6wurQ2.js.map +1 -0
- package/dist/cli/index.js +8 -7
- package/dist/cli/index.js.map +1 -0
- package/dist/index.js +109 -700
- package/dist/index.js.map +1 -0
- package/dist/instrument.js +8 -81
- package/dist/instrument.js.map +1 -0
- package/package.json +2 -11
- package/dist/chunks/theme-DhorI2Hb.js +0 -43
- package/dist/chunks/vendor-ai-sdk-CSJ0bw9X.js +0 -1970
- package/dist/chunks/vendor-sentry-CqA9P3UG.js +0 -71910
- package/scripts/install-vendor-deps.js +0 -34
- package/scripts/install-vendor.js +0 -167
- package/scripts/prepare-release.js +0 -83
- package/vendor/ai-sdk-provider-claude-code-LOCAL.tgz +0 -0
- package/vendor/sentry-core-LOCAL.tgz +0 -0
- package/vendor/sentry-nextjs-LOCAL.tgz +0 -0
- package/vendor/sentry-node-LOCAL.tgz +0 -0
- package/vendor/sentry-node-core-LOCAL.tgz +0 -0
|
@@ -1,1970 +0,0 @@
|
|
|
1
|
-
// OpenBuilder CLI - Built with Rollup
|
|
2
|
-
import 'zod/v4';
|
|
3
|
-
import 'zod/v3';
|
|
4
|
-
import { parse } from 'jsonc-parser';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import { existsSync } from 'fs';
|
|
7
|
-
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
8
|
-
|
|
9
|
-
// src/errors/ai-sdk-error.ts
|
|
10
|
-
var marker = "vercel.ai.error";
|
|
11
|
-
var symbol = Symbol.for(marker);
|
|
12
|
-
var _a;
|
|
13
|
-
var _AISDKError = class _AISDKError extends Error {
|
|
14
|
-
/**
|
|
15
|
-
* Creates an AI SDK Error.
|
|
16
|
-
*
|
|
17
|
-
* @param {Object} params - The parameters for creating the error.
|
|
18
|
-
* @param {string} params.name - The name of the error.
|
|
19
|
-
* @param {string} params.message - The error message.
|
|
20
|
-
* @param {unknown} [params.cause] - The underlying cause of the error.
|
|
21
|
-
*/
|
|
22
|
-
constructor({
|
|
23
|
-
name: name14,
|
|
24
|
-
message,
|
|
25
|
-
cause
|
|
26
|
-
}) {
|
|
27
|
-
super(message);
|
|
28
|
-
this[_a] = true;
|
|
29
|
-
this.name = name14;
|
|
30
|
-
this.cause = cause;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Checks if the given error is an AI SDK Error.
|
|
34
|
-
* @param {unknown} error - The error to check.
|
|
35
|
-
* @returns {boolean} True if the error is an AI SDK Error, false otherwise.
|
|
36
|
-
*/
|
|
37
|
-
static isInstance(error) {
|
|
38
|
-
return _AISDKError.hasMarker(error, marker);
|
|
39
|
-
}
|
|
40
|
-
static hasMarker(error, marker15) {
|
|
41
|
-
const markerSymbol = Symbol.for(marker15);
|
|
42
|
-
return error != null && typeof error === "object" && markerSymbol in error && typeof error[markerSymbol] === "boolean" && error[markerSymbol] === true;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
_a = symbol;
|
|
46
|
-
var AISDKError = _AISDKError;
|
|
47
|
-
|
|
48
|
-
// src/errors/api-call-error.ts
|
|
49
|
-
var name = "AI_APICallError";
|
|
50
|
-
var marker2 = `vercel.ai.error.${name}`;
|
|
51
|
-
var symbol2 = Symbol.for(marker2);
|
|
52
|
-
var _a2;
|
|
53
|
-
var APICallError = class extends AISDKError {
|
|
54
|
-
constructor({
|
|
55
|
-
message,
|
|
56
|
-
url,
|
|
57
|
-
requestBodyValues,
|
|
58
|
-
statusCode,
|
|
59
|
-
responseHeaders,
|
|
60
|
-
responseBody,
|
|
61
|
-
cause,
|
|
62
|
-
isRetryable = statusCode != null && (statusCode === 408 || // request timeout
|
|
63
|
-
statusCode === 409 || // conflict
|
|
64
|
-
statusCode === 429 || // too many requests
|
|
65
|
-
statusCode >= 500),
|
|
66
|
-
// server error
|
|
67
|
-
data
|
|
68
|
-
}) {
|
|
69
|
-
super({ name, message, cause });
|
|
70
|
-
this[_a2] = true;
|
|
71
|
-
this.url = url;
|
|
72
|
-
this.requestBodyValues = requestBodyValues;
|
|
73
|
-
this.statusCode = statusCode;
|
|
74
|
-
this.responseHeaders = responseHeaders;
|
|
75
|
-
this.responseBody = responseBody;
|
|
76
|
-
this.isRetryable = isRetryable;
|
|
77
|
-
this.data = data;
|
|
78
|
-
}
|
|
79
|
-
static isInstance(error) {
|
|
80
|
-
return AISDKError.hasMarker(error, marker2);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
_a2 = symbol2;
|
|
84
|
-
|
|
85
|
-
// src/errors/invalid-argument-error.ts
|
|
86
|
-
var name3 = "AI_InvalidArgumentError";
|
|
87
|
-
var marker4 = `vercel.ai.error.${name3}`;
|
|
88
|
-
var symbol4 = Symbol.for(marker4);
|
|
89
|
-
var _a4;
|
|
90
|
-
var InvalidArgumentError = class extends AISDKError {
|
|
91
|
-
constructor({
|
|
92
|
-
message,
|
|
93
|
-
cause,
|
|
94
|
-
argument
|
|
95
|
-
}) {
|
|
96
|
-
super({ name: name3, message, cause });
|
|
97
|
-
this[_a4] = true;
|
|
98
|
-
this.argument = argument;
|
|
99
|
-
}
|
|
100
|
-
static isInstance(error) {
|
|
101
|
-
return AISDKError.hasMarker(error, marker4);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
_a4 = symbol4;
|
|
105
|
-
|
|
106
|
-
// src/errors/load-api-key-error.ts
|
|
107
|
-
var name7 = "AI_LoadAPIKeyError";
|
|
108
|
-
var marker8 = `vercel.ai.error.${name7}`;
|
|
109
|
-
var symbol8 = Symbol.for(marker8);
|
|
110
|
-
var _a8;
|
|
111
|
-
var LoadAPIKeyError = class extends AISDKError {
|
|
112
|
-
// used in isInstance
|
|
113
|
-
constructor({ message }) {
|
|
114
|
-
super({ name: name7, message });
|
|
115
|
-
this[_a8] = true;
|
|
116
|
-
}
|
|
117
|
-
static isInstance(error) {
|
|
118
|
-
return AISDKError.hasMarker(error, marker8);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
_a8 = symbol8;
|
|
122
|
-
|
|
123
|
-
// src/errors/no-such-model-error.ts
|
|
124
|
-
var name10 = "AI_NoSuchModelError";
|
|
125
|
-
var marker11 = `vercel.ai.error.${name10}`;
|
|
126
|
-
var symbol11 = Symbol.for(marker11);
|
|
127
|
-
var _a11;
|
|
128
|
-
var NoSuchModelError = class extends AISDKError {
|
|
129
|
-
constructor({
|
|
130
|
-
errorName = name10,
|
|
131
|
-
modelId,
|
|
132
|
-
modelType,
|
|
133
|
-
message = `No such ${modelType}: ${modelId}`
|
|
134
|
-
}) {
|
|
135
|
-
super({ name: errorName, message });
|
|
136
|
-
this[_a11] = true;
|
|
137
|
-
this.modelId = modelId;
|
|
138
|
-
this.modelType = modelType;
|
|
139
|
-
}
|
|
140
|
-
static isInstance(error) {
|
|
141
|
-
return AISDKError.hasMarker(error, marker11);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
_a11 = symbol11;
|
|
145
|
-
|
|
146
|
-
// src/combine-headers.ts
|
|
147
|
-
var createIdGenerator = ({
|
|
148
|
-
prefix,
|
|
149
|
-
size = 16,
|
|
150
|
-
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
151
|
-
separator = "-"
|
|
152
|
-
} = {}) => {
|
|
153
|
-
const generator = () => {
|
|
154
|
-
const alphabetLength = alphabet.length;
|
|
155
|
-
const chars = new Array(size);
|
|
156
|
-
for (let i = 0; i < size; i++) {
|
|
157
|
-
chars[i] = alphabet[Math.random() * alphabetLength | 0];
|
|
158
|
-
}
|
|
159
|
-
return chars.join("");
|
|
160
|
-
};
|
|
161
|
-
if (prefix == null) {
|
|
162
|
-
return generator;
|
|
163
|
-
}
|
|
164
|
-
if (alphabet.includes(separator)) {
|
|
165
|
-
throw new InvalidArgumentError({
|
|
166
|
-
argument: "separator",
|
|
167
|
-
message: `The separator "${separator}" must not be part of the alphabet "${alphabet}".`
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
return () => `${prefix}${separator}${generator()}`;
|
|
171
|
-
};
|
|
172
|
-
var generateId = createIdGenerator();
|
|
173
|
-
new Set(
|
|
174
|
-
"ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789"
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
// src/claude-code-provider.ts
|
|
178
|
-
|
|
179
|
-
// src/convert-to-claude-code-messages.ts
|
|
180
|
-
var IMAGE_URL_WARNING = "Image URLs are not supported by this provider; supply base64/data URLs.";
|
|
181
|
-
var IMAGE_CONVERSION_WARNING = "Unable to convert image content; supply base64/data URLs.";
|
|
182
|
-
function normalizeBase64(base64) {
|
|
183
|
-
return base64.replace(/\s+/g, "");
|
|
184
|
-
}
|
|
185
|
-
function isImageMimeType(mimeType) {
|
|
186
|
-
return typeof mimeType === "string" && mimeType.trim().toLowerCase().startsWith("image/");
|
|
187
|
-
}
|
|
188
|
-
function createImageContent(mediaType, data) {
|
|
189
|
-
const trimmedType = mediaType.trim();
|
|
190
|
-
const trimmedData = normalizeBase64(data.trim());
|
|
191
|
-
if (!trimmedType || !trimmedData) {
|
|
192
|
-
return void 0;
|
|
193
|
-
}
|
|
194
|
-
return {
|
|
195
|
-
type: "image",
|
|
196
|
-
source: {
|
|
197
|
-
type: "base64",
|
|
198
|
-
media_type: trimmedType,
|
|
199
|
-
data: trimmedData
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
function extractMimeType(candidate) {
|
|
204
|
-
if (typeof candidate === "string" && candidate.trim()) {
|
|
205
|
-
return candidate.trim();
|
|
206
|
-
}
|
|
207
|
-
return void 0;
|
|
208
|
-
}
|
|
209
|
-
function parseObjectImage(imageObj, fallbackMimeType) {
|
|
210
|
-
const data = typeof imageObj.data === "string" ? imageObj.data : void 0;
|
|
211
|
-
const mimeType = extractMimeType(
|
|
212
|
-
imageObj.mimeType ?? imageObj.mediaType ?? imageObj.media_type ?? fallbackMimeType
|
|
213
|
-
);
|
|
214
|
-
if (!data || !mimeType) {
|
|
215
|
-
return void 0;
|
|
216
|
-
}
|
|
217
|
-
return createImageContent(mimeType, data);
|
|
218
|
-
}
|
|
219
|
-
function parseStringImage(value, fallbackMimeType) {
|
|
220
|
-
const trimmed = value.trim();
|
|
221
|
-
if (/^https?:\/\//i.test(trimmed)) {
|
|
222
|
-
return { warning: IMAGE_URL_WARNING };
|
|
223
|
-
}
|
|
224
|
-
const dataUrlMatch = trimmed.match(/^data:([^;]+);base64,(.+)$/i);
|
|
225
|
-
if (dataUrlMatch) {
|
|
226
|
-
const [, mediaType, data] = dataUrlMatch;
|
|
227
|
-
const content = createImageContent(mediaType, data);
|
|
228
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
229
|
-
}
|
|
230
|
-
const base64Match = trimmed.match(/^base64:([^,]+),(.+)$/i);
|
|
231
|
-
if (base64Match) {
|
|
232
|
-
const [, explicitMimeType, data] = base64Match;
|
|
233
|
-
const content = createImageContent(explicitMimeType, data);
|
|
234
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
235
|
-
}
|
|
236
|
-
if (fallbackMimeType) {
|
|
237
|
-
const content = createImageContent(fallbackMimeType, trimmed);
|
|
238
|
-
if (content) {
|
|
239
|
-
return { content };
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
243
|
-
}
|
|
244
|
-
function parseImagePart(part) {
|
|
245
|
-
if (!part || typeof part !== "object") {
|
|
246
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
247
|
-
}
|
|
248
|
-
const imageValue = part.image;
|
|
249
|
-
const mimeType = extractMimeType(part.mimeType);
|
|
250
|
-
if (typeof imageValue === "string") {
|
|
251
|
-
return parseStringImage(imageValue, mimeType);
|
|
252
|
-
}
|
|
253
|
-
if (imageValue && typeof imageValue === "object") {
|
|
254
|
-
const content = parseObjectImage(imageValue, mimeType);
|
|
255
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
256
|
-
}
|
|
257
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
258
|
-
}
|
|
259
|
-
function convertBinaryToBase64(data) {
|
|
260
|
-
if (typeof Buffer !== "undefined") {
|
|
261
|
-
const buffer = data instanceof Uint8Array ? Buffer.from(data) : Buffer.from(new Uint8Array(data));
|
|
262
|
-
return buffer.toString("base64");
|
|
263
|
-
}
|
|
264
|
-
if (typeof btoa === "function") {
|
|
265
|
-
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
266
|
-
let binary = "";
|
|
267
|
-
const chunkSize = 32768;
|
|
268
|
-
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
269
|
-
const chunk = bytes.subarray(i, i + chunkSize);
|
|
270
|
-
binary += String.fromCharCode(...chunk);
|
|
271
|
-
}
|
|
272
|
-
return btoa(binary);
|
|
273
|
-
}
|
|
274
|
-
return void 0;
|
|
275
|
-
}
|
|
276
|
-
function parseFilePart(part) {
|
|
277
|
-
const mimeType = extractMimeType(part.mediaType ?? part.mimeType);
|
|
278
|
-
if (!mimeType || !isImageMimeType(mimeType)) {
|
|
279
|
-
return {};
|
|
280
|
-
}
|
|
281
|
-
const data = part.data;
|
|
282
|
-
if (typeof data === "string") {
|
|
283
|
-
const content = createImageContent(mimeType, data);
|
|
284
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
285
|
-
}
|
|
286
|
-
if (data instanceof Uint8Array || typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
|
|
287
|
-
const base64 = convertBinaryToBase64(data);
|
|
288
|
-
if (!base64) {
|
|
289
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
290
|
-
}
|
|
291
|
-
const content = createImageContent(mimeType, base64);
|
|
292
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
293
|
-
}
|
|
294
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
295
|
-
}
|
|
296
|
-
function convertToClaudeCodeMessages(prompt, mode = { type: "regular" }, jsonSchema) {
|
|
297
|
-
const messages = [];
|
|
298
|
-
const warnings = [];
|
|
299
|
-
let systemPrompt;
|
|
300
|
-
const streamingSegments = [];
|
|
301
|
-
const imageMap = /* @__PURE__ */ new Map();
|
|
302
|
-
let hasImageParts = false;
|
|
303
|
-
const addSegment = (formatted) => {
|
|
304
|
-
streamingSegments.push({ formatted });
|
|
305
|
-
return streamingSegments.length - 1;
|
|
306
|
-
};
|
|
307
|
-
const addImageForSegment = (segmentIndex, content) => {
|
|
308
|
-
hasImageParts = true;
|
|
309
|
-
if (!imageMap.has(segmentIndex)) {
|
|
310
|
-
imageMap.set(segmentIndex, []);
|
|
311
|
-
}
|
|
312
|
-
imageMap.get(segmentIndex)?.push(content);
|
|
313
|
-
};
|
|
314
|
-
for (const message of prompt) {
|
|
315
|
-
switch (message.role) {
|
|
316
|
-
case "system":
|
|
317
|
-
systemPrompt = message.content;
|
|
318
|
-
if (typeof message.content === "string" && message.content.trim().length > 0) {
|
|
319
|
-
addSegment(message.content);
|
|
320
|
-
} else {
|
|
321
|
-
addSegment("");
|
|
322
|
-
}
|
|
323
|
-
break;
|
|
324
|
-
case "user":
|
|
325
|
-
if (typeof message.content === "string") {
|
|
326
|
-
messages.push(message.content);
|
|
327
|
-
addSegment(`Human: ${message.content}`);
|
|
328
|
-
} else {
|
|
329
|
-
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
330
|
-
const segmentIndex = addSegment(textParts ? `Human: ${textParts}` : "");
|
|
331
|
-
if (textParts) {
|
|
332
|
-
messages.push(textParts);
|
|
333
|
-
}
|
|
334
|
-
for (const part of message.content) {
|
|
335
|
-
if (part.type === "image") {
|
|
336
|
-
const { content, warning } = parseImagePart(part);
|
|
337
|
-
if (content) {
|
|
338
|
-
addImageForSegment(segmentIndex, content);
|
|
339
|
-
} else if (warning) {
|
|
340
|
-
warnings.push(warning);
|
|
341
|
-
}
|
|
342
|
-
} else if (part.type === "file") {
|
|
343
|
-
const { content, warning } = parseFilePart(part);
|
|
344
|
-
if (content) {
|
|
345
|
-
addImageForSegment(segmentIndex, content);
|
|
346
|
-
} else if (warning) {
|
|
347
|
-
warnings.push(warning);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
break;
|
|
353
|
-
case "assistant": {
|
|
354
|
-
let assistantContent = "";
|
|
355
|
-
if (typeof message.content === "string") {
|
|
356
|
-
assistantContent = message.content;
|
|
357
|
-
} else {
|
|
358
|
-
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
359
|
-
if (textParts) {
|
|
360
|
-
assistantContent = textParts;
|
|
361
|
-
}
|
|
362
|
-
const toolCalls = message.content.filter((part) => part.type === "tool-call");
|
|
363
|
-
if (toolCalls.length > 0) {
|
|
364
|
-
assistantContent += `
|
|
365
|
-
[Tool calls made]`;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
const formattedAssistant = `Assistant: ${assistantContent}`;
|
|
369
|
-
messages.push(formattedAssistant);
|
|
370
|
-
addSegment(formattedAssistant);
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
case "tool":
|
|
374
|
-
for (const tool3 of message.content) {
|
|
375
|
-
const resultText = tool3.output.type === "text" ? tool3.output.value : JSON.stringify(tool3.output.value);
|
|
376
|
-
const formattedToolResult = `Tool Result (${tool3.toolName}): ${resultText}`;
|
|
377
|
-
messages.push(formattedToolResult);
|
|
378
|
-
addSegment(formattedToolResult);
|
|
379
|
-
}
|
|
380
|
-
break;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
let finalPrompt = "";
|
|
384
|
-
if (systemPrompt) {
|
|
385
|
-
finalPrompt = systemPrompt;
|
|
386
|
-
}
|
|
387
|
-
if (messages.length > 0) {
|
|
388
|
-
const formattedMessages = [];
|
|
389
|
-
for (let i = 0; i < messages.length; i++) {
|
|
390
|
-
const msg = messages[i];
|
|
391
|
-
if (msg.startsWith("Assistant:") || msg.startsWith("Tool Result")) {
|
|
392
|
-
formattedMessages.push(msg);
|
|
393
|
-
} else {
|
|
394
|
-
formattedMessages.push(`Human: ${msg}`);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
if (finalPrompt) {
|
|
398
|
-
const joinedMessages = formattedMessages.join("\n\n");
|
|
399
|
-
finalPrompt = joinedMessages ? `${finalPrompt}
|
|
400
|
-
|
|
401
|
-
${joinedMessages}` : finalPrompt;
|
|
402
|
-
} else {
|
|
403
|
-
finalPrompt = formattedMessages.join("\n\n");
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
let streamingParts = [];
|
|
407
|
-
const imagePartsInOrder = [];
|
|
408
|
-
const appendImagesForIndex = (index) => {
|
|
409
|
-
const images = imageMap.get(index);
|
|
410
|
-
if (!images) {
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
images.forEach((image) => {
|
|
414
|
-
streamingParts.push(image);
|
|
415
|
-
imagePartsInOrder.push(image);
|
|
416
|
-
});
|
|
417
|
-
};
|
|
418
|
-
if (streamingSegments.length > 0) {
|
|
419
|
-
let accumulatedText = "";
|
|
420
|
-
let emittedText = false;
|
|
421
|
-
const flushText = () => {
|
|
422
|
-
if (!accumulatedText) {
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
streamingParts.push({ type: "text", text: accumulatedText });
|
|
426
|
-
accumulatedText = "";
|
|
427
|
-
emittedText = true;
|
|
428
|
-
};
|
|
429
|
-
streamingSegments.forEach((segment, index) => {
|
|
430
|
-
const segmentText = segment.formatted;
|
|
431
|
-
if (segmentText) {
|
|
432
|
-
if (!accumulatedText) {
|
|
433
|
-
accumulatedText = emittedText ? `
|
|
434
|
-
|
|
435
|
-
${segmentText}` : segmentText;
|
|
436
|
-
} else {
|
|
437
|
-
accumulatedText += `
|
|
438
|
-
|
|
439
|
-
${segmentText}`;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
if (imageMap.has(index)) {
|
|
443
|
-
flushText();
|
|
444
|
-
appendImagesForIndex(index);
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
flushText();
|
|
448
|
-
}
|
|
449
|
-
if (mode?.type === "object-json" && jsonSchema) {
|
|
450
|
-
const schemaStr = JSON.stringify(jsonSchema, null, 2);
|
|
451
|
-
finalPrompt = `CRITICAL: You MUST respond with ONLY a JSON object. NO other text, NO explanations, NO questions.
|
|
452
|
-
|
|
453
|
-
Your response MUST start with { and end with }
|
|
454
|
-
|
|
455
|
-
The JSON MUST match this EXACT schema:
|
|
456
|
-
${schemaStr}
|
|
457
|
-
|
|
458
|
-
Now, based on the following conversation, generate ONLY the JSON object with the exact fields specified above:
|
|
459
|
-
|
|
460
|
-
${finalPrompt}
|
|
461
|
-
|
|
462
|
-
Remember: Your ENTIRE response must be ONLY the JSON object, starting with { and ending with }`;
|
|
463
|
-
streamingParts = [{ type: "text", text: finalPrompt }, ...imagePartsInOrder];
|
|
464
|
-
}
|
|
465
|
-
return {
|
|
466
|
-
messagesPrompt: finalPrompt,
|
|
467
|
-
systemPrompt,
|
|
468
|
-
...warnings.length > 0 && { warnings },
|
|
469
|
-
streamingContentParts: streamingParts.length > 0 ? streamingParts : [
|
|
470
|
-
{ type: "text", text: finalPrompt },
|
|
471
|
-
...imagePartsInOrder
|
|
472
|
-
],
|
|
473
|
-
hasImageParts
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
function extractJson(text) {
|
|
477
|
-
let content = text.trim();
|
|
478
|
-
const fenceMatch = /```(?:json)?\s*([\s\S]*?)\s*```/i.exec(content);
|
|
479
|
-
if (fenceMatch) {
|
|
480
|
-
content = fenceMatch[1];
|
|
481
|
-
}
|
|
482
|
-
const varMatch = /^\s*(?:const|let|var)\s+\w+\s*=\s*([\s\S]*)/i.exec(content);
|
|
483
|
-
if (varMatch) {
|
|
484
|
-
content = varMatch[1];
|
|
485
|
-
if (content.trim().endsWith(";")) {
|
|
486
|
-
content = content.trim().slice(0, -1);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
const firstObj = content.indexOf("{");
|
|
490
|
-
const firstArr = content.indexOf("[");
|
|
491
|
-
if (firstObj === -1 && firstArr === -1) {
|
|
492
|
-
return text;
|
|
493
|
-
}
|
|
494
|
-
const start = firstArr === -1 ? firstObj : firstObj === -1 ? firstArr : Math.min(firstObj, firstArr);
|
|
495
|
-
content = content.slice(start);
|
|
496
|
-
const tryParse = (value) => {
|
|
497
|
-
const errors = [];
|
|
498
|
-
try {
|
|
499
|
-
const result = parse(value, errors, { allowTrailingComma: true });
|
|
500
|
-
if (errors.length === 0) {
|
|
501
|
-
return JSON.stringify(result, null, 2);
|
|
502
|
-
}
|
|
503
|
-
} catch {
|
|
504
|
-
}
|
|
505
|
-
return void 0;
|
|
506
|
-
};
|
|
507
|
-
const parsed = tryParse(content);
|
|
508
|
-
if (parsed !== void 0) {
|
|
509
|
-
return parsed;
|
|
510
|
-
}
|
|
511
|
-
const openChar = content[0];
|
|
512
|
-
const closeChar = openChar === "{" ? "}" : "]";
|
|
513
|
-
const closingPositions = [];
|
|
514
|
-
let depth = 0;
|
|
515
|
-
let inString = false;
|
|
516
|
-
let escapeNext = false;
|
|
517
|
-
for (let i = 0; i < content.length; i++) {
|
|
518
|
-
const char = content[i];
|
|
519
|
-
if (escapeNext) {
|
|
520
|
-
escapeNext = false;
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
if (char === "\\") {
|
|
524
|
-
escapeNext = true;
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
if (char === '"' && !inString) {
|
|
528
|
-
inString = true;
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
if (char === '"' && inString) {
|
|
532
|
-
inString = false;
|
|
533
|
-
continue;
|
|
534
|
-
}
|
|
535
|
-
if (inString) continue;
|
|
536
|
-
if (char === openChar) {
|
|
537
|
-
depth++;
|
|
538
|
-
} else if (char === closeChar) {
|
|
539
|
-
depth--;
|
|
540
|
-
if (depth === 0) {
|
|
541
|
-
closingPositions.push(i + 1);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
for (let i = closingPositions.length - 1; i >= 0; i--) {
|
|
546
|
-
const attempt = tryParse(content.slice(0, closingPositions[i]));
|
|
547
|
-
if (attempt !== void 0) {
|
|
548
|
-
return attempt;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
const searchStart = Math.max(0, content.length - 1e3);
|
|
552
|
-
for (let end = content.length - 1; end > searchStart; end--) {
|
|
553
|
-
const attempt = tryParse(content.slice(0, end));
|
|
554
|
-
if (attempt !== void 0) {
|
|
555
|
-
return attempt;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
return text;
|
|
559
|
-
}
|
|
560
|
-
function createAPICallError({
|
|
561
|
-
message,
|
|
562
|
-
code,
|
|
563
|
-
exitCode,
|
|
564
|
-
stderr,
|
|
565
|
-
promptExcerpt,
|
|
566
|
-
isRetryable = false
|
|
567
|
-
}) {
|
|
568
|
-
const metadata = {
|
|
569
|
-
code,
|
|
570
|
-
exitCode,
|
|
571
|
-
stderr,
|
|
572
|
-
promptExcerpt
|
|
573
|
-
};
|
|
574
|
-
return new APICallError({
|
|
575
|
-
message,
|
|
576
|
-
isRetryable,
|
|
577
|
-
url: "claude-code-cli://command",
|
|
578
|
-
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
|
|
579
|
-
data: metadata
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
function createAuthenticationError({ message }) {
|
|
583
|
-
return new LoadAPIKeyError({
|
|
584
|
-
message: message || "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
function createTimeoutError({
|
|
588
|
-
message,
|
|
589
|
-
promptExcerpt,
|
|
590
|
-
timeoutMs
|
|
591
|
-
}) {
|
|
592
|
-
const metadata = {
|
|
593
|
-
code: "TIMEOUT",
|
|
594
|
-
promptExcerpt
|
|
595
|
-
};
|
|
596
|
-
return new APICallError({
|
|
597
|
-
message,
|
|
598
|
-
isRetryable: true,
|
|
599
|
-
url: "claude-code-cli://command",
|
|
600
|
-
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
|
|
601
|
-
data: timeoutMs !== void 0 ? { ...metadata, timeoutMs } : metadata
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// src/map-claude-code-finish-reason.ts
|
|
606
|
-
function mapClaudeCodeFinishReason(subtype) {
|
|
607
|
-
switch (subtype) {
|
|
608
|
-
case "success":
|
|
609
|
-
return "stop";
|
|
610
|
-
case "error_max_turns":
|
|
611
|
-
return "length";
|
|
612
|
-
case "error_during_execution":
|
|
613
|
-
return "error";
|
|
614
|
-
default:
|
|
615
|
-
return "stop";
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
var loggerFunctionSchema = z.object({
|
|
619
|
-
debug: z.any().refine((val) => typeof val === "function", {
|
|
620
|
-
message: "debug must be a function"
|
|
621
|
-
}),
|
|
622
|
-
info: z.any().refine((val) => typeof val === "function", {
|
|
623
|
-
message: "info must be a function"
|
|
624
|
-
}),
|
|
625
|
-
warn: z.any().refine((val) => typeof val === "function", {
|
|
626
|
-
message: "warn must be a function"
|
|
627
|
-
}),
|
|
628
|
-
error: z.any().refine((val) => typeof val === "function", {
|
|
629
|
-
message: "error must be a function"
|
|
630
|
-
})
|
|
631
|
-
});
|
|
632
|
-
var claudeCodeSettingsSchema = z.object({
|
|
633
|
-
pathToClaudeCodeExecutable: z.string().optional(),
|
|
634
|
-
customSystemPrompt: z.string().optional(),
|
|
635
|
-
appendSystemPrompt: z.string().optional(),
|
|
636
|
-
systemPrompt: z.union([
|
|
637
|
-
z.string(),
|
|
638
|
-
z.object({
|
|
639
|
-
type: z.literal("preset"),
|
|
640
|
-
preset: z.literal("claude_code"),
|
|
641
|
-
append: z.string().optional()
|
|
642
|
-
})
|
|
643
|
-
]).optional(),
|
|
644
|
-
maxTurns: z.number().int().min(1).max(100).optional(),
|
|
645
|
-
maxThinkingTokens: z.number().int().positive().max(1e5).optional(),
|
|
646
|
-
cwd: z.string().refine(
|
|
647
|
-
(val) => {
|
|
648
|
-
if (typeof process === "undefined" || !process.versions?.node) {
|
|
649
|
-
return true;
|
|
650
|
-
}
|
|
651
|
-
return !val || existsSync(val);
|
|
652
|
-
},
|
|
653
|
-
{ message: "Working directory must exist" }
|
|
654
|
-
).optional(),
|
|
655
|
-
executable: z.enum(["bun", "deno", "node"]).optional(),
|
|
656
|
-
executableArgs: z.array(z.string()).optional(),
|
|
657
|
-
permissionMode: z.enum(["default", "acceptEdits", "bypassPermissions", "plan"]).optional(),
|
|
658
|
-
permissionPromptToolName: z.string().optional(),
|
|
659
|
-
continue: z.boolean().optional(),
|
|
660
|
-
resume: z.string().optional(),
|
|
661
|
-
allowedTools: z.array(z.string()).optional(),
|
|
662
|
-
disallowedTools: z.array(z.string()).optional(),
|
|
663
|
-
settingSources: z.array(z.enum(["user", "project", "local"])).optional(),
|
|
664
|
-
streamingInput: z.enum(["auto", "always", "off"]).optional(),
|
|
665
|
-
// Hooks and tool-permission callback (permissive validation of shapes)
|
|
666
|
-
canUseTool: z.any().refine((v) => v === void 0 || typeof v === "function", {
|
|
667
|
-
message: "canUseTool must be a function"
|
|
668
|
-
}).optional(),
|
|
669
|
-
hooks: z.record(
|
|
670
|
-
z.string(),
|
|
671
|
-
z.array(
|
|
672
|
-
z.object({
|
|
673
|
-
matcher: z.string().optional(),
|
|
674
|
-
hooks: z.array(z.any()).nonempty()
|
|
675
|
-
})
|
|
676
|
-
)
|
|
677
|
-
).optional(),
|
|
678
|
-
mcpServers: z.record(
|
|
679
|
-
z.string(),
|
|
680
|
-
z.union([
|
|
681
|
-
// McpStdioServerConfig
|
|
682
|
-
z.object({
|
|
683
|
-
type: z.literal("stdio").optional(),
|
|
684
|
-
command: z.string(),
|
|
685
|
-
args: z.array(z.string()).optional(),
|
|
686
|
-
env: z.record(z.string(), z.string()).optional()
|
|
687
|
-
}),
|
|
688
|
-
// McpSSEServerConfig
|
|
689
|
-
z.object({
|
|
690
|
-
type: z.literal("sse"),
|
|
691
|
-
url: z.string(),
|
|
692
|
-
headers: z.record(z.string(), z.string()).optional()
|
|
693
|
-
}),
|
|
694
|
-
// McpHttpServerConfig
|
|
695
|
-
z.object({
|
|
696
|
-
type: z.literal("http"),
|
|
697
|
-
url: z.string(),
|
|
698
|
-
headers: z.record(z.string(), z.string()).optional()
|
|
699
|
-
}),
|
|
700
|
-
// McpSdkServerConfig (in-process custom tools)
|
|
701
|
-
z.object({
|
|
702
|
-
type: z.literal("sdk"),
|
|
703
|
-
name: z.string(),
|
|
704
|
-
instance: z.any()
|
|
705
|
-
})
|
|
706
|
-
])
|
|
707
|
-
).optional(),
|
|
708
|
-
verbose: z.boolean().optional(),
|
|
709
|
-
logger: z.union([z.literal(false), loggerFunctionSchema]).optional(),
|
|
710
|
-
env: z.record(z.string(), z.string().optional()).optional(),
|
|
711
|
-
additionalDirectories: z.array(z.string()).optional(),
|
|
712
|
-
agents: z.record(
|
|
713
|
-
z.string(),
|
|
714
|
-
z.object({
|
|
715
|
-
description: z.string(),
|
|
716
|
-
tools: z.array(z.string()).optional(),
|
|
717
|
-
prompt: z.string(),
|
|
718
|
-
model: z.enum(["sonnet", "opus", "haiku", "inherit"]).optional()
|
|
719
|
-
})
|
|
720
|
-
).optional(),
|
|
721
|
-
includePartialMessages: z.boolean().optional(),
|
|
722
|
-
fallbackModel: z.string().optional(),
|
|
723
|
-
forkSession: z.boolean().optional(),
|
|
724
|
-
stderr: z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
725
|
-
message: "stderr must be a function"
|
|
726
|
-
}).optional(),
|
|
727
|
-
strictMcpConfig: z.boolean().optional(),
|
|
728
|
-
extraArgs: z.record(z.string(), z.union([z.string(), z.null()])).optional(),
|
|
729
|
-
queryFunction: z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
730
|
-
message: "queryFunction must be a function"
|
|
731
|
-
}).optional()
|
|
732
|
-
}).strict();
|
|
733
|
-
function validateModelId(modelId) {
|
|
734
|
-
const knownModels = ["opus", "sonnet", "haiku"];
|
|
735
|
-
if (!modelId || modelId.trim() === "") {
|
|
736
|
-
throw new Error("Model ID cannot be empty");
|
|
737
|
-
}
|
|
738
|
-
if (!knownModels.includes(modelId)) {
|
|
739
|
-
return `Unknown model ID: '${modelId}'. Proceeding with custom model. Known models are: ${knownModels.join(", ")}`;
|
|
740
|
-
}
|
|
741
|
-
return void 0;
|
|
742
|
-
}
|
|
743
|
-
function validateSettings(settings) {
|
|
744
|
-
const warnings = [];
|
|
745
|
-
const errors = [];
|
|
746
|
-
try {
|
|
747
|
-
const result = claudeCodeSettingsSchema.safeParse(settings);
|
|
748
|
-
if (!result.success) {
|
|
749
|
-
const errorObject = result.error;
|
|
750
|
-
const issues = errorObject.errors || errorObject.issues || [];
|
|
751
|
-
issues.forEach((err) => {
|
|
752
|
-
const path = err.path.join(".");
|
|
753
|
-
errors.push(`${path ? `${path}: ` : ""}${err.message}`);
|
|
754
|
-
});
|
|
755
|
-
return { valid: false, warnings, errors };
|
|
756
|
-
}
|
|
757
|
-
const validSettings = result.data;
|
|
758
|
-
if (validSettings.maxTurns && validSettings.maxTurns > 20) {
|
|
759
|
-
warnings.push(
|
|
760
|
-
`High maxTurns value (${validSettings.maxTurns}) may lead to long-running conversations`
|
|
761
|
-
);
|
|
762
|
-
}
|
|
763
|
-
if (validSettings.maxThinkingTokens && validSettings.maxThinkingTokens > 5e4) {
|
|
764
|
-
warnings.push(
|
|
765
|
-
`Very high maxThinkingTokens (${validSettings.maxThinkingTokens}) may increase response time`
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
if (validSettings.allowedTools && validSettings.disallowedTools) {
|
|
769
|
-
warnings.push(
|
|
770
|
-
"Both allowedTools and disallowedTools are specified. Only allowedTools will be used."
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
const validateToolNames = (tools, type) => {
|
|
774
|
-
tools.forEach((tool3) => {
|
|
775
|
-
if (!/^[a-zA-Z_][a-zA-Z0-9_]*(\([^)]*\))?$/.test(tool3) && !tool3.startsWith("mcp__")) {
|
|
776
|
-
warnings.push(`Unusual ${type} tool name format: '${tool3}'`);
|
|
777
|
-
}
|
|
778
|
-
});
|
|
779
|
-
};
|
|
780
|
-
if (validSettings.allowedTools) {
|
|
781
|
-
validateToolNames(validSettings.allowedTools, "allowed");
|
|
782
|
-
}
|
|
783
|
-
if (validSettings.disallowedTools) {
|
|
784
|
-
validateToolNames(validSettings.disallowedTools, "disallowed");
|
|
785
|
-
}
|
|
786
|
-
return { valid: true, warnings, errors };
|
|
787
|
-
} catch (error) {
|
|
788
|
-
errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
789
|
-
return { valid: false, warnings, errors };
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
function validatePrompt(prompt) {
|
|
793
|
-
const MAX_PROMPT_LENGTH = 1e5;
|
|
794
|
-
if (prompt.length > MAX_PROMPT_LENGTH) {
|
|
795
|
-
return `Very long prompt (${prompt.length} characters) may cause performance issues or timeouts`;
|
|
796
|
-
}
|
|
797
|
-
return void 0;
|
|
798
|
-
}
|
|
799
|
-
function validateSessionId(sessionId) {
|
|
800
|
-
if (sessionId && !/^[a-zA-Z0-9-_]+$/.test(sessionId)) {
|
|
801
|
-
return `Unusual session ID format. This may cause issues with session resumption.`;
|
|
802
|
-
}
|
|
803
|
-
return void 0;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// src/logger.ts
|
|
807
|
-
var defaultLogger = {
|
|
808
|
-
debug: (message) => console.debug(`[DEBUG] ${message}`),
|
|
809
|
-
info: (message) => console.info(`[INFO] ${message}`),
|
|
810
|
-
warn: (message) => console.warn(`[WARN] ${message}`),
|
|
811
|
-
error: (message) => console.error(`[ERROR] ${message}`)
|
|
812
|
-
};
|
|
813
|
-
var noopLogger = {
|
|
814
|
-
debug: () => {
|
|
815
|
-
},
|
|
816
|
-
info: () => {
|
|
817
|
-
},
|
|
818
|
-
warn: () => {
|
|
819
|
-
},
|
|
820
|
-
error: () => {
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
function getLogger(logger) {
|
|
824
|
-
if (logger === false) {
|
|
825
|
-
return noopLogger;
|
|
826
|
-
}
|
|
827
|
-
if (logger === void 0) {
|
|
828
|
-
return defaultLogger;
|
|
829
|
-
}
|
|
830
|
-
return logger;
|
|
831
|
-
}
|
|
832
|
-
function createVerboseLogger(logger, verbose = false) {
|
|
833
|
-
if (verbose) {
|
|
834
|
-
return logger;
|
|
835
|
-
}
|
|
836
|
-
return {
|
|
837
|
-
debug: () => {
|
|
838
|
-
},
|
|
839
|
-
// No-op when not verbose
|
|
840
|
-
info: () => {
|
|
841
|
-
},
|
|
842
|
-
// No-op when not verbose
|
|
843
|
-
warn: logger.warn.bind(logger),
|
|
844
|
-
error: logger.error.bind(logger)
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
var CLAUDE_CODE_TRUNCATION_WARNING = "Claude Code SDK output ended unexpectedly; returning truncated response from buffered text. Await upstream fix to avoid data loss.";
|
|
848
|
-
var MIN_TRUNCATION_LENGTH = 512;
|
|
849
|
-
function isClaudeCodeTruncationError(error, bufferedText) {
|
|
850
|
-
const isSyntaxError = error instanceof SyntaxError || // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
851
|
-
typeof error?.name === "string" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
852
|
-
error.name.toLowerCase() === "syntaxerror";
|
|
853
|
-
if (!isSyntaxError) {
|
|
854
|
-
return false;
|
|
855
|
-
}
|
|
856
|
-
if (!bufferedText) {
|
|
857
|
-
return false;
|
|
858
|
-
}
|
|
859
|
-
const rawMessage = typeof error?.message === "string" ? error.message : "";
|
|
860
|
-
const message = rawMessage.toLowerCase();
|
|
861
|
-
const truncationIndicators = [
|
|
862
|
-
"unexpected end of json input",
|
|
863
|
-
"unexpected end of input",
|
|
864
|
-
"unexpected end of string",
|
|
865
|
-
"unexpected eof",
|
|
866
|
-
"end of file",
|
|
867
|
-
"unterminated string",
|
|
868
|
-
"unterminated string constant"
|
|
869
|
-
];
|
|
870
|
-
if (!truncationIndicators.some((indicator) => message.includes(indicator))) {
|
|
871
|
-
return false;
|
|
872
|
-
}
|
|
873
|
-
if (bufferedText.length < MIN_TRUNCATION_LENGTH) {
|
|
874
|
-
return false;
|
|
875
|
-
}
|
|
876
|
-
return true;
|
|
877
|
-
}
|
|
878
|
-
function isAbortError(err) {
|
|
879
|
-
if (err && typeof err === "object") {
|
|
880
|
-
const e = err;
|
|
881
|
-
if (typeof e.name === "string" && e.name === "AbortError") return true;
|
|
882
|
-
if (typeof e.code === "string" && e.code.toUpperCase() === "ABORT_ERR") return true;
|
|
883
|
-
}
|
|
884
|
-
return false;
|
|
885
|
-
}
|
|
886
|
-
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).";
|
|
887
|
-
function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, contentParts) {
|
|
888
|
-
const content = contentParts && contentParts.length > 0 ? contentParts : [{ type: "text", text: messagesPrompt }];
|
|
889
|
-
const msg = {
|
|
890
|
-
type: "user",
|
|
891
|
-
message: {
|
|
892
|
-
role: "user",
|
|
893
|
-
content
|
|
894
|
-
},
|
|
895
|
-
parent_tool_use_id: null,
|
|
896
|
-
session_id: sessionId ?? ""
|
|
897
|
-
};
|
|
898
|
-
return {
|
|
899
|
-
async *[Symbol.asyncIterator]() {
|
|
900
|
-
yield msg;
|
|
901
|
-
await outputStreamEnded;
|
|
902
|
-
}
|
|
903
|
-
};
|
|
904
|
-
}
|
|
905
|
-
var modelMap = {
|
|
906
|
-
opus: "opus",
|
|
907
|
-
sonnet: "sonnet",
|
|
908
|
-
haiku: "haiku"
|
|
909
|
-
};
|
|
910
|
-
var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
911
|
-
specificationVersion = "v2";
|
|
912
|
-
defaultObjectGenerationMode = "json";
|
|
913
|
-
supportsImageUrls = false;
|
|
914
|
-
supportedUrls = {};
|
|
915
|
-
supportsStructuredOutputs = false;
|
|
916
|
-
// Fallback/magic string constants
|
|
917
|
-
static UNKNOWN_TOOL_NAME = "unknown-tool";
|
|
918
|
-
// Tool input safety limits
|
|
919
|
-
static MAX_TOOL_INPUT_SIZE = 1048576;
|
|
920
|
-
// 1MB hard limit
|
|
921
|
-
static MAX_TOOL_INPUT_WARN = 102400;
|
|
922
|
-
// 100KB warning threshold
|
|
923
|
-
static MAX_DELTA_CALC_SIZE = 1e4;
|
|
924
|
-
// 10KB delta computation threshold
|
|
925
|
-
modelId;
|
|
926
|
-
settings;
|
|
927
|
-
sessionId;
|
|
928
|
-
modelValidationWarning;
|
|
929
|
-
settingsValidationWarnings;
|
|
930
|
-
logger;
|
|
931
|
-
queryFn;
|
|
932
|
-
constructor(options) {
|
|
933
|
-
this.modelId = options.id;
|
|
934
|
-
this.settings = options.settings ?? {};
|
|
935
|
-
this.settingsValidationWarnings = options.settingsValidationWarnings ?? [];
|
|
936
|
-
this.queryFn = this.settings.queryFunction ?? query;
|
|
937
|
-
const baseLogger = getLogger(this.settings.logger);
|
|
938
|
-
this.logger = createVerboseLogger(baseLogger, this.settings.verbose ?? false);
|
|
939
|
-
if (!this.modelId || typeof this.modelId !== "string" || this.modelId.trim() === "") {
|
|
940
|
-
throw new NoSuchModelError({
|
|
941
|
-
modelId: this.modelId,
|
|
942
|
-
modelType: "languageModel"
|
|
943
|
-
});
|
|
944
|
-
}
|
|
945
|
-
this.modelValidationWarning = validateModelId(this.modelId);
|
|
946
|
-
if (this.modelValidationWarning) {
|
|
947
|
-
this.logger.warn(`Claude Code Model: ${this.modelValidationWarning}`);
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
get provider() {
|
|
951
|
-
return "claude-code";
|
|
952
|
-
}
|
|
953
|
-
getModel() {
|
|
954
|
-
const mapped = modelMap[this.modelId];
|
|
955
|
-
return mapped ?? this.modelId;
|
|
956
|
-
}
|
|
957
|
-
extractToolUses(content) {
|
|
958
|
-
if (!Array.isArray(content)) {
|
|
959
|
-
return [];
|
|
960
|
-
}
|
|
961
|
-
return content.filter(
|
|
962
|
-
(item) => typeof item === "object" && item !== null && "type" in item && item.type === "tool_use"
|
|
963
|
-
).map((item) => {
|
|
964
|
-
const { id, name, input } = item;
|
|
965
|
-
return {
|
|
966
|
-
id: typeof id === "string" && id.length > 0 ? id : generateId(),
|
|
967
|
-
name: typeof name === "string" && name.length > 0 ? name : _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME,
|
|
968
|
-
input
|
|
969
|
-
};
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
extractToolResults(content) {
|
|
973
|
-
if (!Array.isArray(content)) {
|
|
974
|
-
return [];
|
|
975
|
-
}
|
|
976
|
-
return content.filter(
|
|
977
|
-
(item) => typeof item === "object" && item !== null && "type" in item && item.type === "tool_result"
|
|
978
|
-
).map((item) => {
|
|
979
|
-
const { tool_use_id, content: content2, is_error, name } = item;
|
|
980
|
-
return {
|
|
981
|
-
id: typeof tool_use_id === "string" && tool_use_id.length > 0 ? tool_use_id : generateId(),
|
|
982
|
-
name: typeof name === "string" && name.length > 0 ? name : void 0,
|
|
983
|
-
result: content2,
|
|
984
|
-
isError: Boolean(is_error)
|
|
985
|
-
};
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
|
-
extractToolErrors(content) {
|
|
989
|
-
if (!Array.isArray(content)) {
|
|
990
|
-
return [];
|
|
991
|
-
}
|
|
992
|
-
return content.filter(
|
|
993
|
-
(item) => typeof item === "object" && item !== null && "type" in item && item.type === "tool_error"
|
|
994
|
-
).map((item) => {
|
|
995
|
-
const { tool_use_id, error, name } = item;
|
|
996
|
-
return {
|
|
997
|
-
id: typeof tool_use_id === "string" && tool_use_id.length > 0 ? tool_use_id : generateId(),
|
|
998
|
-
name: typeof name === "string" && name.length > 0 ? name : void 0,
|
|
999
|
-
error
|
|
1000
|
-
};
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
serializeToolInput(input) {
|
|
1004
|
-
if (typeof input === "string") {
|
|
1005
|
-
return this.checkInputSize(input);
|
|
1006
|
-
}
|
|
1007
|
-
if (input === void 0) {
|
|
1008
|
-
return "";
|
|
1009
|
-
}
|
|
1010
|
-
try {
|
|
1011
|
-
const serialized = JSON.stringify(input);
|
|
1012
|
-
return this.checkInputSize(serialized);
|
|
1013
|
-
} catch {
|
|
1014
|
-
const fallback = String(input);
|
|
1015
|
-
return this.checkInputSize(fallback);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
checkInputSize(str) {
|
|
1019
|
-
const length = str.length;
|
|
1020
|
-
if (length > _ClaudeCodeLanguageModel.MAX_TOOL_INPUT_SIZE) {
|
|
1021
|
-
throw new Error(
|
|
1022
|
-
`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.`
|
|
1023
|
-
);
|
|
1024
|
-
}
|
|
1025
|
-
if (length > _ClaudeCodeLanguageModel.MAX_TOOL_INPUT_WARN) {
|
|
1026
|
-
this.logger.warn(
|
|
1027
|
-
`[claude-code] Large tool input detected: ${length} bytes. Performance may be impacted. Consider chunking or reducing input size.`
|
|
1028
|
-
);
|
|
1029
|
-
}
|
|
1030
|
-
return str;
|
|
1031
|
-
}
|
|
1032
|
-
normalizeToolResult(result) {
|
|
1033
|
-
if (typeof result === "string") {
|
|
1034
|
-
try {
|
|
1035
|
-
return JSON.parse(result);
|
|
1036
|
-
} catch {
|
|
1037
|
-
return result;
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
return result;
|
|
1041
|
-
}
|
|
1042
|
-
generateAllWarnings(options, prompt) {
|
|
1043
|
-
const warnings = [];
|
|
1044
|
-
const unsupportedParams = [];
|
|
1045
|
-
if (options.temperature !== void 0) unsupportedParams.push("temperature");
|
|
1046
|
-
if (options.topP !== void 0) unsupportedParams.push("topP");
|
|
1047
|
-
if (options.topK !== void 0) unsupportedParams.push("topK");
|
|
1048
|
-
if (options.presencePenalty !== void 0) unsupportedParams.push("presencePenalty");
|
|
1049
|
-
if (options.frequencyPenalty !== void 0) unsupportedParams.push("frequencyPenalty");
|
|
1050
|
-
if (options.stopSequences !== void 0 && options.stopSequences.length > 0)
|
|
1051
|
-
unsupportedParams.push("stopSequences");
|
|
1052
|
-
if (options.seed !== void 0) unsupportedParams.push("seed");
|
|
1053
|
-
if (unsupportedParams.length > 0) {
|
|
1054
|
-
for (const param of unsupportedParams) {
|
|
1055
|
-
warnings.push({
|
|
1056
|
-
type: "unsupported-setting",
|
|
1057
|
-
setting: param,
|
|
1058
|
-
details: `Claude Code SDK does not support the ${param} parameter. It will be ignored.`
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
if (this.modelValidationWarning) {
|
|
1063
|
-
warnings.push({
|
|
1064
|
-
type: "other",
|
|
1065
|
-
message: this.modelValidationWarning
|
|
1066
|
-
});
|
|
1067
|
-
}
|
|
1068
|
-
this.settingsValidationWarnings.forEach((warning) => {
|
|
1069
|
-
warnings.push({
|
|
1070
|
-
type: "other",
|
|
1071
|
-
message: warning
|
|
1072
|
-
});
|
|
1073
|
-
});
|
|
1074
|
-
const promptWarning = validatePrompt(prompt);
|
|
1075
|
-
if (promptWarning) {
|
|
1076
|
-
warnings.push({
|
|
1077
|
-
type: "other",
|
|
1078
|
-
message: promptWarning
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
return warnings;
|
|
1082
|
-
}
|
|
1083
|
-
handleJsonExtraction(text, warnings) {
|
|
1084
|
-
const extracted = extractJson(text);
|
|
1085
|
-
const validation = this.validateJsonExtraction(text, extracted);
|
|
1086
|
-
if (!validation.valid && validation.warning) {
|
|
1087
|
-
warnings.push(validation.warning);
|
|
1088
|
-
}
|
|
1089
|
-
return extracted;
|
|
1090
|
-
}
|
|
1091
|
-
createQueryOptions(abortController) {
|
|
1092
|
-
const opts = {
|
|
1093
|
-
model: this.getModel(),
|
|
1094
|
-
abortController,
|
|
1095
|
-
resume: this.settings.resume ?? this.sessionId,
|
|
1096
|
-
pathToClaudeCodeExecutable: this.settings.pathToClaudeCodeExecutable,
|
|
1097
|
-
maxTurns: this.settings.maxTurns,
|
|
1098
|
-
maxThinkingTokens: this.settings.maxThinkingTokens,
|
|
1099
|
-
cwd: this.settings.cwd,
|
|
1100
|
-
executable: this.settings.executable,
|
|
1101
|
-
executableArgs: this.settings.executableArgs,
|
|
1102
|
-
permissionMode: this.settings.permissionMode,
|
|
1103
|
-
permissionPromptToolName: this.settings.permissionPromptToolName,
|
|
1104
|
-
continue: this.settings.continue,
|
|
1105
|
-
allowedTools: this.settings.allowedTools,
|
|
1106
|
-
disallowedTools: this.settings.disallowedTools,
|
|
1107
|
-
mcpServers: this.settings.mcpServers,
|
|
1108
|
-
canUseTool: this.settings.canUseTool
|
|
1109
|
-
};
|
|
1110
|
-
if (this.settings.systemPrompt !== void 0) {
|
|
1111
|
-
opts.systemPrompt = this.settings.systemPrompt;
|
|
1112
|
-
} else if (this.settings.customSystemPrompt !== void 0) {
|
|
1113
|
-
this.logger.warn(
|
|
1114
|
-
"[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? })."
|
|
1115
|
-
);
|
|
1116
|
-
opts.systemPrompt = this.settings.customSystemPrompt;
|
|
1117
|
-
} else if (this.settings.appendSystemPrompt !== void 0) {
|
|
1118
|
-
this.logger.warn(
|
|
1119
|
-
"[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."
|
|
1120
|
-
);
|
|
1121
|
-
opts.systemPrompt = {
|
|
1122
|
-
type: "preset",
|
|
1123
|
-
preset: "claude_code",
|
|
1124
|
-
append: this.settings.appendSystemPrompt
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
if (this.settings.settingSources !== void 0) {
|
|
1128
|
-
opts.settingSources = this.settings.settingSources;
|
|
1129
|
-
}
|
|
1130
|
-
if (this.settings.additionalDirectories !== void 0) {
|
|
1131
|
-
opts.additionalDirectories = this.settings.additionalDirectories;
|
|
1132
|
-
}
|
|
1133
|
-
if (this.settings.agents !== void 0) {
|
|
1134
|
-
opts.agents = this.settings.agents;
|
|
1135
|
-
}
|
|
1136
|
-
if (this.settings.includePartialMessages !== void 0) {
|
|
1137
|
-
opts.includePartialMessages = this.settings.includePartialMessages;
|
|
1138
|
-
}
|
|
1139
|
-
if (this.settings.fallbackModel !== void 0) {
|
|
1140
|
-
opts.fallbackModel = this.settings.fallbackModel;
|
|
1141
|
-
}
|
|
1142
|
-
if (this.settings.forkSession !== void 0) {
|
|
1143
|
-
opts.forkSession = this.settings.forkSession;
|
|
1144
|
-
}
|
|
1145
|
-
if (this.settings.stderr !== void 0) {
|
|
1146
|
-
opts.stderr = this.settings.stderr;
|
|
1147
|
-
}
|
|
1148
|
-
if (this.settings.strictMcpConfig !== void 0) {
|
|
1149
|
-
opts.strictMcpConfig = this.settings.strictMcpConfig;
|
|
1150
|
-
}
|
|
1151
|
-
if (this.settings.extraArgs !== void 0) {
|
|
1152
|
-
opts.extraArgs = this.settings.extraArgs;
|
|
1153
|
-
}
|
|
1154
|
-
if (this.settings.hooks) {
|
|
1155
|
-
opts.hooks = this.settings.hooks;
|
|
1156
|
-
}
|
|
1157
|
-
if (this.settings.env !== void 0) {
|
|
1158
|
-
opts.env = { ...process.env, ...this.settings.env };
|
|
1159
|
-
}
|
|
1160
|
-
return opts;
|
|
1161
|
-
}
|
|
1162
|
-
handleClaudeCodeError(error, messagesPrompt) {
|
|
1163
|
-
if (isAbortError(error)) {
|
|
1164
|
-
throw error;
|
|
1165
|
-
}
|
|
1166
|
-
const isErrorWithMessage = (err) => {
|
|
1167
|
-
return typeof err === "object" && err !== null && "message" in err;
|
|
1168
|
-
};
|
|
1169
|
-
const isErrorWithCode = (err) => {
|
|
1170
|
-
return typeof err === "object" && err !== null;
|
|
1171
|
-
};
|
|
1172
|
-
const authErrorPatterns = [
|
|
1173
|
-
"not logged in",
|
|
1174
|
-
"authentication",
|
|
1175
|
-
"unauthorized",
|
|
1176
|
-
"auth failed",
|
|
1177
|
-
"please login",
|
|
1178
|
-
"claude login"
|
|
1179
|
-
];
|
|
1180
|
-
const errorMessage = isErrorWithMessage(error) && error.message ? error.message.toLowerCase() : "";
|
|
1181
|
-
const exitCode = isErrorWithCode(error) && typeof error.exitCode === "number" ? error.exitCode : void 0;
|
|
1182
|
-
const isAuthError = authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
|
|
1183
|
-
if (isAuthError) {
|
|
1184
|
-
return createAuthenticationError({
|
|
1185
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
|
|
1186
|
-
});
|
|
1187
|
-
}
|
|
1188
|
-
const errorCode = isErrorWithCode(error) && typeof error.code === "string" ? error.code : "";
|
|
1189
|
-
if (errorCode === "ETIMEDOUT" || errorMessage.includes("timeout")) {
|
|
1190
|
-
return createTimeoutError({
|
|
1191
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Request timed out",
|
|
1192
|
-
promptExcerpt: messagesPrompt.substring(0, 200)
|
|
1193
|
-
// Don't specify timeoutMs since we don't know the actual timeout value
|
|
1194
|
-
// It's controlled by the consumer via AbortSignal
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
|
|
1198
|
-
return createAPICallError({
|
|
1199
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
|
|
1200
|
-
code: errorCode || void 0,
|
|
1201
|
-
exitCode,
|
|
1202
|
-
stderr: isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0,
|
|
1203
|
-
promptExcerpt: messagesPrompt.substring(0, 200),
|
|
1204
|
-
isRetryable
|
|
1205
|
-
});
|
|
1206
|
-
}
|
|
1207
|
-
setSessionId(sessionId) {
|
|
1208
|
-
this.sessionId = sessionId;
|
|
1209
|
-
const warning = validateSessionId(sessionId);
|
|
1210
|
-
if (warning) {
|
|
1211
|
-
this.logger.warn(`Claude Code Session: ${warning}`);
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
validateJsonExtraction(originalText, extractedJson) {
|
|
1215
|
-
if (extractedJson === originalText) {
|
|
1216
|
-
return {
|
|
1217
|
-
valid: false,
|
|
1218
|
-
warning: {
|
|
1219
|
-
type: "other",
|
|
1220
|
-
message: "JSON extraction from model response may be incomplete or modified. The model may not have returned valid JSON."
|
|
1221
|
-
}
|
|
1222
|
-
};
|
|
1223
|
-
}
|
|
1224
|
-
try {
|
|
1225
|
-
JSON.parse(extractedJson);
|
|
1226
|
-
return { valid: true };
|
|
1227
|
-
} catch {
|
|
1228
|
-
return {
|
|
1229
|
-
valid: false,
|
|
1230
|
-
warning: {
|
|
1231
|
-
type: "other",
|
|
1232
|
-
message: "JSON extraction resulted in invalid JSON. The response may be malformed."
|
|
1233
|
-
}
|
|
1234
|
-
};
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
async doGenerate(options) {
|
|
1238
|
-
this.logger.debug(`[claude-code] Starting doGenerate request with model: ${this.modelId}`);
|
|
1239
|
-
const mode = options.responseFormat?.type === "json" ? { type: "object-json" } : { type: "regular" };
|
|
1240
|
-
this.logger.debug(
|
|
1241
|
-
`[claude-code] Request mode: ${mode.type}, response format: ${options.responseFormat?.type ?? "none"}`
|
|
1242
|
-
);
|
|
1243
|
-
const {
|
|
1244
|
-
messagesPrompt,
|
|
1245
|
-
warnings: messageWarnings,
|
|
1246
|
-
streamingContentParts,
|
|
1247
|
-
hasImageParts
|
|
1248
|
-
} = convertToClaudeCodeMessages(
|
|
1249
|
-
options.prompt,
|
|
1250
|
-
mode,
|
|
1251
|
-
options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0
|
|
1252
|
-
);
|
|
1253
|
-
this.logger.debug(
|
|
1254
|
-
`[claude-code] Converted ${options.prompt.length} messages, hasImageParts: ${hasImageParts}`
|
|
1255
|
-
);
|
|
1256
|
-
const abortController = new AbortController();
|
|
1257
|
-
let abortListener;
|
|
1258
|
-
if (options.abortSignal?.aborted) {
|
|
1259
|
-
abortController.abort(options.abortSignal.reason);
|
|
1260
|
-
} else if (options.abortSignal) {
|
|
1261
|
-
abortListener = () => abortController.abort(options.abortSignal?.reason);
|
|
1262
|
-
options.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
1263
|
-
}
|
|
1264
|
-
const queryOptions = this.createQueryOptions(abortController);
|
|
1265
|
-
let text = "";
|
|
1266
|
-
let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1267
|
-
let finishReason = "stop";
|
|
1268
|
-
let wasTruncated = false;
|
|
1269
|
-
let costUsd;
|
|
1270
|
-
let durationMs;
|
|
1271
|
-
let rawUsage;
|
|
1272
|
-
const warnings = this.generateAllWarnings(
|
|
1273
|
-
options,
|
|
1274
|
-
messagesPrompt
|
|
1275
|
-
);
|
|
1276
|
-
if (messageWarnings) {
|
|
1277
|
-
messageWarnings.forEach((warning) => {
|
|
1278
|
-
warnings.push({
|
|
1279
|
-
type: "other",
|
|
1280
|
-
message: warning
|
|
1281
|
-
});
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
const modeSetting = this.settings.streamingInput ?? "auto";
|
|
1285
|
-
const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
|
|
1286
|
-
if (!wantsStreamInput && hasImageParts) {
|
|
1287
|
-
warnings.push({
|
|
1288
|
-
type: "other",
|
|
1289
|
-
message: STREAMING_FEATURE_WARNING
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
let done = () => {
|
|
1293
|
-
};
|
|
1294
|
-
const outputStreamEnded = new Promise((resolve) => {
|
|
1295
|
-
done = () => resolve(void 0);
|
|
1296
|
-
});
|
|
1297
|
-
try {
|
|
1298
|
-
if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
|
|
1299
|
-
throw new Error(
|
|
1300
|
-
"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."
|
|
1301
|
-
);
|
|
1302
|
-
}
|
|
1303
|
-
const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
|
|
1304
|
-
messagesPrompt,
|
|
1305
|
-
outputStreamEnded,
|
|
1306
|
-
this.settings.resume ?? this.sessionId,
|
|
1307
|
-
streamingContentParts
|
|
1308
|
-
) : messagesPrompt;
|
|
1309
|
-
this.logger.debug(
|
|
1310
|
-
`[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
|
|
1311
|
-
);
|
|
1312
|
-
const response = this.queryFn({
|
|
1313
|
-
prompt: sdkPrompt,
|
|
1314
|
-
options: queryOptions
|
|
1315
|
-
});
|
|
1316
|
-
for await (const message of response) {
|
|
1317
|
-
this.logger.debug(`[claude-code] Received message type: ${message.type}`);
|
|
1318
|
-
if (message.type === "assistant") {
|
|
1319
|
-
text += message.message.content.map((c) => c.type === "text" ? c.text : "").join("");
|
|
1320
|
-
} else if (message.type === "result") {
|
|
1321
|
-
done();
|
|
1322
|
-
this.setSessionId(message.session_id);
|
|
1323
|
-
costUsd = message.total_cost_usd;
|
|
1324
|
-
durationMs = message.duration_ms;
|
|
1325
|
-
this.logger.info(
|
|
1326
|
-
`[claude-code] Request completed - Session: ${message.session_id}, Cost: $${costUsd?.toFixed(4) ?? "N/A"}, Duration: ${durationMs ?? "N/A"}ms`
|
|
1327
|
-
);
|
|
1328
|
-
if ("usage" in message) {
|
|
1329
|
-
rawUsage = message.usage;
|
|
1330
|
-
usage = {
|
|
1331
|
-
inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
|
|
1332
|
-
outputTokens: message.usage.output_tokens ?? 0,
|
|
1333
|
-
totalTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0) + (message.usage.output_tokens ?? 0)
|
|
1334
|
-
};
|
|
1335
|
-
this.logger.debug(
|
|
1336
|
-
`[claude-code] Token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
|
|
1337
|
-
);
|
|
1338
|
-
}
|
|
1339
|
-
finishReason = mapClaudeCodeFinishReason(message.subtype);
|
|
1340
|
-
this.logger.debug(`[claude-code] Finish reason: ${finishReason}`);
|
|
1341
|
-
} else if (message.type === "system" && message.subtype === "init") {
|
|
1342
|
-
this.setSessionId(message.session_id);
|
|
1343
|
-
this.logger.info(`[claude-code] Session initialized: ${message.session_id}`);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
} catch (error) {
|
|
1347
|
-
done();
|
|
1348
|
-
this.logger.debug(
|
|
1349
|
-
`[claude-code] Error during doGenerate: ${error instanceof Error ? error.message : String(error)}`
|
|
1350
|
-
);
|
|
1351
|
-
if (isAbortError(error)) {
|
|
1352
|
-
this.logger.debug("[claude-code] Request aborted by user");
|
|
1353
|
-
throw options.abortSignal?.aborted ? options.abortSignal.reason : error;
|
|
1354
|
-
}
|
|
1355
|
-
if (isClaudeCodeTruncationError(error, text)) {
|
|
1356
|
-
this.logger.warn(
|
|
1357
|
-
`[claude-code] Detected truncated response, returning ${text.length} characters of buffered text`
|
|
1358
|
-
);
|
|
1359
|
-
wasTruncated = true;
|
|
1360
|
-
finishReason = "length";
|
|
1361
|
-
warnings.push({
|
|
1362
|
-
type: "other",
|
|
1363
|
-
message: CLAUDE_CODE_TRUNCATION_WARNING
|
|
1364
|
-
});
|
|
1365
|
-
} else {
|
|
1366
|
-
throw this.handleClaudeCodeError(error, messagesPrompt);
|
|
1367
|
-
}
|
|
1368
|
-
} finally {
|
|
1369
|
-
if (options.abortSignal && abortListener) {
|
|
1370
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
if (options.responseFormat?.type === "json" && text) {
|
|
1374
|
-
text = this.handleJsonExtraction(text, warnings);
|
|
1375
|
-
}
|
|
1376
|
-
return {
|
|
1377
|
-
content: [{ type: "text", text }],
|
|
1378
|
-
usage,
|
|
1379
|
-
finishReason,
|
|
1380
|
-
warnings,
|
|
1381
|
-
response: {
|
|
1382
|
-
id: generateId(),
|
|
1383
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
1384
|
-
modelId: this.modelId
|
|
1385
|
-
},
|
|
1386
|
-
request: {
|
|
1387
|
-
body: messagesPrompt
|
|
1388
|
-
},
|
|
1389
|
-
providerMetadata: {
|
|
1390
|
-
"claude-code": {
|
|
1391
|
-
...this.sessionId !== void 0 && { sessionId: this.sessionId },
|
|
1392
|
-
...costUsd !== void 0 && { costUsd },
|
|
1393
|
-
...durationMs !== void 0 && { durationMs },
|
|
1394
|
-
...rawUsage !== void 0 && { rawUsage },
|
|
1395
|
-
...wasTruncated && { truncated: true }
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
async doStream(options) {
|
|
1401
|
-
this.logger.debug(`[claude-code] Starting doStream request with model: ${this.modelId}`);
|
|
1402
|
-
const mode = options.responseFormat?.type === "json" ? { type: "object-json" } : { type: "regular" };
|
|
1403
|
-
this.logger.debug(
|
|
1404
|
-
`[claude-code] Stream mode: ${mode.type}, response format: ${options.responseFormat?.type ?? "none"}`
|
|
1405
|
-
);
|
|
1406
|
-
const {
|
|
1407
|
-
messagesPrompt,
|
|
1408
|
-
warnings: messageWarnings,
|
|
1409
|
-
streamingContentParts,
|
|
1410
|
-
hasImageParts
|
|
1411
|
-
} = convertToClaudeCodeMessages(
|
|
1412
|
-
options.prompt,
|
|
1413
|
-
mode,
|
|
1414
|
-
options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0
|
|
1415
|
-
);
|
|
1416
|
-
this.logger.debug(
|
|
1417
|
-
`[claude-code] Converted ${options.prompt.length} messages for streaming, hasImageParts: ${hasImageParts}`
|
|
1418
|
-
);
|
|
1419
|
-
const abortController = new AbortController();
|
|
1420
|
-
let abortListener;
|
|
1421
|
-
if (options.abortSignal?.aborted) {
|
|
1422
|
-
abortController.abort(options.abortSignal.reason);
|
|
1423
|
-
} else if (options.abortSignal) {
|
|
1424
|
-
abortListener = () => abortController.abort(options.abortSignal?.reason);
|
|
1425
|
-
options.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
1426
|
-
}
|
|
1427
|
-
const queryOptions = this.createQueryOptions(abortController);
|
|
1428
|
-
const warnings = this.generateAllWarnings(
|
|
1429
|
-
options,
|
|
1430
|
-
messagesPrompt
|
|
1431
|
-
);
|
|
1432
|
-
if (messageWarnings) {
|
|
1433
|
-
messageWarnings.forEach((warning) => {
|
|
1434
|
-
warnings.push({
|
|
1435
|
-
type: "other",
|
|
1436
|
-
message: warning
|
|
1437
|
-
});
|
|
1438
|
-
});
|
|
1439
|
-
}
|
|
1440
|
-
const modeSetting = this.settings.streamingInput ?? "auto";
|
|
1441
|
-
const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
|
|
1442
|
-
if (!wantsStreamInput && hasImageParts) {
|
|
1443
|
-
warnings.push({
|
|
1444
|
-
type: "other",
|
|
1445
|
-
message: STREAMING_FEATURE_WARNING
|
|
1446
|
-
});
|
|
1447
|
-
}
|
|
1448
|
-
const stream = new ReadableStream({
|
|
1449
|
-
start: async (controller) => {
|
|
1450
|
-
let done = () => {
|
|
1451
|
-
};
|
|
1452
|
-
const outputStreamEnded = new Promise((resolve) => {
|
|
1453
|
-
done = () => resolve(void 0);
|
|
1454
|
-
});
|
|
1455
|
-
const toolStates = /* @__PURE__ */ new Map();
|
|
1456
|
-
const streamWarnings = [];
|
|
1457
|
-
const closeToolInput = (toolId, state) => {
|
|
1458
|
-
if (!state.inputClosed && state.inputStarted) {
|
|
1459
|
-
controller.enqueue({
|
|
1460
|
-
type: "tool-input-end",
|
|
1461
|
-
id: toolId
|
|
1462
|
-
});
|
|
1463
|
-
state.inputClosed = true;
|
|
1464
|
-
}
|
|
1465
|
-
};
|
|
1466
|
-
const emitToolCall = (toolId, state) => {
|
|
1467
|
-
if (state.callEmitted) {
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
closeToolInput(toolId, state);
|
|
1471
|
-
controller.enqueue({
|
|
1472
|
-
type: "tool-call",
|
|
1473
|
-
toolCallId: toolId,
|
|
1474
|
-
toolName: state.name,
|
|
1475
|
-
input: state.lastSerializedInput ?? "",
|
|
1476
|
-
providerExecuted: true,
|
|
1477
|
-
dynamic: true,
|
|
1478
|
-
// V3 field: indicates tool is provider-defined (not in user's tools map)
|
|
1479
|
-
providerMetadata: {
|
|
1480
|
-
"claude-code": {
|
|
1481
|
-
// rawInput preserves the original serialized format before AI SDK normalization.
|
|
1482
|
-
// Use this if you need the exact string sent to the Claude CLI, which may differ
|
|
1483
|
-
// from the `input` field after AI SDK processing.
|
|
1484
|
-
rawInput: state.lastSerializedInput ?? ""
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
});
|
|
1488
|
-
state.callEmitted = true;
|
|
1489
|
-
};
|
|
1490
|
-
const finalizeToolCalls = () => {
|
|
1491
|
-
for (const [toolId, state] of toolStates) {
|
|
1492
|
-
emitToolCall(toolId, state);
|
|
1493
|
-
}
|
|
1494
|
-
toolStates.clear();
|
|
1495
|
-
};
|
|
1496
|
-
let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1497
|
-
let accumulatedText = "";
|
|
1498
|
-
let textPartId;
|
|
1499
|
-
try {
|
|
1500
|
-
controller.enqueue({ type: "stream-start", warnings });
|
|
1501
|
-
if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
|
|
1502
|
-
throw new Error(
|
|
1503
|
-
"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."
|
|
1504
|
-
);
|
|
1505
|
-
}
|
|
1506
|
-
const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
|
|
1507
|
-
messagesPrompt,
|
|
1508
|
-
outputStreamEnded,
|
|
1509
|
-
this.settings.resume ?? this.sessionId,
|
|
1510
|
-
streamingContentParts
|
|
1511
|
-
) : messagesPrompt;
|
|
1512
|
-
this.logger.debug(
|
|
1513
|
-
`[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
|
|
1514
|
-
);
|
|
1515
|
-
const response = this.queryFn({
|
|
1516
|
-
prompt: sdkPrompt,
|
|
1517
|
-
options: queryOptions
|
|
1518
|
-
});
|
|
1519
|
-
for await (const message of response) {
|
|
1520
|
-
this.logger.debug(`[claude-code] Stream received message type: ${message.type}`);
|
|
1521
|
-
if (message.type === "assistant") {
|
|
1522
|
-
if (!message.message?.content) {
|
|
1523
|
-
this.logger.warn(
|
|
1524
|
-
`[claude-code] Unexpected assistant message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
|
|
1525
|
-
);
|
|
1526
|
-
continue;
|
|
1527
|
-
}
|
|
1528
|
-
const content = message.message.content;
|
|
1529
|
-
for (const tool3 of this.extractToolUses(content)) {
|
|
1530
|
-
const toolId = tool3.id;
|
|
1531
|
-
let state = toolStates.get(toolId);
|
|
1532
|
-
if (!state) {
|
|
1533
|
-
state = {
|
|
1534
|
-
name: tool3.name,
|
|
1535
|
-
inputStarted: false,
|
|
1536
|
-
inputClosed: false,
|
|
1537
|
-
callEmitted: false
|
|
1538
|
-
};
|
|
1539
|
-
toolStates.set(toolId, state);
|
|
1540
|
-
this.logger.debug(
|
|
1541
|
-
`[claude-code] New tool use detected - Tool: ${tool3.name}, ID: ${toolId}`
|
|
1542
|
-
);
|
|
1543
|
-
}
|
|
1544
|
-
state.name = tool3.name;
|
|
1545
|
-
if (!state.inputStarted) {
|
|
1546
|
-
this.logger.debug(
|
|
1547
|
-
`[claude-code] Tool input started - Tool: ${tool3.name}, ID: ${toolId}`
|
|
1548
|
-
);
|
|
1549
|
-
controller.enqueue({
|
|
1550
|
-
type: "tool-input-start",
|
|
1551
|
-
id: toolId,
|
|
1552
|
-
toolName: tool3.name,
|
|
1553
|
-
providerExecuted: true,
|
|
1554
|
-
dynamic: true
|
|
1555
|
-
// V3 field: indicates tool is provider-defined
|
|
1556
|
-
});
|
|
1557
|
-
state.inputStarted = true;
|
|
1558
|
-
}
|
|
1559
|
-
const serializedInput = this.serializeToolInput(tool3.input);
|
|
1560
|
-
if (serializedInput) {
|
|
1561
|
-
let deltaPayload = "";
|
|
1562
|
-
if (state.lastSerializedInput === void 0) {
|
|
1563
|
-
if (serializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE) {
|
|
1564
|
-
deltaPayload = serializedInput;
|
|
1565
|
-
}
|
|
1566
|
-
} else if (serializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE && state.lastSerializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE && serializedInput.startsWith(state.lastSerializedInput)) {
|
|
1567
|
-
deltaPayload = serializedInput.slice(state.lastSerializedInput.length);
|
|
1568
|
-
} else if (serializedInput !== state.lastSerializedInput) {
|
|
1569
|
-
deltaPayload = "";
|
|
1570
|
-
}
|
|
1571
|
-
if (deltaPayload) {
|
|
1572
|
-
controller.enqueue({
|
|
1573
|
-
type: "tool-input-delta",
|
|
1574
|
-
id: toolId,
|
|
1575
|
-
delta: deltaPayload
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
state.lastSerializedInput = serializedInput;
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
const text = content.map((c) => c.type === "text" ? c.text : "").join("");
|
|
1582
|
-
if (text) {
|
|
1583
|
-
accumulatedText += text;
|
|
1584
|
-
if (options.responseFormat?.type !== "json") {
|
|
1585
|
-
if (!textPartId) {
|
|
1586
|
-
textPartId = generateId();
|
|
1587
|
-
controller.enqueue({
|
|
1588
|
-
type: "text-start",
|
|
1589
|
-
id: textPartId
|
|
1590
|
-
});
|
|
1591
|
-
}
|
|
1592
|
-
controller.enqueue({
|
|
1593
|
-
type: "text-delta",
|
|
1594
|
-
id: textPartId,
|
|
1595
|
-
delta: text
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
} else if (message.type === "user") {
|
|
1600
|
-
if (!message.message?.content) {
|
|
1601
|
-
this.logger.warn(
|
|
1602
|
-
`[claude-code] Unexpected user message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
|
|
1603
|
-
);
|
|
1604
|
-
continue;
|
|
1605
|
-
}
|
|
1606
|
-
const content = message.message.content;
|
|
1607
|
-
for (const result of this.extractToolResults(content)) {
|
|
1608
|
-
let state = toolStates.get(result.id);
|
|
1609
|
-
const toolName = result.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
1610
|
-
this.logger.debug(
|
|
1611
|
-
`[claude-code] Tool result received - Tool: ${toolName}, ID: ${result.id}`
|
|
1612
|
-
);
|
|
1613
|
-
if (!state) {
|
|
1614
|
-
this.logger.warn(
|
|
1615
|
-
`[claude-code] Received tool result for unknown tool ID: ${result.id}`
|
|
1616
|
-
);
|
|
1617
|
-
state = {
|
|
1618
|
-
name: toolName,
|
|
1619
|
-
inputStarted: false,
|
|
1620
|
-
inputClosed: false,
|
|
1621
|
-
callEmitted: false
|
|
1622
|
-
};
|
|
1623
|
-
toolStates.set(result.id, state);
|
|
1624
|
-
if (!state.inputStarted) {
|
|
1625
|
-
controller.enqueue({
|
|
1626
|
-
type: "tool-input-start",
|
|
1627
|
-
id: result.id,
|
|
1628
|
-
toolName,
|
|
1629
|
-
providerExecuted: true,
|
|
1630
|
-
dynamic: true
|
|
1631
|
-
// V3 field: indicates tool is provider-defined
|
|
1632
|
-
});
|
|
1633
|
-
state.inputStarted = true;
|
|
1634
|
-
}
|
|
1635
|
-
if (!state.inputClosed) {
|
|
1636
|
-
controller.enqueue({
|
|
1637
|
-
type: "tool-input-end",
|
|
1638
|
-
id: result.id
|
|
1639
|
-
});
|
|
1640
|
-
state.inputClosed = true;
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
state.name = toolName;
|
|
1644
|
-
const normalizedResult = this.normalizeToolResult(result.result);
|
|
1645
|
-
const rawResult = typeof result.result === "string" ? result.result : (() => {
|
|
1646
|
-
try {
|
|
1647
|
-
return JSON.stringify(result.result);
|
|
1648
|
-
} catch {
|
|
1649
|
-
return String(result.result);
|
|
1650
|
-
}
|
|
1651
|
-
})();
|
|
1652
|
-
emitToolCall(result.id, state);
|
|
1653
|
-
controller.enqueue({
|
|
1654
|
-
type: "tool-result",
|
|
1655
|
-
toolCallId: result.id,
|
|
1656
|
-
toolName,
|
|
1657
|
-
result: normalizedResult,
|
|
1658
|
-
isError: result.isError,
|
|
1659
|
-
providerExecuted: true,
|
|
1660
|
-
dynamic: true,
|
|
1661
|
-
// V3 field: indicates tool is provider-defined
|
|
1662
|
-
providerMetadata: {
|
|
1663
|
-
"claude-code": {
|
|
1664
|
-
// rawResult preserves the original CLI output string before JSON parsing.
|
|
1665
|
-
// Use this when you need the exact string returned by the tool, especially
|
|
1666
|
-
// if the `result` field has been parsed/normalized and you need the original format.
|
|
1667
|
-
rawResult
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
});
|
|
1671
|
-
}
|
|
1672
|
-
for (const error of this.extractToolErrors(content)) {
|
|
1673
|
-
let state = toolStates.get(error.id);
|
|
1674
|
-
const toolName = error.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
1675
|
-
this.logger.debug(
|
|
1676
|
-
`[claude-code] Tool error received - Tool: ${toolName}, ID: ${error.id}`
|
|
1677
|
-
);
|
|
1678
|
-
if (!state) {
|
|
1679
|
-
this.logger.warn(
|
|
1680
|
-
`[claude-code] Received tool error for unknown tool ID: ${error.id}`
|
|
1681
|
-
);
|
|
1682
|
-
state = {
|
|
1683
|
-
name: toolName,
|
|
1684
|
-
inputStarted: true,
|
|
1685
|
-
inputClosed: true,
|
|
1686
|
-
callEmitted: false
|
|
1687
|
-
};
|
|
1688
|
-
toolStates.set(error.id, state);
|
|
1689
|
-
}
|
|
1690
|
-
emitToolCall(error.id, state);
|
|
1691
|
-
const rawError = typeof error.error === "string" ? error.error : typeof error.error === "object" && error.error !== null ? (() => {
|
|
1692
|
-
try {
|
|
1693
|
-
return JSON.stringify(error.error);
|
|
1694
|
-
} catch {
|
|
1695
|
-
return String(error.error);
|
|
1696
|
-
}
|
|
1697
|
-
})() : String(error.error);
|
|
1698
|
-
controller.enqueue({
|
|
1699
|
-
type: "tool-error",
|
|
1700
|
-
toolCallId: error.id,
|
|
1701
|
-
toolName,
|
|
1702
|
-
error: rawError,
|
|
1703
|
-
providerExecuted: true,
|
|
1704
|
-
dynamic: true,
|
|
1705
|
-
// V3 field: indicates tool is provider-defined
|
|
1706
|
-
providerMetadata: {
|
|
1707
|
-
"claude-code": {
|
|
1708
|
-
rawError
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
} else if (message.type === "result") {
|
|
1714
|
-
done();
|
|
1715
|
-
this.logger.info(
|
|
1716
|
-
`[claude-code] Stream completed - Session: ${message.session_id}, Cost: $${message.total_cost_usd?.toFixed(4) ?? "N/A"}, Duration: ${message.duration_ms ?? "N/A"}ms`
|
|
1717
|
-
);
|
|
1718
|
-
let rawUsage;
|
|
1719
|
-
if ("usage" in message) {
|
|
1720
|
-
rawUsage = message.usage;
|
|
1721
|
-
usage = {
|
|
1722
|
-
inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
|
|
1723
|
-
outputTokens: message.usage.output_tokens ?? 0,
|
|
1724
|
-
totalTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0) + (message.usage.output_tokens ?? 0)
|
|
1725
|
-
};
|
|
1726
|
-
this.logger.debug(
|
|
1727
|
-
`[claude-code] Stream token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
|
|
1728
|
-
);
|
|
1729
|
-
}
|
|
1730
|
-
const finishReason = mapClaudeCodeFinishReason(
|
|
1731
|
-
message.subtype
|
|
1732
|
-
);
|
|
1733
|
-
this.logger.debug(`[claude-code] Stream finish reason: ${finishReason}`);
|
|
1734
|
-
this.setSessionId(message.session_id);
|
|
1735
|
-
if (options.responseFormat?.type === "json" && accumulatedText) {
|
|
1736
|
-
const extractedJson = this.handleJsonExtraction(accumulatedText, streamWarnings);
|
|
1737
|
-
const jsonTextId = generateId();
|
|
1738
|
-
controller.enqueue({
|
|
1739
|
-
type: "text-start",
|
|
1740
|
-
id: jsonTextId
|
|
1741
|
-
});
|
|
1742
|
-
controller.enqueue({
|
|
1743
|
-
type: "text-delta",
|
|
1744
|
-
id: jsonTextId,
|
|
1745
|
-
delta: extractedJson
|
|
1746
|
-
});
|
|
1747
|
-
controller.enqueue({
|
|
1748
|
-
type: "text-end",
|
|
1749
|
-
id: jsonTextId
|
|
1750
|
-
});
|
|
1751
|
-
} else if (textPartId) {
|
|
1752
|
-
controller.enqueue({
|
|
1753
|
-
type: "text-end",
|
|
1754
|
-
id: textPartId
|
|
1755
|
-
});
|
|
1756
|
-
}
|
|
1757
|
-
finalizeToolCalls();
|
|
1758
|
-
const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
|
|
1759
|
-
controller.enqueue({
|
|
1760
|
-
type: "finish",
|
|
1761
|
-
finishReason,
|
|
1762
|
-
usage,
|
|
1763
|
-
providerMetadata: {
|
|
1764
|
-
"claude-code": {
|
|
1765
|
-
sessionId: message.session_id,
|
|
1766
|
-
...message.total_cost_usd !== void 0 && {
|
|
1767
|
-
costUsd: message.total_cost_usd
|
|
1768
|
-
},
|
|
1769
|
-
...message.duration_ms !== void 0 && { durationMs: message.duration_ms },
|
|
1770
|
-
...rawUsage !== void 0 && { rawUsage },
|
|
1771
|
-
// JSON validation warnings are collected during streaming and included
|
|
1772
|
-
// in providerMetadata since the AI SDK's finish event doesn't support
|
|
1773
|
-
// a top-level warnings field (unlike stream-start which was already emitted)
|
|
1774
|
-
...streamWarnings.length > 0 && {
|
|
1775
|
-
warnings: warningsJson
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
});
|
|
1780
|
-
} else if (message.type === "system" && message.subtype === "init") {
|
|
1781
|
-
this.setSessionId(message.session_id);
|
|
1782
|
-
this.logger.info(`[claude-code] Stream session initialized: ${message.session_id}`);
|
|
1783
|
-
controller.enqueue({
|
|
1784
|
-
type: "response-metadata",
|
|
1785
|
-
id: message.session_id,
|
|
1786
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
1787
|
-
modelId: this.modelId
|
|
1788
|
-
});
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
finalizeToolCalls();
|
|
1792
|
-
this.logger.debug("[claude-code] Stream finalized, closing stream");
|
|
1793
|
-
controller.close();
|
|
1794
|
-
} catch (error) {
|
|
1795
|
-
done();
|
|
1796
|
-
this.logger.debug(
|
|
1797
|
-
`[claude-code] Error during doStream: ${error instanceof Error ? error.message : String(error)}`
|
|
1798
|
-
);
|
|
1799
|
-
if (isClaudeCodeTruncationError(error, accumulatedText)) {
|
|
1800
|
-
this.logger.warn(
|
|
1801
|
-
`[claude-code] Detected truncated stream response, returning ${accumulatedText.length} characters of buffered text`
|
|
1802
|
-
);
|
|
1803
|
-
const truncationWarning = {
|
|
1804
|
-
type: "other",
|
|
1805
|
-
message: CLAUDE_CODE_TRUNCATION_WARNING
|
|
1806
|
-
};
|
|
1807
|
-
streamWarnings.push(truncationWarning);
|
|
1808
|
-
const emitJsonText = () => {
|
|
1809
|
-
const extractedJson = this.handleJsonExtraction(accumulatedText, streamWarnings);
|
|
1810
|
-
const jsonTextId = generateId();
|
|
1811
|
-
controller.enqueue({
|
|
1812
|
-
type: "text-start",
|
|
1813
|
-
id: jsonTextId
|
|
1814
|
-
});
|
|
1815
|
-
controller.enqueue({
|
|
1816
|
-
type: "text-delta",
|
|
1817
|
-
id: jsonTextId,
|
|
1818
|
-
delta: extractedJson
|
|
1819
|
-
});
|
|
1820
|
-
controller.enqueue({
|
|
1821
|
-
type: "text-end",
|
|
1822
|
-
id: jsonTextId
|
|
1823
|
-
});
|
|
1824
|
-
};
|
|
1825
|
-
if (options.responseFormat?.type === "json") {
|
|
1826
|
-
emitJsonText();
|
|
1827
|
-
} else if (textPartId) {
|
|
1828
|
-
controller.enqueue({
|
|
1829
|
-
type: "text-end",
|
|
1830
|
-
id: textPartId
|
|
1831
|
-
});
|
|
1832
|
-
} else if (accumulatedText) {
|
|
1833
|
-
const fallbackTextId = generateId();
|
|
1834
|
-
controller.enqueue({
|
|
1835
|
-
type: "text-start",
|
|
1836
|
-
id: fallbackTextId
|
|
1837
|
-
});
|
|
1838
|
-
controller.enqueue({
|
|
1839
|
-
type: "text-delta",
|
|
1840
|
-
id: fallbackTextId,
|
|
1841
|
-
delta: accumulatedText
|
|
1842
|
-
});
|
|
1843
|
-
controller.enqueue({
|
|
1844
|
-
type: "text-end",
|
|
1845
|
-
id: fallbackTextId
|
|
1846
|
-
});
|
|
1847
|
-
}
|
|
1848
|
-
finalizeToolCalls();
|
|
1849
|
-
const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
|
|
1850
|
-
controller.enqueue({
|
|
1851
|
-
type: "finish",
|
|
1852
|
-
finishReason: "length",
|
|
1853
|
-
usage,
|
|
1854
|
-
providerMetadata: {
|
|
1855
|
-
"claude-code": {
|
|
1856
|
-
...this.sessionId !== void 0 && { sessionId: this.sessionId },
|
|
1857
|
-
truncated: true,
|
|
1858
|
-
...streamWarnings.length > 0 && {
|
|
1859
|
-
warnings: warningsJson
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
});
|
|
1864
|
-
controller.close();
|
|
1865
|
-
return;
|
|
1866
|
-
}
|
|
1867
|
-
finalizeToolCalls();
|
|
1868
|
-
let errorToEmit;
|
|
1869
|
-
if (isAbortError(error)) {
|
|
1870
|
-
errorToEmit = options.abortSignal?.aborted ? options.abortSignal.reason : error;
|
|
1871
|
-
} else {
|
|
1872
|
-
errorToEmit = this.handleClaudeCodeError(error, messagesPrompt);
|
|
1873
|
-
}
|
|
1874
|
-
controller.enqueue({
|
|
1875
|
-
type: "error",
|
|
1876
|
-
error: errorToEmit
|
|
1877
|
-
});
|
|
1878
|
-
controller.close();
|
|
1879
|
-
} finally {
|
|
1880
|
-
if (options.abortSignal && abortListener) {
|
|
1881
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
},
|
|
1885
|
-
cancel: () => {
|
|
1886
|
-
if (options.abortSignal && abortListener) {
|
|
1887
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
});
|
|
1891
|
-
return {
|
|
1892
|
-
stream,
|
|
1893
|
-
request: {
|
|
1894
|
-
body: messagesPrompt
|
|
1895
|
-
}
|
|
1896
|
-
};
|
|
1897
|
-
}
|
|
1898
|
-
serializeWarningsForMetadata(warnings) {
|
|
1899
|
-
const result = warnings.map((w) => {
|
|
1900
|
-
const base = { type: w.type };
|
|
1901
|
-
if ("message" in w) {
|
|
1902
|
-
const m = w.message;
|
|
1903
|
-
if (m !== void 0) base.message = String(m);
|
|
1904
|
-
}
|
|
1905
|
-
if (w.type === "unsupported-setting") {
|
|
1906
|
-
const setting = w.setting;
|
|
1907
|
-
if (setting !== void 0) base.setting = String(setting);
|
|
1908
|
-
if ("details" in w) {
|
|
1909
|
-
const d = w.details;
|
|
1910
|
-
if (d !== void 0) base.details = String(d);
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
return base;
|
|
1914
|
-
});
|
|
1915
|
-
return result;
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
|
|
1919
|
-
// src/claude-code-provider.ts
|
|
1920
|
-
function createClaudeCode(options = {}) {
|
|
1921
|
-
const logger = getLogger(options.defaultSettings?.logger);
|
|
1922
|
-
if (options.defaultSettings) {
|
|
1923
|
-
const validation = validateSettings(options.defaultSettings);
|
|
1924
|
-
if (!validation.valid) {
|
|
1925
|
-
throw new Error(`Invalid default settings: ${validation.errors.join(", ")}`);
|
|
1926
|
-
}
|
|
1927
|
-
if (validation.warnings.length > 0) {
|
|
1928
|
-
validation.warnings.forEach((warning) => logger.warn(`Claude Code Provider: ${warning}`));
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
const createModel = (modelId, settings = {}) => {
|
|
1932
|
-
const mergedSettings = {
|
|
1933
|
-
...options.defaultSettings,
|
|
1934
|
-
...settings
|
|
1935
|
-
};
|
|
1936
|
-
const validation = validateSettings(mergedSettings);
|
|
1937
|
-
if (!validation.valid) {
|
|
1938
|
-
throw new Error(`Invalid settings: ${validation.errors.join(", ")}`);
|
|
1939
|
-
}
|
|
1940
|
-
return new ClaudeCodeLanguageModel({
|
|
1941
|
-
id: modelId,
|
|
1942
|
-
settings: mergedSettings,
|
|
1943
|
-
settingsValidationWarnings: validation.warnings
|
|
1944
|
-
});
|
|
1945
|
-
};
|
|
1946
|
-
const provider = function(modelId, settings) {
|
|
1947
|
-
if (new.target) {
|
|
1948
|
-
throw new Error("The Claude Code model function cannot be called with the new keyword.");
|
|
1949
|
-
}
|
|
1950
|
-
return createModel(modelId, settings);
|
|
1951
|
-
};
|
|
1952
|
-
provider.languageModel = createModel;
|
|
1953
|
-
provider.chat = createModel;
|
|
1954
|
-
provider.textEmbeddingModel = (modelId) => {
|
|
1955
|
-
throw new NoSuchModelError({
|
|
1956
|
-
modelId,
|
|
1957
|
-
modelType: "textEmbeddingModel"
|
|
1958
|
-
});
|
|
1959
|
-
};
|
|
1960
|
-
provider.imageModel = (modelId) => {
|
|
1961
|
-
throw new NoSuchModelError({
|
|
1962
|
-
modelId,
|
|
1963
|
-
modelType: "imageModel"
|
|
1964
|
-
});
|
|
1965
|
-
};
|
|
1966
|
-
return provider;
|
|
1967
|
-
}
|
|
1968
|
-
var claudeCode = createClaudeCode();
|
|
1969
|
-
|
|
1970
|
-
export { claudeCode as c };
|