@openbuilder/cli 0.50.44 → 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-BlktOjfl.js → Banner-BKC6yG6z.js} +2 -2
- package/dist/chunks/{Banner-BlktOjfl.js.map → Banner-BKC6yG6z.js.map} +1 -1
- package/dist/chunks/{cli-auth-ChCnxlFl.js → cli-auth-BgiGSBOt.js} +4 -4
- package/dist/chunks/{cli-auth-ChCnxlFl.js.map → cli-auth-BgiGSBOt.js.map} +1 -1
- package/dist/chunks/{index-oFqGtEeF.js → index-ZNRLfdj5.js} +2 -2
- package/dist/chunks/{index-oFqGtEeF.js.map → index-ZNRLfdj5.js.map} +1 -1
- package/dist/chunks/{init-BsQ3dhwf.js → init-IQRjA1g3.js} +5 -6
- package/dist/chunks/{init-BsQ3dhwf.js.map → init-IQRjA1g3.js.map} +1 -1
- package/dist/chunks/{init-tui-Dvk6Ndvl.js → init-tui-B4jfmo3U.js} +5 -6
- package/dist/chunks/{init-tui-Dvk6Ndvl.js.map → init-tui-B4jfmo3U.js.map} +1 -1
- package/dist/chunks/{login-BhtodVsj.js → login-CrIcDJpS.js} +2 -2
- package/dist/chunks/{login-BhtodVsj.js.map → login-CrIcDJpS.js.map} +1 -1
- package/dist/chunks/{logout-CDDASeuQ.js → logout-BxgiczmY.js} +2 -2
- package/dist/chunks/{logout-CDDASeuQ.js.map → logout-BxgiczmY.js.map} +1 -1
- package/dist/chunks/{main-tui-Cklcr3FX.js → main-tui-NPDVPKol.js} +7 -8
- package/dist/chunks/{main-tui-Cklcr3FX.js.map → main-tui-NPDVPKol.js.map} +1 -1
- package/dist/chunks/{port-allocator-Ct3ioni4.js → port-allocator-DuAZe2_S.js} +3 -4
- package/dist/chunks/{port-allocator-Ct3ioni4.js.map → port-allocator-DuAZe2_S.js.map} +1 -1
- package/dist/chunks/{run-wycadErJ.js → run-Yh3YjeLl.js} +10 -16
- package/dist/chunks/{run-wycadErJ.js.map → run-Yh3YjeLl.js.map} +1 -1
- package/dist/chunks/{start-CQKEEma-.js → start-B-brfyVy.js} +5 -6
- package/dist/chunks/{start-CQKEEma-.js.map → start-B-brfyVy.js.map} +1 -1
- package/dist/chunks/{theme-CktnrDZj.js → theme-DOjeB8BU.js} +13 -8
- package/dist/chunks/{theme-CktnrDZj.js.map → theme-DOjeB8BU.js.map} +1 -1
- package/dist/chunks/{use-app-Cj2bzWaw.js → use-app-DozfqdJj.js} +2 -2
- package/dist/chunks/{use-app-Cj2bzWaw.js.map → use-app-DozfqdJj.js.map} +1 -1
- package/dist/chunks/{useBuildState-pcDGDakI.js → useBuildState-DV6wurQ2.js} +2 -2
- package/dist/chunks/{useBuildState-pcDGDakI.js.map → useBuildState-DV6wurQ2.js.map} +1 -1
- package/dist/cli/index.js +7 -7
- package/dist/index.js +205 -2734
- package/dist/index.js.map +1 -1
- package/dist/instrument.js +7 -64162
- package/dist/instrument.js.map +1 -1
- package/package.json +2 -11
- package/dist/chunks/_commonjsHelpers-h-Bqc03Z.js +0 -34
- package/dist/chunks/_commonjsHelpers-h-Bqc03Z.js.map +0 -1
- package/dist/chunks/exports-ij9sv4UM.js +0 -7793
- package/dist/chunks/exports-ij9sv4UM.js.map +0 -1
- 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
package/dist/index.js
CHANGED
|
@@ -1,2004 +1,38 @@
|
|
|
1
1
|
// OpenBuilder CLI - Built with Rollup
|
|
2
|
+
import * as Sentry from '@sentry/node';
|
|
2
3
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
3
4
|
import { Codex } from '@openai/codex-sdk';
|
|
4
|
-
import { a as streamLog, f as fileLog, i as initRunnerLogger, s as setFileLoggerTuiMode, b as getLogger
|
|
5
|
+
import { a as streamLog, f as fileLog, i as initRunnerLogger, s as setFileLoggerTuiMode, b as getLogger } from './chunks/runner-logger-instance-nDWv2h2T.js';
|
|
5
6
|
import { config as config$1 } from 'dotenv';
|
|
6
|
-
import
|
|
7
|
+
import path$1, { resolve, relative, isAbsolute, dirname, join as join$1 } from 'node:path';
|
|
7
8
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import {
|
|
9
|
-
import 'zod/v4';
|
|
10
|
-
import 'zod/v3';
|
|
11
|
-
import { parse } from 'jsonc-parser';
|
|
12
|
-
import { z } from 'zod';
|
|
13
|
-
import { existsSync, mkdirSync as mkdirSync$1 } from 'fs';
|
|
14
|
-
import { existsSync as existsSync$1, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
15
10
|
import { readFile } from 'fs/promises';
|
|
16
11
|
import * as path from 'path';
|
|
17
|
-
import { join } from 'path';
|
|
18
|
-
import WebSocket$1, { WebSocketServer, WebSocket } from 'ws';
|
|
19
|
-
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
20
|
-
import pg from 'pg';
|
|
21
|
-
import { pgTable, timestamp, boolean, text, uuid, index, uniqueIndex, integer, jsonb } from 'drizzle-orm/pg-core';
|
|
22
|
-
import { sql, eq, and, desc, isNull } from 'drizzle-orm';
|
|
23
|
-
import { randomUUID, createHash } from 'crypto';
|
|
24
|
-
import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
|
25
|
-
import {
|
|
26
|
-
import
|
|
27
|
-
import { randomUUID as randomUUID$1 } from 'node:crypto';
|
|
28
|
-
import express from 'express';
|
|
29
|
-
import { spawn } from 'node:child_process';
|
|
30
|
-
import { EventEmitter } from 'node:events';
|
|
31
|
-
import { createServer, createConnection } from 'node:net';
|
|
32
|
-
import { readFile as readFile$1, rm, writeFile, readdir } from 'node:fs/promises';
|
|
33
|
-
import { simpleGit } from 'simple-git';
|
|
34
|
-
import * as os from 'os';
|
|
35
|
-
import {
|
|
36
|
-
import '
|
|
37
|
-
import '
|
|
38
|
-
import 'http';
|
|
39
|
-
import 'http-proxy';
|
|
40
|
-
import 'zlib';
|
|
41
|
-
|
|
42
|
-
// src/errors/ai-sdk-error.ts
|
|
43
|
-
var marker = "vercel.ai.error";
|
|
44
|
-
var symbol = Symbol.for(marker);
|
|
45
|
-
var _a;
|
|
46
|
-
var _AISDKError = class _AISDKError extends Error {
|
|
47
|
-
/**
|
|
48
|
-
* Creates an AI SDK Error.
|
|
49
|
-
*
|
|
50
|
-
* @param {Object} params - The parameters for creating the error.
|
|
51
|
-
* @param {string} params.name - The name of the error.
|
|
52
|
-
* @param {string} params.message - The error message.
|
|
53
|
-
* @param {unknown} [params.cause] - The underlying cause of the error.
|
|
54
|
-
*/
|
|
55
|
-
constructor({
|
|
56
|
-
name: name14,
|
|
57
|
-
message,
|
|
58
|
-
cause
|
|
59
|
-
}) {
|
|
60
|
-
super(message);
|
|
61
|
-
this[_a] = true;
|
|
62
|
-
this.name = name14;
|
|
63
|
-
this.cause = cause;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Checks if the given error is an AI SDK Error.
|
|
67
|
-
* @param {unknown} error - The error to check.
|
|
68
|
-
* @returns {boolean} True if the error is an AI SDK Error, false otherwise.
|
|
69
|
-
*/
|
|
70
|
-
static isInstance(error) {
|
|
71
|
-
return _AISDKError.hasMarker(error, marker);
|
|
72
|
-
}
|
|
73
|
-
static hasMarker(error, marker15) {
|
|
74
|
-
const markerSymbol = Symbol.for(marker15);
|
|
75
|
-
return error != null && typeof error === "object" && markerSymbol in error && typeof error[markerSymbol] === "boolean" && error[markerSymbol] === true;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
_a = symbol;
|
|
79
|
-
var AISDKError = _AISDKError;
|
|
80
|
-
|
|
81
|
-
// src/errors/api-call-error.ts
|
|
82
|
-
var name = "AI_APICallError";
|
|
83
|
-
var marker2 = `vercel.ai.error.${name}`;
|
|
84
|
-
var symbol2 = Symbol.for(marker2);
|
|
85
|
-
var _a2;
|
|
86
|
-
var APICallError = class extends AISDKError {
|
|
87
|
-
constructor({
|
|
88
|
-
message,
|
|
89
|
-
url,
|
|
90
|
-
requestBodyValues,
|
|
91
|
-
statusCode,
|
|
92
|
-
responseHeaders,
|
|
93
|
-
responseBody,
|
|
94
|
-
cause,
|
|
95
|
-
isRetryable = statusCode != null && (statusCode === 408 || // request timeout
|
|
96
|
-
statusCode === 409 || // conflict
|
|
97
|
-
statusCode === 429 || // too many requests
|
|
98
|
-
statusCode >= 500),
|
|
99
|
-
// server error
|
|
100
|
-
data
|
|
101
|
-
}) {
|
|
102
|
-
super({ name, message, cause });
|
|
103
|
-
this[_a2] = true;
|
|
104
|
-
this.url = url;
|
|
105
|
-
this.requestBodyValues = requestBodyValues;
|
|
106
|
-
this.statusCode = statusCode;
|
|
107
|
-
this.responseHeaders = responseHeaders;
|
|
108
|
-
this.responseBody = responseBody;
|
|
109
|
-
this.isRetryable = isRetryable;
|
|
110
|
-
this.data = data;
|
|
111
|
-
}
|
|
112
|
-
static isInstance(error) {
|
|
113
|
-
return AISDKError.hasMarker(error, marker2);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
_a2 = symbol2;
|
|
117
|
-
|
|
118
|
-
// src/errors/invalid-argument-error.ts
|
|
119
|
-
var name3 = "AI_InvalidArgumentError";
|
|
120
|
-
var marker4 = `vercel.ai.error.${name3}`;
|
|
121
|
-
var symbol4 = Symbol.for(marker4);
|
|
122
|
-
var _a4;
|
|
123
|
-
var InvalidArgumentError = class extends AISDKError {
|
|
124
|
-
constructor({
|
|
125
|
-
message,
|
|
126
|
-
cause,
|
|
127
|
-
argument
|
|
128
|
-
}) {
|
|
129
|
-
super({ name: name3, message, cause });
|
|
130
|
-
this[_a4] = true;
|
|
131
|
-
this.argument = argument;
|
|
132
|
-
}
|
|
133
|
-
static isInstance(error) {
|
|
134
|
-
return AISDKError.hasMarker(error, marker4);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
_a4 = symbol4;
|
|
138
|
-
|
|
139
|
-
// src/errors/load-api-key-error.ts
|
|
140
|
-
var name7 = "AI_LoadAPIKeyError";
|
|
141
|
-
var marker8 = `vercel.ai.error.${name7}`;
|
|
142
|
-
var symbol8 = Symbol.for(marker8);
|
|
143
|
-
var _a8;
|
|
144
|
-
var LoadAPIKeyError = class extends AISDKError {
|
|
145
|
-
// used in isInstance
|
|
146
|
-
constructor({ message }) {
|
|
147
|
-
super({ name: name7, message });
|
|
148
|
-
this[_a8] = true;
|
|
149
|
-
}
|
|
150
|
-
static isInstance(error) {
|
|
151
|
-
return AISDKError.hasMarker(error, marker8);
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
_a8 = symbol8;
|
|
155
|
-
|
|
156
|
-
// src/errors/no-such-model-error.ts
|
|
157
|
-
var name10 = "AI_NoSuchModelError";
|
|
158
|
-
var marker11 = `vercel.ai.error.${name10}`;
|
|
159
|
-
var symbol11 = Symbol.for(marker11);
|
|
160
|
-
var _a11;
|
|
161
|
-
var NoSuchModelError = class extends AISDKError {
|
|
162
|
-
constructor({
|
|
163
|
-
errorName = name10,
|
|
164
|
-
modelId,
|
|
165
|
-
modelType,
|
|
166
|
-
message = `No such ${modelType}: ${modelId}`
|
|
167
|
-
}) {
|
|
168
|
-
super({ name: errorName, message });
|
|
169
|
-
this[_a11] = true;
|
|
170
|
-
this.modelId = modelId;
|
|
171
|
-
this.modelType = modelType;
|
|
172
|
-
}
|
|
173
|
-
static isInstance(error) {
|
|
174
|
-
return AISDKError.hasMarker(error, marker11);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
_a11 = symbol11;
|
|
178
|
-
|
|
179
|
-
// src/combine-headers.ts
|
|
180
|
-
var createIdGenerator = ({
|
|
181
|
-
prefix,
|
|
182
|
-
size = 16,
|
|
183
|
-
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
184
|
-
separator = "-"
|
|
185
|
-
} = {}) => {
|
|
186
|
-
const generator = () => {
|
|
187
|
-
const alphabetLength = alphabet.length;
|
|
188
|
-
const chars = new Array(size);
|
|
189
|
-
for (let i = 0; i < size; i++) {
|
|
190
|
-
chars[i] = alphabet[Math.random() * alphabetLength | 0];
|
|
191
|
-
}
|
|
192
|
-
return chars.join("");
|
|
193
|
-
};
|
|
194
|
-
if (prefix == null) {
|
|
195
|
-
return generator;
|
|
196
|
-
}
|
|
197
|
-
if (alphabet.includes(separator)) {
|
|
198
|
-
throw new InvalidArgumentError({
|
|
199
|
-
argument: "separator",
|
|
200
|
-
message: `The separator "${separator}" must not be part of the alphabet "${alphabet}".`
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
return () => `${prefix}${separator}${generator()}`;
|
|
204
|
-
};
|
|
205
|
-
var generateId = createIdGenerator();
|
|
206
|
-
new Set(
|
|
207
|
-
"ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789"
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
// src/claude-code-provider.ts
|
|
211
|
-
|
|
212
|
-
// src/convert-to-claude-code-messages.ts
|
|
213
|
-
var IMAGE_URL_WARNING = "Image URLs are not supported by this provider; supply base64/data URLs.";
|
|
214
|
-
var IMAGE_CONVERSION_WARNING = "Unable to convert image content; supply base64/data URLs.";
|
|
215
|
-
function normalizeBase64(base64) {
|
|
216
|
-
return base64.replace(/\s+/g, "");
|
|
217
|
-
}
|
|
218
|
-
function isImageMimeType(mimeType) {
|
|
219
|
-
return typeof mimeType === "string" && mimeType.trim().toLowerCase().startsWith("image/");
|
|
220
|
-
}
|
|
221
|
-
function createImageContent(mediaType, data) {
|
|
222
|
-
const trimmedType = mediaType.trim();
|
|
223
|
-
const trimmedData = normalizeBase64(data.trim());
|
|
224
|
-
if (!trimmedType || !trimmedData) {
|
|
225
|
-
return void 0;
|
|
226
|
-
}
|
|
227
|
-
return {
|
|
228
|
-
type: "image",
|
|
229
|
-
source: {
|
|
230
|
-
type: "base64",
|
|
231
|
-
media_type: trimmedType,
|
|
232
|
-
data: trimmedData
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
function extractMimeType(candidate) {
|
|
237
|
-
if (typeof candidate === "string" && candidate.trim()) {
|
|
238
|
-
return candidate.trim();
|
|
239
|
-
}
|
|
240
|
-
return void 0;
|
|
241
|
-
}
|
|
242
|
-
function parseObjectImage(imageObj, fallbackMimeType) {
|
|
243
|
-
const data = typeof imageObj.data === "string" ? imageObj.data : void 0;
|
|
244
|
-
const mimeType = extractMimeType(
|
|
245
|
-
imageObj.mimeType ?? imageObj.mediaType ?? imageObj.media_type ?? fallbackMimeType
|
|
246
|
-
);
|
|
247
|
-
if (!data || !mimeType) {
|
|
248
|
-
return void 0;
|
|
249
|
-
}
|
|
250
|
-
return createImageContent(mimeType, data);
|
|
251
|
-
}
|
|
252
|
-
function parseStringImage(value, fallbackMimeType) {
|
|
253
|
-
const trimmed = value.trim();
|
|
254
|
-
if (/^https?:\/\//i.test(trimmed)) {
|
|
255
|
-
return { warning: IMAGE_URL_WARNING };
|
|
256
|
-
}
|
|
257
|
-
const dataUrlMatch = trimmed.match(/^data:([^;]+);base64,(.+)$/i);
|
|
258
|
-
if (dataUrlMatch) {
|
|
259
|
-
const [, mediaType, data] = dataUrlMatch;
|
|
260
|
-
const content = createImageContent(mediaType, data);
|
|
261
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
262
|
-
}
|
|
263
|
-
const base64Match = trimmed.match(/^base64:([^,]+),(.+)$/i);
|
|
264
|
-
if (base64Match) {
|
|
265
|
-
const [, explicitMimeType, data] = base64Match;
|
|
266
|
-
const content = createImageContent(explicitMimeType, data);
|
|
267
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
268
|
-
}
|
|
269
|
-
if (fallbackMimeType) {
|
|
270
|
-
const content = createImageContent(fallbackMimeType, trimmed);
|
|
271
|
-
if (content) {
|
|
272
|
-
return { content };
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
276
|
-
}
|
|
277
|
-
function parseImagePart(part) {
|
|
278
|
-
if (!part || typeof part !== "object") {
|
|
279
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
280
|
-
}
|
|
281
|
-
const imageValue = part.image;
|
|
282
|
-
const mimeType = extractMimeType(part.mimeType);
|
|
283
|
-
if (typeof imageValue === "string") {
|
|
284
|
-
return parseStringImage(imageValue, mimeType);
|
|
285
|
-
}
|
|
286
|
-
if (imageValue && typeof imageValue === "object") {
|
|
287
|
-
const content = parseObjectImage(imageValue, mimeType);
|
|
288
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
289
|
-
}
|
|
290
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
291
|
-
}
|
|
292
|
-
function convertBinaryToBase64(data) {
|
|
293
|
-
if (typeof Buffer !== "undefined") {
|
|
294
|
-
const buffer = data instanceof Uint8Array ? Buffer.from(data) : Buffer.from(new Uint8Array(data));
|
|
295
|
-
return buffer.toString("base64");
|
|
296
|
-
}
|
|
297
|
-
if (typeof btoa === "function") {
|
|
298
|
-
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
299
|
-
let binary = "";
|
|
300
|
-
const chunkSize = 32768;
|
|
301
|
-
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
302
|
-
const chunk = bytes.subarray(i, i + chunkSize);
|
|
303
|
-
binary += String.fromCharCode(...chunk);
|
|
304
|
-
}
|
|
305
|
-
return btoa(binary);
|
|
306
|
-
}
|
|
307
|
-
return void 0;
|
|
308
|
-
}
|
|
309
|
-
function parseFilePart(part) {
|
|
310
|
-
const mimeType = extractMimeType(part.mediaType ?? part.mimeType);
|
|
311
|
-
if (!mimeType || !isImageMimeType(mimeType)) {
|
|
312
|
-
return {};
|
|
313
|
-
}
|
|
314
|
-
const data = part.data;
|
|
315
|
-
if (typeof data === "string") {
|
|
316
|
-
const content = createImageContent(mimeType, data);
|
|
317
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
318
|
-
}
|
|
319
|
-
if (data instanceof Uint8Array || typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
|
|
320
|
-
const base64 = convertBinaryToBase64(data);
|
|
321
|
-
if (!base64) {
|
|
322
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
323
|
-
}
|
|
324
|
-
const content = createImageContent(mimeType, base64);
|
|
325
|
-
return content ? { content } : { warning: IMAGE_CONVERSION_WARNING };
|
|
326
|
-
}
|
|
327
|
-
return { warning: IMAGE_CONVERSION_WARNING };
|
|
328
|
-
}
|
|
329
|
-
function convertToClaudeCodeMessages(prompt, mode = { type: "regular" }, jsonSchema) {
|
|
330
|
-
const messages = [];
|
|
331
|
-
const warnings = [];
|
|
332
|
-
let systemPrompt;
|
|
333
|
-
const streamingSegments = [];
|
|
334
|
-
const imageMap = /* @__PURE__ */ new Map();
|
|
335
|
-
let hasImageParts = false;
|
|
336
|
-
const addSegment = (formatted) => {
|
|
337
|
-
streamingSegments.push({ formatted });
|
|
338
|
-
return streamingSegments.length - 1;
|
|
339
|
-
};
|
|
340
|
-
const addImageForSegment = (segmentIndex, content) => {
|
|
341
|
-
hasImageParts = true;
|
|
342
|
-
if (!imageMap.has(segmentIndex)) {
|
|
343
|
-
imageMap.set(segmentIndex, []);
|
|
344
|
-
}
|
|
345
|
-
imageMap.get(segmentIndex)?.push(content);
|
|
346
|
-
};
|
|
347
|
-
for (const message of prompt) {
|
|
348
|
-
switch (message.role) {
|
|
349
|
-
case "system":
|
|
350
|
-
systemPrompt = message.content;
|
|
351
|
-
if (typeof message.content === "string" && message.content.trim().length > 0) {
|
|
352
|
-
addSegment(message.content);
|
|
353
|
-
} else {
|
|
354
|
-
addSegment("");
|
|
355
|
-
}
|
|
356
|
-
break;
|
|
357
|
-
case "user":
|
|
358
|
-
if (typeof message.content === "string") {
|
|
359
|
-
messages.push(message.content);
|
|
360
|
-
addSegment(`Human: ${message.content}`);
|
|
361
|
-
} else {
|
|
362
|
-
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
363
|
-
const segmentIndex = addSegment(textParts ? `Human: ${textParts}` : "");
|
|
364
|
-
if (textParts) {
|
|
365
|
-
messages.push(textParts);
|
|
366
|
-
}
|
|
367
|
-
for (const part of message.content) {
|
|
368
|
-
if (part.type === "image") {
|
|
369
|
-
const { content, warning } = parseImagePart(part);
|
|
370
|
-
if (content) {
|
|
371
|
-
addImageForSegment(segmentIndex, content);
|
|
372
|
-
} else if (warning) {
|
|
373
|
-
warnings.push(warning);
|
|
374
|
-
}
|
|
375
|
-
} else if (part.type === "file") {
|
|
376
|
-
const { content, warning } = parseFilePart(part);
|
|
377
|
-
if (content) {
|
|
378
|
-
addImageForSegment(segmentIndex, content);
|
|
379
|
-
} else if (warning) {
|
|
380
|
-
warnings.push(warning);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
break;
|
|
386
|
-
case "assistant": {
|
|
387
|
-
let assistantContent = "";
|
|
388
|
-
if (typeof message.content === "string") {
|
|
389
|
-
assistantContent = message.content;
|
|
390
|
-
} else {
|
|
391
|
-
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
392
|
-
if (textParts) {
|
|
393
|
-
assistantContent = textParts;
|
|
394
|
-
}
|
|
395
|
-
const toolCalls = message.content.filter((part) => part.type === "tool-call");
|
|
396
|
-
if (toolCalls.length > 0) {
|
|
397
|
-
assistantContent += `
|
|
398
|
-
[Tool calls made]`;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
const formattedAssistant = `Assistant: ${assistantContent}`;
|
|
402
|
-
messages.push(formattedAssistant);
|
|
403
|
-
addSegment(formattedAssistant);
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
case "tool":
|
|
407
|
-
for (const tool3 of message.content) {
|
|
408
|
-
const resultText = tool3.output.type === "text" ? tool3.output.value : JSON.stringify(tool3.output.value);
|
|
409
|
-
const formattedToolResult = `Tool Result (${tool3.toolName}): ${resultText}`;
|
|
410
|
-
messages.push(formattedToolResult);
|
|
411
|
-
addSegment(formattedToolResult);
|
|
412
|
-
}
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
let finalPrompt = "";
|
|
417
|
-
if (systemPrompt) {
|
|
418
|
-
finalPrompt = systemPrompt;
|
|
419
|
-
}
|
|
420
|
-
if (messages.length > 0) {
|
|
421
|
-
const formattedMessages = [];
|
|
422
|
-
for (let i = 0; i < messages.length; i++) {
|
|
423
|
-
const msg = messages[i];
|
|
424
|
-
if (msg.startsWith("Assistant:") || msg.startsWith("Tool Result")) {
|
|
425
|
-
formattedMessages.push(msg);
|
|
426
|
-
} else {
|
|
427
|
-
formattedMessages.push(`Human: ${msg}`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
if (finalPrompt) {
|
|
431
|
-
const joinedMessages = formattedMessages.join("\n\n");
|
|
432
|
-
finalPrompt = joinedMessages ? `${finalPrompt}
|
|
433
|
-
|
|
434
|
-
${joinedMessages}` : finalPrompt;
|
|
435
|
-
} else {
|
|
436
|
-
finalPrompt = formattedMessages.join("\n\n");
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
let streamingParts = [];
|
|
440
|
-
const imagePartsInOrder = [];
|
|
441
|
-
const appendImagesForIndex = (index) => {
|
|
442
|
-
const images = imageMap.get(index);
|
|
443
|
-
if (!images) {
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
images.forEach((image) => {
|
|
447
|
-
streamingParts.push(image);
|
|
448
|
-
imagePartsInOrder.push(image);
|
|
449
|
-
});
|
|
450
|
-
};
|
|
451
|
-
if (streamingSegments.length > 0) {
|
|
452
|
-
let accumulatedText = "";
|
|
453
|
-
let emittedText = false;
|
|
454
|
-
const flushText = () => {
|
|
455
|
-
if (!accumulatedText) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
streamingParts.push({ type: "text", text: accumulatedText });
|
|
459
|
-
accumulatedText = "";
|
|
460
|
-
emittedText = true;
|
|
461
|
-
};
|
|
462
|
-
streamingSegments.forEach((segment, index) => {
|
|
463
|
-
const segmentText = segment.formatted;
|
|
464
|
-
if (segmentText) {
|
|
465
|
-
if (!accumulatedText) {
|
|
466
|
-
accumulatedText = emittedText ? `
|
|
467
|
-
|
|
468
|
-
${segmentText}` : segmentText;
|
|
469
|
-
} else {
|
|
470
|
-
accumulatedText += `
|
|
471
|
-
|
|
472
|
-
${segmentText}`;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
if (imageMap.has(index)) {
|
|
476
|
-
flushText();
|
|
477
|
-
appendImagesForIndex(index);
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
flushText();
|
|
481
|
-
}
|
|
482
|
-
if (mode?.type === "object-json" && jsonSchema) {
|
|
483
|
-
const schemaStr = JSON.stringify(jsonSchema, null, 2);
|
|
484
|
-
finalPrompt = `CRITICAL: You MUST respond with ONLY a JSON object. NO other text, NO explanations, NO questions.
|
|
485
|
-
|
|
486
|
-
Your response MUST start with { and end with }
|
|
487
|
-
|
|
488
|
-
The JSON MUST match this EXACT schema:
|
|
489
|
-
${schemaStr}
|
|
490
|
-
|
|
491
|
-
Now, based on the following conversation, generate ONLY the JSON object with the exact fields specified above:
|
|
492
|
-
|
|
493
|
-
${finalPrompt}
|
|
494
|
-
|
|
495
|
-
Remember: Your ENTIRE response must be ONLY the JSON object, starting with { and ending with }`;
|
|
496
|
-
streamingParts = [{ type: "text", text: finalPrompt }, ...imagePartsInOrder];
|
|
497
|
-
}
|
|
498
|
-
return {
|
|
499
|
-
messagesPrompt: finalPrompt,
|
|
500
|
-
systemPrompt,
|
|
501
|
-
...warnings.length > 0 && { warnings },
|
|
502
|
-
streamingContentParts: streamingParts.length > 0 ? streamingParts : [
|
|
503
|
-
{ type: "text", text: finalPrompt },
|
|
504
|
-
...imagePartsInOrder
|
|
505
|
-
],
|
|
506
|
-
hasImageParts
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
function extractJson(text) {
|
|
510
|
-
let content = text.trim();
|
|
511
|
-
const fenceMatch = /```(?:json)?\s*([\s\S]*?)\s*```/i.exec(content);
|
|
512
|
-
if (fenceMatch) {
|
|
513
|
-
content = fenceMatch[1];
|
|
514
|
-
}
|
|
515
|
-
const varMatch = /^\s*(?:const|let|var)\s+\w+\s*=\s*([\s\S]*)/i.exec(content);
|
|
516
|
-
if (varMatch) {
|
|
517
|
-
content = varMatch[1];
|
|
518
|
-
if (content.trim().endsWith(";")) {
|
|
519
|
-
content = content.trim().slice(0, -1);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
const firstObj = content.indexOf("{");
|
|
523
|
-
const firstArr = content.indexOf("[");
|
|
524
|
-
if (firstObj === -1 && firstArr === -1) {
|
|
525
|
-
return text;
|
|
526
|
-
}
|
|
527
|
-
const start = firstArr === -1 ? firstObj : firstObj === -1 ? firstArr : Math.min(firstObj, firstArr);
|
|
528
|
-
content = content.slice(start);
|
|
529
|
-
const tryParse = (value) => {
|
|
530
|
-
const errors = [];
|
|
531
|
-
try {
|
|
532
|
-
const result = parse(value, errors, { allowTrailingComma: true });
|
|
533
|
-
if (errors.length === 0) {
|
|
534
|
-
return JSON.stringify(result, null, 2);
|
|
535
|
-
}
|
|
536
|
-
} catch {
|
|
537
|
-
}
|
|
538
|
-
return void 0;
|
|
539
|
-
};
|
|
540
|
-
const parsed = tryParse(content);
|
|
541
|
-
if (parsed !== void 0) {
|
|
542
|
-
return parsed;
|
|
543
|
-
}
|
|
544
|
-
const openChar = content[0];
|
|
545
|
-
const closeChar = openChar === "{" ? "}" : "]";
|
|
546
|
-
const closingPositions = [];
|
|
547
|
-
let depth = 0;
|
|
548
|
-
let inString = false;
|
|
549
|
-
let escapeNext = false;
|
|
550
|
-
for (let i = 0; i < content.length; i++) {
|
|
551
|
-
const char = content[i];
|
|
552
|
-
if (escapeNext) {
|
|
553
|
-
escapeNext = false;
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
if (char === "\\") {
|
|
557
|
-
escapeNext = true;
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
if (char === '"' && !inString) {
|
|
561
|
-
inString = true;
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
if (char === '"' && inString) {
|
|
565
|
-
inString = false;
|
|
566
|
-
continue;
|
|
567
|
-
}
|
|
568
|
-
if (inString) continue;
|
|
569
|
-
if (char === openChar) {
|
|
570
|
-
depth++;
|
|
571
|
-
} else if (char === closeChar) {
|
|
572
|
-
depth--;
|
|
573
|
-
if (depth === 0) {
|
|
574
|
-
closingPositions.push(i + 1);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
for (let i = closingPositions.length - 1; i >= 0; i--) {
|
|
579
|
-
const attempt = tryParse(content.slice(0, closingPositions[i]));
|
|
580
|
-
if (attempt !== void 0) {
|
|
581
|
-
return attempt;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
const searchStart = Math.max(0, content.length - 1e3);
|
|
585
|
-
for (let end = content.length - 1; end > searchStart; end--) {
|
|
586
|
-
const attempt = tryParse(content.slice(0, end));
|
|
587
|
-
if (attempt !== void 0) {
|
|
588
|
-
return attempt;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
return text;
|
|
592
|
-
}
|
|
593
|
-
function createAPICallError({
|
|
594
|
-
message,
|
|
595
|
-
code,
|
|
596
|
-
exitCode,
|
|
597
|
-
stderr,
|
|
598
|
-
promptExcerpt,
|
|
599
|
-
isRetryable = false
|
|
600
|
-
}) {
|
|
601
|
-
const metadata = {
|
|
602
|
-
code,
|
|
603
|
-
exitCode,
|
|
604
|
-
stderr,
|
|
605
|
-
promptExcerpt
|
|
606
|
-
};
|
|
607
|
-
return new APICallError({
|
|
608
|
-
message,
|
|
609
|
-
isRetryable,
|
|
610
|
-
url: "claude-code-cli://command",
|
|
611
|
-
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
|
|
612
|
-
data: metadata
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
function createAuthenticationError({ message }) {
|
|
616
|
-
return new LoadAPIKeyError({
|
|
617
|
-
message: message || "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
function createTimeoutError({
|
|
621
|
-
message,
|
|
622
|
-
promptExcerpt,
|
|
623
|
-
timeoutMs
|
|
624
|
-
}) {
|
|
625
|
-
const metadata = {
|
|
626
|
-
code: "TIMEOUT",
|
|
627
|
-
promptExcerpt
|
|
628
|
-
};
|
|
629
|
-
return new APICallError({
|
|
630
|
-
message,
|
|
631
|
-
isRetryable: true,
|
|
632
|
-
url: "claude-code-cli://command",
|
|
633
|
-
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
|
|
634
|
-
data: timeoutMs !== void 0 ? { ...metadata, timeoutMs } : metadata
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// src/map-claude-code-finish-reason.ts
|
|
639
|
-
function mapClaudeCodeFinishReason(subtype) {
|
|
640
|
-
switch (subtype) {
|
|
641
|
-
case "success":
|
|
642
|
-
return "stop";
|
|
643
|
-
case "error_max_turns":
|
|
644
|
-
return "length";
|
|
645
|
-
case "error_during_execution":
|
|
646
|
-
return "error";
|
|
647
|
-
default:
|
|
648
|
-
return "stop";
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
var loggerFunctionSchema = z.object({
|
|
652
|
-
debug: z.any().refine((val) => typeof val === "function", {
|
|
653
|
-
message: "debug must be a function"
|
|
654
|
-
}),
|
|
655
|
-
info: z.any().refine((val) => typeof val === "function", {
|
|
656
|
-
message: "info must be a function"
|
|
657
|
-
}),
|
|
658
|
-
warn: z.any().refine((val) => typeof val === "function", {
|
|
659
|
-
message: "warn must be a function"
|
|
660
|
-
}),
|
|
661
|
-
error: z.any().refine((val) => typeof val === "function", {
|
|
662
|
-
message: "error must be a function"
|
|
663
|
-
})
|
|
664
|
-
});
|
|
665
|
-
var claudeCodeSettingsSchema = z.object({
|
|
666
|
-
pathToClaudeCodeExecutable: z.string().optional(),
|
|
667
|
-
customSystemPrompt: z.string().optional(),
|
|
668
|
-
appendSystemPrompt: z.string().optional(),
|
|
669
|
-
systemPrompt: z.union([
|
|
670
|
-
z.string(),
|
|
671
|
-
z.object({
|
|
672
|
-
type: z.literal("preset"),
|
|
673
|
-
preset: z.literal("claude_code"),
|
|
674
|
-
append: z.string().optional()
|
|
675
|
-
})
|
|
676
|
-
]).optional(),
|
|
677
|
-
maxTurns: z.number().int().min(1).max(100).optional(),
|
|
678
|
-
maxThinkingTokens: z.number().int().positive().max(1e5).optional(),
|
|
679
|
-
cwd: z.string().refine(
|
|
680
|
-
(val) => {
|
|
681
|
-
if (typeof process === "undefined" || !process.versions?.node) {
|
|
682
|
-
return true;
|
|
683
|
-
}
|
|
684
|
-
return !val || existsSync(val);
|
|
685
|
-
},
|
|
686
|
-
{ message: "Working directory must exist" }
|
|
687
|
-
).optional(),
|
|
688
|
-
executable: z.enum(["bun", "deno", "node"]).optional(),
|
|
689
|
-
executableArgs: z.array(z.string()).optional(),
|
|
690
|
-
permissionMode: z.enum(["default", "acceptEdits", "bypassPermissions", "plan"]).optional(),
|
|
691
|
-
permissionPromptToolName: z.string().optional(),
|
|
692
|
-
continue: z.boolean().optional(),
|
|
693
|
-
resume: z.string().optional(),
|
|
694
|
-
allowedTools: z.array(z.string()).optional(),
|
|
695
|
-
disallowedTools: z.array(z.string()).optional(),
|
|
696
|
-
settingSources: z.array(z.enum(["user", "project", "local"])).optional(),
|
|
697
|
-
streamingInput: z.enum(["auto", "always", "off"]).optional(),
|
|
698
|
-
// Hooks and tool-permission callback (permissive validation of shapes)
|
|
699
|
-
canUseTool: z.any().refine((v) => v === void 0 || typeof v === "function", {
|
|
700
|
-
message: "canUseTool must be a function"
|
|
701
|
-
}).optional(),
|
|
702
|
-
hooks: z.record(
|
|
703
|
-
z.string(),
|
|
704
|
-
z.array(
|
|
705
|
-
z.object({
|
|
706
|
-
matcher: z.string().optional(),
|
|
707
|
-
hooks: z.array(z.any()).nonempty()
|
|
708
|
-
})
|
|
709
|
-
)
|
|
710
|
-
).optional(),
|
|
711
|
-
mcpServers: z.record(
|
|
712
|
-
z.string(),
|
|
713
|
-
z.union([
|
|
714
|
-
// McpStdioServerConfig
|
|
715
|
-
z.object({
|
|
716
|
-
type: z.literal("stdio").optional(),
|
|
717
|
-
command: z.string(),
|
|
718
|
-
args: z.array(z.string()).optional(),
|
|
719
|
-
env: z.record(z.string(), z.string()).optional()
|
|
720
|
-
}),
|
|
721
|
-
// McpSSEServerConfig
|
|
722
|
-
z.object({
|
|
723
|
-
type: z.literal("sse"),
|
|
724
|
-
url: z.string(),
|
|
725
|
-
headers: z.record(z.string(), z.string()).optional()
|
|
726
|
-
}),
|
|
727
|
-
// McpHttpServerConfig
|
|
728
|
-
z.object({
|
|
729
|
-
type: z.literal("http"),
|
|
730
|
-
url: z.string(),
|
|
731
|
-
headers: z.record(z.string(), z.string()).optional()
|
|
732
|
-
}),
|
|
733
|
-
// McpSdkServerConfig (in-process custom tools)
|
|
734
|
-
z.object({
|
|
735
|
-
type: z.literal("sdk"),
|
|
736
|
-
name: z.string(),
|
|
737
|
-
instance: z.any()
|
|
738
|
-
})
|
|
739
|
-
])
|
|
740
|
-
).optional(),
|
|
741
|
-
verbose: z.boolean().optional(),
|
|
742
|
-
logger: z.union([z.literal(false), loggerFunctionSchema]).optional(),
|
|
743
|
-
env: z.record(z.string(), z.string().optional()).optional(),
|
|
744
|
-
additionalDirectories: z.array(z.string()).optional(),
|
|
745
|
-
agents: z.record(
|
|
746
|
-
z.string(),
|
|
747
|
-
z.object({
|
|
748
|
-
description: z.string(),
|
|
749
|
-
tools: z.array(z.string()).optional(),
|
|
750
|
-
prompt: z.string(),
|
|
751
|
-
model: z.enum(["sonnet", "opus", "haiku", "inherit"]).optional()
|
|
752
|
-
})
|
|
753
|
-
).optional(),
|
|
754
|
-
includePartialMessages: z.boolean().optional(),
|
|
755
|
-
fallbackModel: z.string().optional(),
|
|
756
|
-
forkSession: z.boolean().optional(),
|
|
757
|
-
stderr: z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
758
|
-
message: "stderr must be a function"
|
|
759
|
-
}).optional(),
|
|
760
|
-
strictMcpConfig: z.boolean().optional(),
|
|
761
|
-
extraArgs: z.record(z.string(), z.union([z.string(), z.null()])).optional(),
|
|
762
|
-
queryFunction: z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
763
|
-
message: "queryFunction must be a function"
|
|
764
|
-
}).optional()
|
|
765
|
-
}).strict();
|
|
766
|
-
function validateModelId(modelId) {
|
|
767
|
-
const knownModels = ["opus", "sonnet", "haiku"];
|
|
768
|
-
if (!modelId || modelId.trim() === "") {
|
|
769
|
-
throw new Error("Model ID cannot be empty");
|
|
770
|
-
}
|
|
771
|
-
if (!knownModels.includes(modelId)) {
|
|
772
|
-
return `Unknown model ID: '${modelId}'. Proceeding with custom model. Known models are: ${knownModels.join(", ")}`;
|
|
773
|
-
}
|
|
774
|
-
return void 0;
|
|
775
|
-
}
|
|
776
|
-
function validateSettings(settings) {
|
|
777
|
-
const warnings = [];
|
|
778
|
-
const errors = [];
|
|
779
|
-
try {
|
|
780
|
-
const result = claudeCodeSettingsSchema.safeParse(settings);
|
|
781
|
-
if (!result.success) {
|
|
782
|
-
const errorObject = result.error;
|
|
783
|
-
const issues = errorObject.errors || errorObject.issues || [];
|
|
784
|
-
issues.forEach((err) => {
|
|
785
|
-
const path = err.path.join(".");
|
|
786
|
-
errors.push(`${path ? `${path}: ` : ""}${err.message}`);
|
|
787
|
-
});
|
|
788
|
-
return { valid: false, warnings, errors };
|
|
789
|
-
}
|
|
790
|
-
const validSettings = result.data;
|
|
791
|
-
if (validSettings.maxTurns && validSettings.maxTurns > 20) {
|
|
792
|
-
warnings.push(
|
|
793
|
-
`High maxTurns value (${validSettings.maxTurns}) may lead to long-running conversations`
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
if (validSettings.maxThinkingTokens && validSettings.maxThinkingTokens > 5e4) {
|
|
797
|
-
warnings.push(
|
|
798
|
-
`Very high maxThinkingTokens (${validSettings.maxThinkingTokens}) may increase response time`
|
|
799
|
-
);
|
|
800
|
-
}
|
|
801
|
-
if (validSettings.allowedTools && validSettings.disallowedTools) {
|
|
802
|
-
warnings.push(
|
|
803
|
-
"Both allowedTools and disallowedTools are specified. Only allowedTools will be used."
|
|
804
|
-
);
|
|
805
|
-
}
|
|
806
|
-
const validateToolNames = (tools, type) => {
|
|
807
|
-
tools.forEach((tool3) => {
|
|
808
|
-
if (!/^[a-zA-Z_][a-zA-Z0-9_]*(\([^)]*\))?$/.test(tool3) && !tool3.startsWith("mcp__")) {
|
|
809
|
-
warnings.push(`Unusual ${type} tool name format: '${tool3}'`);
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
};
|
|
813
|
-
if (validSettings.allowedTools) {
|
|
814
|
-
validateToolNames(validSettings.allowedTools, "allowed");
|
|
815
|
-
}
|
|
816
|
-
if (validSettings.disallowedTools) {
|
|
817
|
-
validateToolNames(validSettings.disallowedTools, "disallowed");
|
|
818
|
-
}
|
|
819
|
-
return { valid: true, warnings, errors };
|
|
820
|
-
} catch (error) {
|
|
821
|
-
errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
822
|
-
return { valid: false, warnings, errors };
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
function validatePrompt(prompt) {
|
|
826
|
-
const MAX_PROMPT_LENGTH = 1e5;
|
|
827
|
-
if (prompt.length > MAX_PROMPT_LENGTH) {
|
|
828
|
-
return `Very long prompt (${prompt.length} characters) may cause performance issues or timeouts`;
|
|
829
|
-
}
|
|
830
|
-
return void 0;
|
|
831
|
-
}
|
|
832
|
-
function validateSessionId(sessionId) {
|
|
833
|
-
if (sessionId && !/^[a-zA-Z0-9-_]+$/.test(sessionId)) {
|
|
834
|
-
return `Unusual session ID format. This may cause issues with session resumption.`;
|
|
835
|
-
}
|
|
836
|
-
return void 0;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// src/logger.ts
|
|
840
|
-
var defaultLogger = {
|
|
841
|
-
debug: (message) => console.debug(`[DEBUG] ${message}`),
|
|
842
|
-
info: (message) => console.info(`[INFO] ${message}`),
|
|
843
|
-
warn: (message) => console.warn(`[WARN] ${message}`),
|
|
844
|
-
error: (message) => console.error(`[ERROR] ${message}`)
|
|
845
|
-
};
|
|
846
|
-
var noopLogger = {
|
|
847
|
-
debug: () => {
|
|
848
|
-
},
|
|
849
|
-
info: () => {
|
|
850
|
-
},
|
|
851
|
-
warn: () => {
|
|
852
|
-
},
|
|
853
|
-
error: () => {
|
|
854
|
-
}
|
|
855
|
-
};
|
|
856
|
-
function getLogger(logger) {
|
|
857
|
-
if (logger === false) {
|
|
858
|
-
return noopLogger;
|
|
859
|
-
}
|
|
860
|
-
if (logger === void 0) {
|
|
861
|
-
return defaultLogger;
|
|
862
|
-
}
|
|
863
|
-
return logger;
|
|
864
|
-
}
|
|
865
|
-
function createVerboseLogger(logger, verbose = false) {
|
|
866
|
-
if (verbose) {
|
|
867
|
-
return logger;
|
|
868
|
-
}
|
|
869
|
-
return {
|
|
870
|
-
debug: () => {
|
|
871
|
-
},
|
|
872
|
-
// No-op when not verbose
|
|
873
|
-
info: () => {
|
|
874
|
-
},
|
|
875
|
-
// No-op when not verbose
|
|
876
|
-
warn: logger.warn.bind(logger),
|
|
877
|
-
error: logger.error.bind(logger)
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
var CLAUDE_CODE_TRUNCATION_WARNING = "Claude Code SDK output ended unexpectedly; returning truncated response from buffered text. Await upstream fix to avoid data loss.";
|
|
881
|
-
var MIN_TRUNCATION_LENGTH = 512;
|
|
882
|
-
function isClaudeCodeTruncationError(error, bufferedText) {
|
|
883
|
-
const isSyntaxError = error instanceof SyntaxError || // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
884
|
-
typeof error?.name === "string" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
885
|
-
error.name.toLowerCase() === "syntaxerror";
|
|
886
|
-
if (!isSyntaxError) {
|
|
887
|
-
return false;
|
|
888
|
-
}
|
|
889
|
-
if (!bufferedText) {
|
|
890
|
-
return false;
|
|
891
|
-
}
|
|
892
|
-
const rawMessage = typeof error?.message === "string" ? error.message : "";
|
|
893
|
-
const message = rawMessage.toLowerCase();
|
|
894
|
-
const truncationIndicators = [
|
|
895
|
-
"unexpected end of json input",
|
|
896
|
-
"unexpected end of input",
|
|
897
|
-
"unexpected end of string",
|
|
898
|
-
"unexpected eof",
|
|
899
|
-
"end of file",
|
|
900
|
-
"unterminated string",
|
|
901
|
-
"unterminated string constant"
|
|
902
|
-
];
|
|
903
|
-
if (!truncationIndicators.some((indicator) => message.includes(indicator))) {
|
|
904
|
-
return false;
|
|
905
|
-
}
|
|
906
|
-
if (bufferedText.length < MIN_TRUNCATION_LENGTH) {
|
|
907
|
-
return false;
|
|
908
|
-
}
|
|
909
|
-
return true;
|
|
910
|
-
}
|
|
911
|
-
function isAbortError(err) {
|
|
912
|
-
if (err && typeof err === "object") {
|
|
913
|
-
const e = err;
|
|
914
|
-
if (typeof e.name === "string" && e.name === "AbortError") return true;
|
|
915
|
-
if (typeof e.code === "string" && e.code.toUpperCase() === "ABORT_ERR") return true;
|
|
916
|
-
}
|
|
917
|
-
return false;
|
|
918
|
-
}
|
|
919
|
-
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).";
|
|
920
|
-
function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, contentParts) {
|
|
921
|
-
const content = contentParts && contentParts.length > 0 ? contentParts : [{ type: "text", text: messagesPrompt }];
|
|
922
|
-
const msg = {
|
|
923
|
-
type: "user",
|
|
924
|
-
message: {
|
|
925
|
-
role: "user",
|
|
926
|
-
content
|
|
927
|
-
},
|
|
928
|
-
parent_tool_use_id: null,
|
|
929
|
-
session_id: sessionId ?? ""
|
|
930
|
-
};
|
|
931
|
-
return {
|
|
932
|
-
async *[Symbol.asyncIterator]() {
|
|
933
|
-
yield msg;
|
|
934
|
-
await outputStreamEnded;
|
|
935
|
-
}
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
var modelMap = {
|
|
939
|
-
opus: "opus",
|
|
940
|
-
sonnet: "sonnet",
|
|
941
|
-
haiku: "haiku"
|
|
942
|
-
};
|
|
943
|
-
var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
944
|
-
specificationVersion = "v2";
|
|
945
|
-
defaultObjectGenerationMode = "json";
|
|
946
|
-
supportsImageUrls = false;
|
|
947
|
-
supportedUrls = {};
|
|
948
|
-
supportsStructuredOutputs = false;
|
|
949
|
-
// Fallback/magic string constants
|
|
950
|
-
static UNKNOWN_TOOL_NAME = "unknown-tool";
|
|
951
|
-
// Tool input safety limits
|
|
952
|
-
static MAX_TOOL_INPUT_SIZE = 1048576;
|
|
953
|
-
// 1MB hard limit
|
|
954
|
-
static MAX_TOOL_INPUT_WARN = 102400;
|
|
955
|
-
// 100KB warning threshold
|
|
956
|
-
static MAX_DELTA_CALC_SIZE = 1e4;
|
|
957
|
-
// 10KB delta computation threshold
|
|
958
|
-
modelId;
|
|
959
|
-
settings;
|
|
960
|
-
sessionId;
|
|
961
|
-
modelValidationWarning;
|
|
962
|
-
settingsValidationWarnings;
|
|
963
|
-
logger;
|
|
964
|
-
queryFn;
|
|
965
|
-
constructor(options) {
|
|
966
|
-
this.modelId = options.id;
|
|
967
|
-
this.settings = options.settings ?? {};
|
|
968
|
-
this.settingsValidationWarnings = options.settingsValidationWarnings ?? [];
|
|
969
|
-
this.queryFn = this.settings.queryFunction ?? query;
|
|
970
|
-
const baseLogger = getLogger(this.settings.logger);
|
|
971
|
-
this.logger = createVerboseLogger(baseLogger, this.settings.verbose ?? false);
|
|
972
|
-
if (!this.modelId || typeof this.modelId !== "string" || this.modelId.trim() === "") {
|
|
973
|
-
throw new NoSuchModelError({
|
|
974
|
-
modelId: this.modelId,
|
|
975
|
-
modelType: "languageModel"
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
this.modelValidationWarning = validateModelId(this.modelId);
|
|
979
|
-
if (this.modelValidationWarning) {
|
|
980
|
-
this.logger.warn(`Claude Code Model: ${this.modelValidationWarning}`);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
get provider() {
|
|
984
|
-
return "claude-code";
|
|
985
|
-
}
|
|
986
|
-
getModel() {
|
|
987
|
-
const mapped = modelMap[this.modelId];
|
|
988
|
-
return mapped ?? this.modelId;
|
|
989
|
-
}
|
|
990
|
-
extractToolUses(content) {
|
|
991
|
-
if (!Array.isArray(content)) {
|
|
992
|
-
return [];
|
|
993
|
-
}
|
|
994
|
-
return content.filter(
|
|
995
|
-
(item) => typeof item === "object" && item !== null && "type" in item && item.type === "tool_use"
|
|
996
|
-
).map((item) => {
|
|
997
|
-
const { id, name, input } = item;
|
|
998
|
-
return {
|
|
999
|
-
id: typeof id === "string" && id.length > 0 ? id : generateId(),
|
|
1000
|
-
name: typeof name === "string" && name.length > 0 ? name : _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME,
|
|
1001
|
-
input
|
|
1002
|
-
};
|
|
1003
|
-
});
|
|
1004
|
-
}
|
|
1005
|
-
extractToolResults(content) {
|
|
1006
|
-
if (!Array.isArray(content)) {
|
|
1007
|
-
return [];
|
|
1008
|
-
}
|
|
1009
|
-
return content.filter(
|
|
1010
|
-
(item) => typeof item === "object" && item !== null && "type" in item && item.type === "tool_result"
|
|
1011
|
-
).map((item) => {
|
|
1012
|
-
const { tool_use_id, content: content2, is_error, name } = item;
|
|
1013
|
-
return {
|
|
1014
|
-
id: typeof tool_use_id === "string" && tool_use_id.length > 0 ? tool_use_id : generateId(),
|
|
1015
|
-
name: typeof name === "string" && name.length > 0 ? name : void 0,
|
|
1016
|
-
result: content2,
|
|
1017
|
-
isError: Boolean(is_error)
|
|
1018
|
-
};
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
extractToolErrors(content) {
|
|
1022
|
-
if (!Array.isArray(content)) {
|
|
1023
|
-
return [];
|
|
1024
|
-
}
|
|
1025
|
-
return content.filter(
|
|
1026
|
-
(item) => typeof item === "object" && item !== null && "type" in item && item.type === "tool_error"
|
|
1027
|
-
).map((item) => {
|
|
1028
|
-
const { tool_use_id, error, name } = item;
|
|
1029
|
-
return {
|
|
1030
|
-
id: typeof tool_use_id === "string" && tool_use_id.length > 0 ? tool_use_id : generateId(),
|
|
1031
|
-
name: typeof name === "string" && name.length > 0 ? name : void 0,
|
|
1032
|
-
error
|
|
1033
|
-
};
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
serializeToolInput(input) {
|
|
1037
|
-
if (typeof input === "string") {
|
|
1038
|
-
return this.checkInputSize(input);
|
|
1039
|
-
}
|
|
1040
|
-
if (input === void 0) {
|
|
1041
|
-
return "";
|
|
1042
|
-
}
|
|
1043
|
-
try {
|
|
1044
|
-
const serialized = JSON.stringify(input);
|
|
1045
|
-
return this.checkInputSize(serialized);
|
|
1046
|
-
} catch {
|
|
1047
|
-
const fallback = String(input);
|
|
1048
|
-
return this.checkInputSize(fallback);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
checkInputSize(str) {
|
|
1052
|
-
const length = str.length;
|
|
1053
|
-
if (length > _ClaudeCodeLanguageModel.MAX_TOOL_INPUT_SIZE) {
|
|
1054
|
-
throw new Error(
|
|
1055
|
-
`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.`
|
|
1056
|
-
);
|
|
1057
|
-
}
|
|
1058
|
-
if (length > _ClaudeCodeLanguageModel.MAX_TOOL_INPUT_WARN) {
|
|
1059
|
-
this.logger.warn(
|
|
1060
|
-
`[claude-code] Large tool input detected: ${length} bytes. Performance may be impacted. Consider chunking or reducing input size.`
|
|
1061
|
-
);
|
|
1062
|
-
}
|
|
1063
|
-
return str;
|
|
1064
|
-
}
|
|
1065
|
-
normalizeToolResult(result) {
|
|
1066
|
-
if (typeof result === "string") {
|
|
1067
|
-
try {
|
|
1068
|
-
return JSON.parse(result);
|
|
1069
|
-
} catch {
|
|
1070
|
-
return result;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
return result;
|
|
1074
|
-
}
|
|
1075
|
-
generateAllWarnings(options, prompt) {
|
|
1076
|
-
const warnings = [];
|
|
1077
|
-
const unsupportedParams = [];
|
|
1078
|
-
if (options.temperature !== void 0) unsupportedParams.push("temperature");
|
|
1079
|
-
if (options.topP !== void 0) unsupportedParams.push("topP");
|
|
1080
|
-
if (options.topK !== void 0) unsupportedParams.push("topK");
|
|
1081
|
-
if (options.presencePenalty !== void 0) unsupportedParams.push("presencePenalty");
|
|
1082
|
-
if (options.frequencyPenalty !== void 0) unsupportedParams.push("frequencyPenalty");
|
|
1083
|
-
if (options.stopSequences !== void 0 && options.stopSequences.length > 0)
|
|
1084
|
-
unsupportedParams.push("stopSequences");
|
|
1085
|
-
if (options.seed !== void 0) unsupportedParams.push("seed");
|
|
1086
|
-
if (unsupportedParams.length > 0) {
|
|
1087
|
-
for (const param of unsupportedParams) {
|
|
1088
|
-
warnings.push({
|
|
1089
|
-
type: "unsupported-setting",
|
|
1090
|
-
setting: param,
|
|
1091
|
-
details: `Claude Code SDK does not support the ${param} parameter. It will be ignored.`
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
if (this.modelValidationWarning) {
|
|
1096
|
-
warnings.push({
|
|
1097
|
-
type: "other",
|
|
1098
|
-
message: this.modelValidationWarning
|
|
1099
|
-
});
|
|
1100
|
-
}
|
|
1101
|
-
this.settingsValidationWarnings.forEach((warning) => {
|
|
1102
|
-
warnings.push({
|
|
1103
|
-
type: "other",
|
|
1104
|
-
message: warning
|
|
1105
|
-
});
|
|
1106
|
-
});
|
|
1107
|
-
const promptWarning = validatePrompt(prompt);
|
|
1108
|
-
if (promptWarning) {
|
|
1109
|
-
warnings.push({
|
|
1110
|
-
type: "other",
|
|
1111
|
-
message: promptWarning
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
|
-
return warnings;
|
|
1115
|
-
}
|
|
1116
|
-
handleJsonExtraction(text, warnings) {
|
|
1117
|
-
const extracted = extractJson(text);
|
|
1118
|
-
const validation = this.validateJsonExtraction(text, extracted);
|
|
1119
|
-
if (!validation.valid && validation.warning) {
|
|
1120
|
-
warnings.push(validation.warning);
|
|
1121
|
-
}
|
|
1122
|
-
return extracted;
|
|
1123
|
-
}
|
|
1124
|
-
createQueryOptions(abortController) {
|
|
1125
|
-
const opts = {
|
|
1126
|
-
model: this.getModel(),
|
|
1127
|
-
abortController,
|
|
1128
|
-
resume: this.settings.resume ?? this.sessionId,
|
|
1129
|
-
pathToClaudeCodeExecutable: this.settings.pathToClaudeCodeExecutable,
|
|
1130
|
-
maxTurns: this.settings.maxTurns,
|
|
1131
|
-
maxThinkingTokens: this.settings.maxThinkingTokens,
|
|
1132
|
-
cwd: this.settings.cwd,
|
|
1133
|
-
executable: this.settings.executable,
|
|
1134
|
-
executableArgs: this.settings.executableArgs,
|
|
1135
|
-
permissionMode: this.settings.permissionMode,
|
|
1136
|
-
permissionPromptToolName: this.settings.permissionPromptToolName,
|
|
1137
|
-
continue: this.settings.continue,
|
|
1138
|
-
allowedTools: this.settings.allowedTools,
|
|
1139
|
-
disallowedTools: this.settings.disallowedTools,
|
|
1140
|
-
mcpServers: this.settings.mcpServers,
|
|
1141
|
-
canUseTool: this.settings.canUseTool
|
|
1142
|
-
};
|
|
1143
|
-
if (this.settings.systemPrompt !== void 0) {
|
|
1144
|
-
opts.systemPrompt = this.settings.systemPrompt;
|
|
1145
|
-
} else if (this.settings.customSystemPrompt !== void 0) {
|
|
1146
|
-
this.logger.warn(
|
|
1147
|
-
"[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? })."
|
|
1148
|
-
);
|
|
1149
|
-
opts.systemPrompt = this.settings.customSystemPrompt;
|
|
1150
|
-
} else if (this.settings.appendSystemPrompt !== void 0) {
|
|
1151
|
-
this.logger.warn(
|
|
1152
|
-
"[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."
|
|
1153
|
-
);
|
|
1154
|
-
opts.systemPrompt = {
|
|
1155
|
-
type: "preset",
|
|
1156
|
-
preset: "claude_code",
|
|
1157
|
-
append: this.settings.appendSystemPrompt
|
|
1158
|
-
};
|
|
1159
|
-
}
|
|
1160
|
-
if (this.settings.settingSources !== void 0) {
|
|
1161
|
-
opts.settingSources = this.settings.settingSources;
|
|
1162
|
-
}
|
|
1163
|
-
if (this.settings.additionalDirectories !== void 0) {
|
|
1164
|
-
opts.additionalDirectories = this.settings.additionalDirectories;
|
|
1165
|
-
}
|
|
1166
|
-
if (this.settings.agents !== void 0) {
|
|
1167
|
-
opts.agents = this.settings.agents;
|
|
1168
|
-
}
|
|
1169
|
-
if (this.settings.includePartialMessages !== void 0) {
|
|
1170
|
-
opts.includePartialMessages = this.settings.includePartialMessages;
|
|
1171
|
-
}
|
|
1172
|
-
if (this.settings.fallbackModel !== void 0) {
|
|
1173
|
-
opts.fallbackModel = this.settings.fallbackModel;
|
|
1174
|
-
}
|
|
1175
|
-
if (this.settings.forkSession !== void 0) {
|
|
1176
|
-
opts.forkSession = this.settings.forkSession;
|
|
1177
|
-
}
|
|
1178
|
-
if (this.settings.stderr !== void 0) {
|
|
1179
|
-
opts.stderr = this.settings.stderr;
|
|
1180
|
-
}
|
|
1181
|
-
if (this.settings.strictMcpConfig !== void 0) {
|
|
1182
|
-
opts.strictMcpConfig = this.settings.strictMcpConfig;
|
|
1183
|
-
}
|
|
1184
|
-
if (this.settings.extraArgs !== void 0) {
|
|
1185
|
-
opts.extraArgs = this.settings.extraArgs;
|
|
1186
|
-
}
|
|
1187
|
-
if (this.settings.hooks) {
|
|
1188
|
-
opts.hooks = this.settings.hooks;
|
|
1189
|
-
}
|
|
1190
|
-
if (this.settings.env !== void 0) {
|
|
1191
|
-
opts.env = { ...process.env, ...this.settings.env };
|
|
1192
|
-
}
|
|
1193
|
-
return opts;
|
|
1194
|
-
}
|
|
1195
|
-
handleClaudeCodeError(error, messagesPrompt) {
|
|
1196
|
-
if (isAbortError(error)) {
|
|
1197
|
-
throw error;
|
|
1198
|
-
}
|
|
1199
|
-
const isErrorWithMessage = (err) => {
|
|
1200
|
-
return typeof err === "object" && err !== null && "message" in err;
|
|
1201
|
-
};
|
|
1202
|
-
const isErrorWithCode = (err) => {
|
|
1203
|
-
return typeof err === "object" && err !== null;
|
|
1204
|
-
};
|
|
1205
|
-
const authErrorPatterns = [
|
|
1206
|
-
"not logged in",
|
|
1207
|
-
"authentication",
|
|
1208
|
-
"unauthorized",
|
|
1209
|
-
"auth failed",
|
|
1210
|
-
"please login",
|
|
1211
|
-
"claude login"
|
|
1212
|
-
];
|
|
1213
|
-
const errorMessage = isErrorWithMessage(error) && error.message ? error.message.toLowerCase() : "";
|
|
1214
|
-
const exitCode = isErrorWithCode(error) && typeof error.exitCode === "number" ? error.exitCode : void 0;
|
|
1215
|
-
const isAuthError = authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
|
|
1216
|
-
if (isAuthError) {
|
|
1217
|
-
return createAuthenticationError({
|
|
1218
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
|
|
1219
|
-
});
|
|
1220
|
-
}
|
|
1221
|
-
const errorCode = isErrorWithCode(error) && typeof error.code === "string" ? error.code : "";
|
|
1222
|
-
if (errorCode === "ETIMEDOUT" || errorMessage.includes("timeout")) {
|
|
1223
|
-
return createTimeoutError({
|
|
1224
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Request timed out",
|
|
1225
|
-
promptExcerpt: messagesPrompt.substring(0, 200)
|
|
1226
|
-
// Don't specify timeoutMs since we don't know the actual timeout value
|
|
1227
|
-
// It's controlled by the consumer via AbortSignal
|
|
1228
|
-
});
|
|
1229
|
-
}
|
|
1230
|
-
const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
|
|
1231
|
-
return createAPICallError({
|
|
1232
|
-
message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
|
|
1233
|
-
code: errorCode || void 0,
|
|
1234
|
-
exitCode,
|
|
1235
|
-
stderr: isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0,
|
|
1236
|
-
promptExcerpt: messagesPrompt.substring(0, 200),
|
|
1237
|
-
isRetryable
|
|
1238
|
-
});
|
|
1239
|
-
}
|
|
1240
|
-
setSessionId(sessionId) {
|
|
1241
|
-
this.sessionId = sessionId;
|
|
1242
|
-
const warning = validateSessionId(sessionId);
|
|
1243
|
-
if (warning) {
|
|
1244
|
-
this.logger.warn(`Claude Code Session: ${warning}`);
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
validateJsonExtraction(originalText, extractedJson) {
|
|
1248
|
-
if (extractedJson === originalText) {
|
|
1249
|
-
return {
|
|
1250
|
-
valid: false,
|
|
1251
|
-
warning: {
|
|
1252
|
-
type: "other",
|
|
1253
|
-
message: "JSON extraction from model response may be incomplete or modified. The model may not have returned valid JSON."
|
|
1254
|
-
}
|
|
1255
|
-
};
|
|
1256
|
-
}
|
|
1257
|
-
try {
|
|
1258
|
-
JSON.parse(extractedJson);
|
|
1259
|
-
return { valid: true };
|
|
1260
|
-
} catch {
|
|
1261
|
-
return {
|
|
1262
|
-
valid: false,
|
|
1263
|
-
warning: {
|
|
1264
|
-
type: "other",
|
|
1265
|
-
message: "JSON extraction resulted in invalid JSON. The response may be malformed."
|
|
1266
|
-
}
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
async doGenerate(options) {
|
|
1271
|
-
this.logger.debug(`[claude-code] Starting doGenerate request with model: ${this.modelId}`);
|
|
1272
|
-
const mode = options.responseFormat?.type === "json" ? { type: "object-json" } : { type: "regular" };
|
|
1273
|
-
this.logger.debug(
|
|
1274
|
-
`[claude-code] Request mode: ${mode.type}, response format: ${options.responseFormat?.type ?? "none"}`
|
|
1275
|
-
);
|
|
1276
|
-
const {
|
|
1277
|
-
messagesPrompt,
|
|
1278
|
-
warnings: messageWarnings,
|
|
1279
|
-
streamingContentParts,
|
|
1280
|
-
hasImageParts
|
|
1281
|
-
} = convertToClaudeCodeMessages(
|
|
1282
|
-
options.prompt,
|
|
1283
|
-
mode,
|
|
1284
|
-
options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0
|
|
1285
|
-
);
|
|
1286
|
-
this.logger.debug(
|
|
1287
|
-
`[claude-code] Converted ${options.prompt.length} messages, hasImageParts: ${hasImageParts}`
|
|
1288
|
-
);
|
|
1289
|
-
const abortController = new AbortController();
|
|
1290
|
-
let abortListener;
|
|
1291
|
-
if (options.abortSignal?.aborted) {
|
|
1292
|
-
abortController.abort(options.abortSignal.reason);
|
|
1293
|
-
} else if (options.abortSignal) {
|
|
1294
|
-
abortListener = () => abortController.abort(options.abortSignal?.reason);
|
|
1295
|
-
options.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
1296
|
-
}
|
|
1297
|
-
const queryOptions = this.createQueryOptions(abortController);
|
|
1298
|
-
let text = "";
|
|
1299
|
-
let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1300
|
-
let finishReason = "stop";
|
|
1301
|
-
let wasTruncated = false;
|
|
1302
|
-
let costUsd;
|
|
1303
|
-
let durationMs;
|
|
1304
|
-
let rawUsage;
|
|
1305
|
-
const warnings = this.generateAllWarnings(
|
|
1306
|
-
options,
|
|
1307
|
-
messagesPrompt
|
|
1308
|
-
);
|
|
1309
|
-
if (messageWarnings) {
|
|
1310
|
-
messageWarnings.forEach((warning) => {
|
|
1311
|
-
warnings.push({
|
|
1312
|
-
type: "other",
|
|
1313
|
-
message: warning
|
|
1314
|
-
});
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
const modeSetting = this.settings.streamingInput ?? "auto";
|
|
1318
|
-
const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
|
|
1319
|
-
if (!wantsStreamInput && hasImageParts) {
|
|
1320
|
-
warnings.push({
|
|
1321
|
-
type: "other",
|
|
1322
|
-
message: STREAMING_FEATURE_WARNING
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
let done = () => {
|
|
1326
|
-
};
|
|
1327
|
-
const outputStreamEnded = new Promise((resolve) => {
|
|
1328
|
-
done = () => resolve(void 0);
|
|
1329
|
-
});
|
|
1330
|
-
try {
|
|
1331
|
-
if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
|
|
1332
|
-
throw new Error(
|
|
1333
|
-
"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."
|
|
1334
|
-
);
|
|
1335
|
-
}
|
|
1336
|
-
const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
|
|
1337
|
-
messagesPrompt,
|
|
1338
|
-
outputStreamEnded,
|
|
1339
|
-
this.settings.resume ?? this.sessionId,
|
|
1340
|
-
streamingContentParts
|
|
1341
|
-
) : messagesPrompt;
|
|
1342
|
-
this.logger.debug(
|
|
1343
|
-
`[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
|
|
1344
|
-
);
|
|
1345
|
-
const response = this.queryFn({
|
|
1346
|
-
prompt: sdkPrompt,
|
|
1347
|
-
options: queryOptions
|
|
1348
|
-
});
|
|
1349
|
-
for await (const message of response) {
|
|
1350
|
-
this.logger.debug(`[claude-code] Received message type: ${message.type}`);
|
|
1351
|
-
if (message.type === "assistant") {
|
|
1352
|
-
text += message.message.content.map((c) => c.type === "text" ? c.text : "").join("");
|
|
1353
|
-
} else if (message.type === "result") {
|
|
1354
|
-
done();
|
|
1355
|
-
this.setSessionId(message.session_id);
|
|
1356
|
-
costUsd = message.total_cost_usd;
|
|
1357
|
-
durationMs = message.duration_ms;
|
|
1358
|
-
this.logger.info(
|
|
1359
|
-
`[claude-code] Request completed - Session: ${message.session_id}, Cost: $${costUsd?.toFixed(4) ?? "N/A"}, Duration: ${durationMs ?? "N/A"}ms`
|
|
1360
|
-
);
|
|
1361
|
-
if ("usage" in message) {
|
|
1362
|
-
rawUsage = message.usage;
|
|
1363
|
-
usage = {
|
|
1364
|
-
inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
|
|
1365
|
-
outputTokens: message.usage.output_tokens ?? 0,
|
|
1366
|
-
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)
|
|
1367
|
-
};
|
|
1368
|
-
this.logger.debug(
|
|
1369
|
-
`[claude-code] Token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
|
|
1370
|
-
);
|
|
1371
|
-
}
|
|
1372
|
-
finishReason = mapClaudeCodeFinishReason(message.subtype);
|
|
1373
|
-
this.logger.debug(`[claude-code] Finish reason: ${finishReason}`);
|
|
1374
|
-
} else if (message.type === "system" && message.subtype === "init") {
|
|
1375
|
-
this.setSessionId(message.session_id);
|
|
1376
|
-
this.logger.info(`[claude-code] Session initialized: ${message.session_id}`);
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
} catch (error) {
|
|
1380
|
-
done();
|
|
1381
|
-
this.logger.debug(
|
|
1382
|
-
`[claude-code] Error during doGenerate: ${error instanceof Error ? error.message : String(error)}`
|
|
1383
|
-
);
|
|
1384
|
-
if (isAbortError(error)) {
|
|
1385
|
-
this.logger.debug("[claude-code] Request aborted by user");
|
|
1386
|
-
throw options.abortSignal?.aborted ? options.abortSignal.reason : error;
|
|
1387
|
-
}
|
|
1388
|
-
if (isClaudeCodeTruncationError(error, text)) {
|
|
1389
|
-
this.logger.warn(
|
|
1390
|
-
`[claude-code] Detected truncated response, returning ${text.length} characters of buffered text`
|
|
1391
|
-
);
|
|
1392
|
-
wasTruncated = true;
|
|
1393
|
-
finishReason = "length";
|
|
1394
|
-
warnings.push({
|
|
1395
|
-
type: "other",
|
|
1396
|
-
message: CLAUDE_CODE_TRUNCATION_WARNING
|
|
1397
|
-
});
|
|
1398
|
-
} else {
|
|
1399
|
-
throw this.handleClaudeCodeError(error, messagesPrompt);
|
|
1400
|
-
}
|
|
1401
|
-
} finally {
|
|
1402
|
-
if (options.abortSignal && abortListener) {
|
|
1403
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
if (options.responseFormat?.type === "json" && text) {
|
|
1407
|
-
text = this.handleJsonExtraction(text, warnings);
|
|
1408
|
-
}
|
|
1409
|
-
return {
|
|
1410
|
-
content: [{ type: "text", text }],
|
|
1411
|
-
usage,
|
|
1412
|
-
finishReason,
|
|
1413
|
-
warnings,
|
|
1414
|
-
response: {
|
|
1415
|
-
id: generateId(),
|
|
1416
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
1417
|
-
modelId: this.modelId
|
|
1418
|
-
},
|
|
1419
|
-
request: {
|
|
1420
|
-
body: messagesPrompt
|
|
1421
|
-
},
|
|
1422
|
-
providerMetadata: {
|
|
1423
|
-
"claude-code": {
|
|
1424
|
-
...this.sessionId !== void 0 && { sessionId: this.sessionId },
|
|
1425
|
-
...costUsd !== void 0 && { costUsd },
|
|
1426
|
-
...durationMs !== void 0 && { durationMs },
|
|
1427
|
-
...rawUsage !== void 0 && { rawUsage },
|
|
1428
|
-
...wasTruncated && { truncated: true }
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
};
|
|
1432
|
-
}
|
|
1433
|
-
async doStream(options) {
|
|
1434
|
-
this.logger.debug(`[claude-code] Starting doStream request with model: ${this.modelId}`);
|
|
1435
|
-
const mode = options.responseFormat?.type === "json" ? { type: "object-json" } : { type: "regular" };
|
|
1436
|
-
this.logger.debug(
|
|
1437
|
-
`[claude-code] Stream mode: ${mode.type}, response format: ${options.responseFormat?.type ?? "none"}`
|
|
1438
|
-
);
|
|
1439
|
-
const {
|
|
1440
|
-
messagesPrompt,
|
|
1441
|
-
warnings: messageWarnings,
|
|
1442
|
-
streamingContentParts,
|
|
1443
|
-
hasImageParts
|
|
1444
|
-
} = convertToClaudeCodeMessages(
|
|
1445
|
-
options.prompt,
|
|
1446
|
-
mode,
|
|
1447
|
-
options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0
|
|
1448
|
-
);
|
|
1449
|
-
this.logger.debug(
|
|
1450
|
-
`[claude-code] Converted ${options.prompt.length} messages for streaming, hasImageParts: ${hasImageParts}`
|
|
1451
|
-
);
|
|
1452
|
-
const abortController = new AbortController();
|
|
1453
|
-
let abortListener;
|
|
1454
|
-
if (options.abortSignal?.aborted) {
|
|
1455
|
-
abortController.abort(options.abortSignal.reason);
|
|
1456
|
-
} else if (options.abortSignal) {
|
|
1457
|
-
abortListener = () => abortController.abort(options.abortSignal?.reason);
|
|
1458
|
-
options.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
1459
|
-
}
|
|
1460
|
-
const queryOptions = this.createQueryOptions(abortController);
|
|
1461
|
-
const warnings = this.generateAllWarnings(
|
|
1462
|
-
options,
|
|
1463
|
-
messagesPrompt
|
|
1464
|
-
);
|
|
1465
|
-
if (messageWarnings) {
|
|
1466
|
-
messageWarnings.forEach((warning) => {
|
|
1467
|
-
warnings.push({
|
|
1468
|
-
type: "other",
|
|
1469
|
-
message: warning
|
|
1470
|
-
});
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
const modeSetting = this.settings.streamingInput ?? "auto";
|
|
1474
|
-
const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
|
|
1475
|
-
if (!wantsStreamInput && hasImageParts) {
|
|
1476
|
-
warnings.push({
|
|
1477
|
-
type: "other",
|
|
1478
|
-
message: STREAMING_FEATURE_WARNING
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
|
-
const stream = new ReadableStream({
|
|
1482
|
-
start: async (controller) => {
|
|
1483
|
-
let done = () => {
|
|
1484
|
-
};
|
|
1485
|
-
const outputStreamEnded = new Promise((resolve) => {
|
|
1486
|
-
done = () => resolve(void 0);
|
|
1487
|
-
});
|
|
1488
|
-
const toolStates = /* @__PURE__ */ new Map();
|
|
1489
|
-
const streamWarnings = [];
|
|
1490
|
-
const closeToolInput = (toolId, state) => {
|
|
1491
|
-
if (!state.inputClosed && state.inputStarted) {
|
|
1492
|
-
controller.enqueue({
|
|
1493
|
-
type: "tool-input-end",
|
|
1494
|
-
id: toolId
|
|
1495
|
-
});
|
|
1496
|
-
state.inputClosed = true;
|
|
1497
|
-
}
|
|
1498
|
-
};
|
|
1499
|
-
const emitToolCall = (toolId, state) => {
|
|
1500
|
-
if (state.callEmitted) {
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
|
-
closeToolInput(toolId, state);
|
|
1504
|
-
controller.enqueue({
|
|
1505
|
-
type: "tool-call",
|
|
1506
|
-
toolCallId: toolId,
|
|
1507
|
-
toolName: state.name,
|
|
1508
|
-
input: state.lastSerializedInput ?? "",
|
|
1509
|
-
providerExecuted: true,
|
|
1510
|
-
dynamic: true,
|
|
1511
|
-
// V3 field: indicates tool is provider-defined (not in user's tools map)
|
|
1512
|
-
providerMetadata: {
|
|
1513
|
-
"claude-code": {
|
|
1514
|
-
// rawInput preserves the original serialized format before AI SDK normalization.
|
|
1515
|
-
// Use this if you need the exact string sent to the Claude CLI, which may differ
|
|
1516
|
-
// from the `input` field after AI SDK processing.
|
|
1517
|
-
rawInput: state.lastSerializedInput ?? ""
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
});
|
|
1521
|
-
state.callEmitted = true;
|
|
1522
|
-
};
|
|
1523
|
-
const finalizeToolCalls = () => {
|
|
1524
|
-
for (const [toolId, state] of toolStates) {
|
|
1525
|
-
emitToolCall(toolId, state);
|
|
1526
|
-
}
|
|
1527
|
-
toolStates.clear();
|
|
1528
|
-
};
|
|
1529
|
-
let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1530
|
-
let accumulatedText = "";
|
|
1531
|
-
let textPartId;
|
|
1532
|
-
try {
|
|
1533
|
-
controller.enqueue({ type: "stream-start", warnings });
|
|
1534
|
-
if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
|
|
1535
|
-
throw new Error(
|
|
1536
|
-
"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."
|
|
1537
|
-
);
|
|
1538
|
-
}
|
|
1539
|
-
const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
|
|
1540
|
-
messagesPrompt,
|
|
1541
|
-
outputStreamEnded,
|
|
1542
|
-
this.settings.resume ?? this.sessionId,
|
|
1543
|
-
streamingContentParts
|
|
1544
|
-
) : messagesPrompt;
|
|
1545
|
-
this.logger.debug(
|
|
1546
|
-
`[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
|
|
1547
|
-
);
|
|
1548
|
-
const response = this.queryFn({
|
|
1549
|
-
prompt: sdkPrompt,
|
|
1550
|
-
options: queryOptions
|
|
1551
|
-
});
|
|
1552
|
-
for await (const message of response) {
|
|
1553
|
-
this.logger.debug(`[claude-code] Stream received message type: ${message.type}`);
|
|
1554
|
-
if (message.type === "assistant") {
|
|
1555
|
-
if (!message.message?.content) {
|
|
1556
|
-
this.logger.warn(
|
|
1557
|
-
`[claude-code] Unexpected assistant message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
|
|
1558
|
-
);
|
|
1559
|
-
continue;
|
|
1560
|
-
}
|
|
1561
|
-
const content = message.message.content;
|
|
1562
|
-
for (const tool3 of this.extractToolUses(content)) {
|
|
1563
|
-
const toolId = tool3.id;
|
|
1564
|
-
let state = toolStates.get(toolId);
|
|
1565
|
-
if (!state) {
|
|
1566
|
-
state = {
|
|
1567
|
-
name: tool3.name,
|
|
1568
|
-
inputStarted: false,
|
|
1569
|
-
inputClosed: false,
|
|
1570
|
-
callEmitted: false
|
|
1571
|
-
};
|
|
1572
|
-
toolStates.set(toolId, state);
|
|
1573
|
-
this.logger.debug(
|
|
1574
|
-
`[claude-code] New tool use detected - Tool: ${tool3.name}, ID: ${toolId}`
|
|
1575
|
-
);
|
|
1576
|
-
}
|
|
1577
|
-
state.name = tool3.name;
|
|
1578
|
-
if (!state.inputStarted) {
|
|
1579
|
-
this.logger.debug(
|
|
1580
|
-
`[claude-code] Tool input started - Tool: ${tool3.name}, ID: ${toolId}`
|
|
1581
|
-
);
|
|
1582
|
-
controller.enqueue({
|
|
1583
|
-
type: "tool-input-start",
|
|
1584
|
-
id: toolId,
|
|
1585
|
-
toolName: tool3.name,
|
|
1586
|
-
providerExecuted: true,
|
|
1587
|
-
dynamic: true
|
|
1588
|
-
// V3 field: indicates tool is provider-defined
|
|
1589
|
-
});
|
|
1590
|
-
state.inputStarted = true;
|
|
1591
|
-
}
|
|
1592
|
-
const serializedInput = this.serializeToolInput(tool3.input);
|
|
1593
|
-
if (serializedInput) {
|
|
1594
|
-
let deltaPayload = "";
|
|
1595
|
-
if (state.lastSerializedInput === void 0) {
|
|
1596
|
-
if (serializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE) {
|
|
1597
|
-
deltaPayload = serializedInput;
|
|
1598
|
-
}
|
|
1599
|
-
} else if (serializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE && state.lastSerializedInput.length <= _ClaudeCodeLanguageModel.MAX_DELTA_CALC_SIZE && serializedInput.startsWith(state.lastSerializedInput)) {
|
|
1600
|
-
deltaPayload = serializedInput.slice(state.lastSerializedInput.length);
|
|
1601
|
-
} else if (serializedInput !== state.lastSerializedInput) {
|
|
1602
|
-
deltaPayload = "";
|
|
1603
|
-
}
|
|
1604
|
-
if (deltaPayload) {
|
|
1605
|
-
controller.enqueue({
|
|
1606
|
-
type: "tool-input-delta",
|
|
1607
|
-
id: toolId,
|
|
1608
|
-
delta: deltaPayload
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
state.lastSerializedInput = serializedInput;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
const text = content.map((c) => c.type === "text" ? c.text : "").join("");
|
|
1615
|
-
if (text) {
|
|
1616
|
-
accumulatedText += text;
|
|
1617
|
-
if (options.responseFormat?.type !== "json") {
|
|
1618
|
-
if (!textPartId) {
|
|
1619
|
-
textPartId = generateId();
|
|
1620
|
-
controller.enqueue({
|
|
1621
|
-
type: "text-start",
|
|
1622
|
-
id: textPartId
|
|
1623
|
-
});
|
|
1624
|
-
}
|
|
1625
|
-
controller.enqueue({
|
|
1626
|
-
type: "text-delta",
|
|
1627
|
-
id: textPartId,
|
|
1628
|
-
delta: text
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
} else if (message.type === "user") {
|
|
1633
|
-
if (!message.message?.content) {
|
|
1634
|
-
this.logger.warn(
|
|
1635
|
-
`[claude-code] Unexpected user message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
|
|
1636
|
-
);
|
|
1637
|
-
continue;
|
|
1638
|
-
}
|
|
1639
|
-
const content = message.message.content;
|
|
1640
|
-
for (const result of this.extractToolResults(content)) {
|
|
1641
|
-
let state = toolStates.get(result.id);
|
|
1642
|
-
const toolName = result.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
1643
|
-
this.logger.debug(
|
|
1644
|
-
`[claude-code] Tool result received - Tool: ${toolName}, ID: ${result.id}`
|
|
1645
|
-
);
|
|
1646
|
-
if (!state) {
|
|
1647
|
-
this.logger.warn(
|
|
1648
|
-
`[claude-code] Received tool result for unknown tool ID: ${result.id}`
|
|
1649
|
-
);
|
|
1650
|
-
state = {
|
|
1651
|
-
name: toolName,
|
|
1652
|
-
inputStarted: false,
|
|
1653
|
-
inputClosed: false,
|
|
1654
|
-
callEmitted: false
|
|
1655
|
-
};
|
|
1656
|
-
toolStates.set(result.id, state);
|
|
1657
|
-
if (!state.inputStarted) {
|
|
1658
|
-
controller.enqueue({
|
|
1659
|
-
type: "tool-input-start",
|
|
1660
|
-
id: result.id,
|
|
1661
|
-
toolName,
|
|
1662
|
-
providerExecuted: true,
|
|
1663
|
-
dynamic: true
|
|
1664
|
-
// V3 field: indicates tool is provider-defined
|
|
1665
|
-
});
|
|
1666
|
-
state.inputStarted = true;
|
|
1667
|
-
}
|
|
1668
|
-
if (!state.inputClosed) {
|
|
1669
|
-
controller.enqueue({
|
|
1670
|
-
type: "tool-input-end",
|
|
1671
|
-
id: result.id
|
|
1672
|
-
});
|
|
1673
|
-
state.inputClosed = true;
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
state.name = toolName;
|
|
1677
|
-
const normalizedResult = this.normalizeToolResult(result.result);
|
|
1678
|
-
const rawResult = typeof result.result === "string" ? result.result : (() => {
|
|
1679
|
-
try {
|
|
1680
|
-
return JSON.stringify(result.result);
|
|
1681
|
-
} catch {
|
|
1682
|
-
return String(result.result);
|
|
1683
|
-
}
|
|
1684
|
-
})();
|
|
1685
|
-
emitToolCall(result.id, state);
|
|
1686
|
-
controller.enqueue({
|
|
1687
|
-
type: "tool-result",
|
|
1688
|
-
toolCallId: result.id,
|
|
1689
|
-
toolName,
|
|
1690
|
-
result: normalizedResult,
|
|
1691
|
-
isError: result.isError,
|
|
1692
|
-
providerExecuted: true,
|
|
1693
|
-
dynamic: true,
|
|
1694
|
-
// V3 field: indicates tool is provider-defined
|
|
1695
|
-
providerMetadata: {
|
|
1696
|
-
"claude-code": {
|
|
1697
|
-
// rawResult preserves the original CLI output string before JSON parsing.
|
|
1698
|
-
// Use this when you need the exact string returned by the tool, especially
|
|
1699
|
-
// if the `result` field has been parsed/normalized and you need the original format.
|
|
1700
|
-
rawResult
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
});
|
|
1704
|
-
}
|
|
1705
|
-
for (const error of this.extractToolErrors(content)) {
|
|
1706
|
-
let state = toolStates.get(error.id);
|
|
1707
|
-
const toolName = error.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
|
|
1708
|
-
this.logger.debug(
|
|
1709
|
-
`[claude-code] Tool error received - Tool: ${toolName}, ID: ${error.id}`
|
|
1710
|
-
);
|
|
1711
|
-
if (!state) {
|
|
1712
|
-
this.logger.warn(
|
|
1713
|
-
`[claude-code] Received tool error for unknown tool ID: ${error.id}`
|
|
1714
|
-
);
|
|
1715
|
-
state = {
|
|
1716
|
-
name: toolName,
|
|
1717
|
-
inputStarted: true,
|
|
1718
|
-
inputClosed: true,
|
|
1719
|
-
callEmitted: false
|
|
1720
|
-
};
|
|
1721
|
-
toolStates.set(error.id, state);
|
|
1722
|
-
}
|
|
1723
|
-
emitToolCall(error.id, state);
|
|
1724
|
-
const rawError = typeof error.error === "string" ? error.error : typeof error.error === "object" && error.error !== null ? (() => {
|
|
1725
|
-
try {
|
|
1726
|
-
return JSON.stringify(error.error);
|
|
1727
|
-
} catch {
|
|
1728
|
-
return String(error.error);
|
|
1729
|
-
}
|
|
1730
|
-
})() : String(error.error);
|
|
1731
|
-
controller.enqueue({
|
|
1732
|
-
type: "tool-error",
|
|
1733
|
-
toolCallId: error.id,
|
|
1734
|
-
toolName,
|
|
1735
|
-
error: rawError,
|
|
1736
|
-
providerExecuted: true,
|
|
1737
|
-
dynamic: true,
|
|
1738
|
-
// V3 field: indicates tool is provider-defined
|
|
1739
|
-
providerMetadata: {
|
|
1740
|
-
"claude-code": {
|
|
1741
|
-
rawError
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
});
|
|
1745
|
-
}
|
|
1746
|
-
} else if (message.type === "result") {
|
|
1747
|
-
done();
|
|
1748
|
-
this.logger.info(
|
|
1749
|
-
`[claude-code] Stream completed - Session: ${message.session_id}, Cost: $${message.total_cost_usd?.toFixed(4) ?? "N/A"}, Duration: ${message.duration_ms ?? "N/A"}ms`
|
|
1750
|
-
);
|
|
1751
|
-
let rawUsage;
|
|
1752
|
-
if ("usage" in message) {
|
|
1753
|
-
rawUsage = message.usage;
|
|
1754
|
-
usage = {
|
|
1755
|
-
inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
|
|
1756
|
-
outputTokens: message.usage.output_tokens ?? 0,
|
|
1757
|
-
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)
|
|
1758
|
-
};
|
|
1759
|
-
this.logger.debug(
|
|
1760
|
-
`[claude-code] Stream token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
|
|
1761
|
-
);
|
|
1762
|
-
}
|
|
1763
|
-
const finishReason = mapClaudeCodeFinishReason(
|
|
1764
|
-
message.subtype
|
|
1765
|
-
);
|
|
1766
|
-
this.logger.debug(`[claude-code] Stream finish reason: ${finishReason}`);
|
|
1767
|
-
this.setSessionId(message.session_id);
|
|
1768
|
-
if (options.responseFormat?.type === "json" && accumulatedText) {
|
|
1769
|
-
const extractedJson = this.handleJsonExtraction(accumulatedText, streamWarnings);
|
|
1770
|
-
const jsonTextId = generateId();
|
|
1771
|
-
controller.enqueue({
|
|
1772
|
-
type: "text-start",
|
|
1773
|
-
id: jsonTextId
|
|
1774
|
-
});
|
|
1775
|
-
controller.enqueue({
|
|
1776
|
-
type: "text-delta",
|
|
1777
|
-
id: jsonTextId,
|
|
1778
|
-
delta: extractedJson
|
|
1779
|
-
});
|
|
1780
|
-
controller.enqueue({
|
|
1781
|
-
type: "text-end",
|
|
1782
|
-
id: jsonTextId
|
|
1783
|
-
});
|
|
1784
|
-
} else if (textPartId) {
|
|
1785
|
-
controller.enqueue({
|
|
1786
|
-
type: "text-end",
|
|
1787
|
-
id: textPartId
|
|
1788
|
-
});
|
|
1789
|
-
}
|
|
1790
|
-
finalizeToolCalls();
|
|
1791
|
-
const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
|
|
1792
|
-
controller.enqueue({
|
|
1793
|
-
type: "finish",
|
|
1794
|
-
finishReason,
|
|
1795
|
-
usage,
|
|
1796
|
-
providerMetadata: {
|
|
1797
|
-
"claude-code": {
|
|
1798
|
-
sessionId: message.session_id,
|
|
1799
|
-
...message.total_cost_usd !== void 0 && {
|
|
1800
|
-
costUsd: message.total_cost_usd
|
|
1801
|
-
},
|
|
1802
|
-
...message.duration_ms !== void 0 && { durationMs: message.duration_ms },
|
|
1803
|
-
...rawUsage !== void 0 && { rawUsage },
|
|
1804
|
-
// JSON validation warnings are collected during streaming and included
|
|
1805
|
-
// in providerMetadata since the AI SDK's finish event doesn't support
|
|
1806
|
-
// a top-level warnings field (unlike stream-start which was already emitted)
|
|
1807
|
-
...streamWarnings.length > 0 && {
|
|
1808
|
-
warnings: warningsJson
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
});
|
|
1813
|
-
} else if (message.type === "system" && message.subtype === "init") {
|
|
1814
|
-
this.setSessionId(message.session_id);
|
|
1815
|
-
this.logger.info(`[claude-code] Stream session initialized: ${message.session_id}`);
|
|
1816
|
-
controller.enqueue({
|
|
1817
|
-
type: "response-metadata",
|
|
1818
|
-
id: message.session_id,
|
|
1819
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
1820
|
-
modelId: this.modelId
|
|
1821
|
-
});
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
finalizeToolCalls();
|
|
1825
|
-
this.logger.debug("[claude-code] Stream finalized, closing stream");
|
|
1826
|
-
controller.close();
|
|
1827
|
-
} catch (error) {
|
|
1828
|
-
done();
|
|
1829
|
-
this.logger.debug(
|
|
1830
|
-
`[claude-code] Error during doStream: ${error instanceof Error ? error.message : String(error)}`
|
|
1831
|
-
);
|
|
1832
|
-
if (isClaudeCodeTruncationError(error, accumulatedText)) {
|
|
1833
|
-
this.logger.warn(
|
|
1834
|
-
`[claude-code] Detected truncated stream response, returning ${accumulatedText.length} characters of buffered text`
|
|
1835
|
-
);
|
|
1836
|
-
const truncationWarning = {
|
|
1837
|
-
type: "other",
|
|
1838
|
-
message: CLAUDE_CODE_TRUNCATION_WARNING
|
|
1839
|
-
};
|
|
1840
|
-
streamWarnings.push(truncationWarning);
|
|
1841
|
-
const emitJsonText = () => {
|
|
1842
|
-
const extractedJson = this.handleJsonExtraction(accumulatedText, streamWarnings);
|
|
1843
|
-
const jsonTextId = generateId();
|
|
1844
|
-
controller.enqueue({
|
|
1845
|
-
type: "text-start",
|
|
1846
|
-
id: jsonTextId
|
|
1847
|
-
});
|
|
1848
|
-
controller.enqueue({
|
|
1849
|
-
type: "text-delta",
|
|
1850
|
-
id: jsonTextId,
|
|
1851
|
-
delta: extractedJson
|
|
1852
|
-
});
|
|
1853
|
-
controller.enqueue({
|
|
1854
|
-
type: "text-end",
|
|
1855
|
-
id: jsonTextId
|
|
1856
|
-
});
|
|
1857
|
-
};
|
|
1858
|
-
if (options.responseFormat?.type === "json") {
|
|
1859
|
-
emitJsonText();
|
|
1860
|
-
} else if (textPartId) {
|
|
1861
|
-
controller.enqueue({
|
|
1862
|
-
type: "text-end",
|
|
1863
|
-
id: textPartId
|
|
1864
|
-
});
|
|
1865
|
-
} else if (accumulatedText) {
|
|
1866
|
-
const fallbackTextId = generateId();
|
|
1867
|
-
controller.enqueue({
|
|
1868
|
-
type: "text-start",
|
|
1869
|
-
id: fallbackTextId
|
|
1870
|
-
});
|
|
1871
|
-
controller.enqueue({
|
|
1872
|
-
type: "text-delta",
|
|
1873
|
-
id: fallbackTextId,
|
|
1874
|
-
delta: accumulatedText
|
|
1875
|
-
});
|
|
1876
|
-
controller.enqueue({
|
|
1877
|
-
type: "text-end",
|
|
1878
|
-
id: fallbackTextId
|
|
1879
|
-
});
|
|
1880
|
-
}
|
|
1881
|
-
finalizeToolCalls();
|
|
1882
|
-
const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
|
|
1883
|
-
controller.enqueue({
|
|
1884
|
-
type: "finish",
|
|
1885
|
-
finishReason: "length",
|
|
1886
|
-
usage,
|
|
1887
|
-
providerMetadata: {
|
|
1888
|
-
"claude-code": {
|
|
1889
|
-
...this.sessionId !== void 0 && { sessionId: this.sessionId },
|
|
1890
|
-
truncated: true,
|
|
1891
|
-
...streamWarnings.length > 0 && {
|
|
1892
|
-
warnings: warningsJson
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
});
|
|
1897
|
-
controller.close();
|
|
1898
|
-
return;
|
|
1899
|
-
}
|
|
1900
|
-
finalizeToolCalls();
|
|
1901
|
-
let errorToEmit;
|
|
1902
|
-
if (isAbortError(error)) {
|
|
1903
|
-
errorToEmit = options.abortSignal?.aborted ? options.abortSignal.reason : error;
|
|
1904
|
-
} else {
|
|
1905
|
-
errorToEmit = this.handleClaudeCodeError(error, messagesPrompt);
|
|
1906
|
-
}
|
|
1907
|
-
controller.enqueue({
|
|
1908
|
-
type: "error",
|
|
1909
|
-
error: errorToEmit
|
|
1910
|
-
});
|
|
1911
|
-
controller.close();
|
|
1912
|
-
} finally {
|
|
1913
|
-
if (options.abortSignal && abortListener) {
|
|
1914
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
},
|
|
1918
|
-
cancel: () => {
|
|
1919
|
-
if (options.abortSignal && abortListener) {
|
|
1920
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
});
|
|
1924
|
-
return {
|
|
1925
|
-
stream,
|
|
1926
|
-
request: {
|
|
1927
|
-
body: messagesPrompt
|
|
1928
|
-
}
|
|
1929
|
-
};
|
|
1930
|
-
}
|
|
1931
|
-
serializeWarningsForMetadata(warnings) {
|
|
1932
|
-
const result = warnings.map((w) => {
|
|
1933
|
-
const base = { type: w.type };
|
|
1934
|
-
if ("message" in w) {
|
|
1935
|
-
const m = w.message;
|
|
1936
|
-
if (m !== void 0) base.message = String(m);
|
|
1937
|
-
}
|
|
1938
|
-
if (w.type === "unsupported-setting") {
|
|
1939
|
-
const setting = w.setting;
|
|
1940
|
-
if (setting !== void 0) base.setting = String(setting);
|
|
1941
|
-
if ("details" in w) {
|
|
1942
|
-
const d = w.details;
|
|
1943
|
-
if (d !== void 0) base.details = String(d);
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
return base;
|
|
1947
|
-
});
|
|
1948
|
-
return result;
|
|
1949
|
-
}
|
|
1950
|
-
};
|
|
1951
|
-
|
|
1952
|
-
// src/claude-code-provider.ts
|
|
1953
|
-
function createClaudeCode(options = {}) {
|
|
1954
|
-
const logger = getLogger(options.defaultSettings?.logger);
|
|
1955
|
-
if (options.defaultSettings) {
|
|
1956
|
-
const validation = validateSettings(options.defaultSettings);
|
|
1957
|
-
if (!validation.valid) {
|
|
1958
|
-
throw new Error(`Invalid default settings: ${validation.errors.join(", ")}`);
|
|
1959
|
-
}
|
|
1960
|
-
if (validation.warnings.length > 0) {
|
|
1961
|
-
validation.warnings.forEach((warning) => logger.warn(`Claude Code Provider: ${warning}`));
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
const createModel = (modelId, settings = {}) => {
|
|
1965
|
-
const mergedSettings = {
|
|
1966
|
-
...options.defaultSettings,
|
|
1967
|
-
...settings
|
|
1968
|
-
};
|
|
1969
|
-
const validation = validateSettings(mergedSettings);
|
|
1970
|
-
if (!validation.valid) {
|
|
1971
|
-
throw new Error(`Invalid settings: ${validation.errors.join(", ")}`);
|
|
1972
|
-
}
|
|
1973
|
-
return new ClaudeCodeLanguageModel({
|
|
1974
|
-
id: modelId,
|
|
1975
|
-
settings: mergedSettings,
|
|
1976
|
-
settingsValidationWarnings: validation.warnings
|
|
1977
|
-
});
|
|
1978
|
-
};
|
|
1979
|
-
const provider = function(modelId, settings) {
|
|
1980
|
-
if (new.target) {
|
|
1981
|
-
throw new Error("The Claude Code model function cannot be called with the new keyword.");
|
|
1982
|
-
}
|
|
1983
|
-
return createModel(modelId, settings);
|
|
1984
|
-
};
|
|
1985
|
-
provider.languageModel = createModel;
|
|
1986
|
-
provider.chat = createModel;
|
|
1987
|
-
provider.textEmbeddingModel = (modelId) => {
|
|
1988
|
-
throw new NoSuchModelError({
|
|
1989
|
-
modelId,
|
|
1990
|
-
modelType: "textEmbeddingModel"
|
|
1991
|
-
});
|
|
1992
|
-
};
|
|
1993
|
-
provider.imageModel = (modelId) => {
|
|
1994
|
-
throw new NoSuchModelError({
|
|
1995
|
-
modelId,
|
|
1996
|
-
modelType: "imageModel"
|
|
1997
|
-
});
|
|
1998
|
-
};
|
|
1999
|
-
return provider;
|
|
2000
|
-
}
|
|
2001
|
-
var claudeCode = createClaudeCode();
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import WebSocket$1, { WebSocketServer, WebSocket } from 'ws';
|
|
14
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
15
|
+
import pg from 'pg';
|
|
16
|
+
import { pgTable, timestamp, boolean, text, uuid, index, uniqueIndex, integer, jsonb } from 'drizzle-orm/pg-core';
|
|
17
|
+
import { sql, eq, and, desc, isNull } from 'drizzle-orm';
|
|
18
|
+
import { randomUUID, createHash } from 'crypto';
|
|
19
|
+
import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
|
20
|
+
import { z } from 'zod';
|
|
21
|
+
import os$1 from 'node:os';
|
|
22
|
+
import { randomUUID as randomUUID$1 } from 'node:crypto';
|
|
23
|
+
import express from 'express';
|
|
24
|
+
import { spawn } from 'node:child_process';
|
|
25
|
+
import { EventEmitter } from 'node:events';
|
|
26
|
+
import { createServer, createConnection } from 'node:net';
|
|
27
|
+
import { readFile as readFile$1, rm, writeFile, readdir } from 'node:fs/promises';
|
|
28
|
+
import { simpleGit } from 'simple-git';
|
|
29
|
+
import * as os from 'os';
|
|
30
|
+
import { existsSync as existsSync$1, mkdirSync as mkdirSync$1 } from 'fs';
|
|
31
|
+
import { tunnelManager } from './chunks/manager-CvGX9qqe.js';
|
|
32
|
+
import 'chalk';
|
|
33
|
+
import 'http';
|
|
34
|
+
import 'http-proxy';
|
|
35
|
+
import 'zlib';
|
|
2002
36
|
|
|
2003
37
|
/**
|
|
2004
38
|
* Creates a permission handler that restricts Claude to a specific project directory
|
|
@@ -4133,7 +2167,7 @@ var BuildLogger$1 = class BuildLogger {
|
|
|
4133
2167
|
if (typeof process !== "undefined" && process.env.NODE_ENV !== "test") {
|
|
4134
2168
|
try {
|
|
4135
2169
|
if (level === "warn" || level === "error") {
|
|
4136
|
-
addBreadcrumb({
|
|
2170
|
+
Sentry.addBreadcrumb({
|
|
4137
2171
|
category: `build-logger.${context}`,
|
|
4138
2172
|
message,
|
|
4139
2173
|
level: level === "error" ? "error" : "warning",
|
|
@@ -5605,7 +3639,7 @@ var BuildWebSocketServer = class {
|
|
|
5605
3639
|
pingInterval,
|
|
5606
3640
|
userId: runnerUserId
|
|
5607
3641
|
});
|
|
5608
|
-
addBreadcrumb({
|
|
3642
|
+
Sentry.addBreadcrumb({
|
|
5609
3643
|
category: "websocket",
|
|
5610
3644
|
message: `Runner connected: ${runnerId}`,
|
|
5611
3645
|
level: "info"
|
|
@@ -5639,7 +3673,7 @@ var BuildWebSocketServer = class {
|
|
|
5639
3673
|
}
|
|
5640
3674
|
} catch (error) {
|
|
5641
3675
|
this.runnerTotalErrors++;
|
|
5642
|
-
captureException(error, {
|
|
3676
|
+
Sentry.captureException(error, {
|
|
5643
3677
|
tags: { runnerId, source: "websocket_message" },
|
|
5644
3678
|
level: "error"
|
|
5645
3679
|
});
|
|
@@ -5656,7 +3690,7 @@ var BuildWebSocketServer = class {
|
|
|
5656
3690
|
httpProxyManager.cancelRequestsForRunner(runnerId);
|
|
5657
3691
|
hmrProxyManager$1.disconnectRunner(runnerId);
|
|
5658
3692
|
this.cleanupRunnerProcesses(runnerId);
|
|
5659
|
-
addBreadcrumb({
|
|
3693
|
+
Sentry.addBreadcrumb({
|
|
5660
3694
|
category: "websocket",
|
|
5661
3695
|
message: `Runner disconnected: ${runnerId}`,
|
|
5662
3696
|
level: "info",
|
|
@@ -5666,7 +3700,7 @@ var BuildWebSocketServer = class {
|
|
|
5666
3700
|
ws.on("error", (error) => {
|
|
5667
3701
|
buildLogger$2.websocket.error("Runner socket error", error, { runnerId });
|
|
5668
3702
|
this.runnerTotalErrors++;
|
|
5669
|
-
captureException(error, {
|
|
3703
|
+
Sentry.captureException(error, {
|
|
5670
3704
|
tags: { runnerId, source: "websocket_error" },
|
|
5671
3705
|
level: "error"
|
|
5672
3706
|
});
|
|
@@ -5707,10 +3741,10 @@ var BuildWebSocketServer = class {
|
|
|
5707
3741
|
return false;
|
|
5708
3742
|
}
|
|
5709
3743
|
try {
|
|
5710
|
-
const activeSpan = getActiveSpan();
|
|
3744
|
+
const activeSpan = Sentry.getActiveSpan();
|
|
5711
3745
|
const hasTrace = !!activeSpan;
|
|
5712
3746
|
if (activeSpan) {
|
|
5713
|
-
const traceData = getTraceData();
|
|
3747
|
+
const traceData = Sentry.getTraceData();
|
|
5714
3748
|
if (traceData["sentry-trace"]) {
|
|
5715
3749
|
command._sentry = {
|
|
5716
3750
|
trace: traceData["sentry-trace"],
|
|
@@ -5727,7 +3761,7 @@ var BuildWebSocketServer = class {
|
|
|
5727
3761
|
return true;
|
|
5728
3762
|
} catch (error) {
|
|
5729
3763
|
this.runnerTotalErrors++;
|
|
5730
|
-
captureException(error, {
|
|
3764
|
+
Sentry.captureException(error, {
|
|
5731
3765
|
tags: { runnerId, commandType: command.type },
|
|
5732
3766
|
level: "error"
|
|
5733
3767
|
});
|
|
@@ -5793,7 +3827,7 @@ var BuildWebSocketServer = class {
|
|
|
5793
3827
|
for (const [runnerId, conn] of this.runnerConnections.entries()) {
|
|
5794
3828
|
if (now - conn.lastHeartbeat > this.RUNNER_HEARTBEAT_TIMEOUT) {
|
|
5795
3829
|
buildLogger$2.websocket.runnerStaleRemoved(runnerId);
|
|
5796
|
-
addBreadcrumb({
|
|
3830
|
+
Sentry.addBreadcrumb({
|
|
5797
3831
|
category: "websocket",
|
|
5798
3832
|
message: `Stale runner connection removed: ${runnerId}`,
|
|
5799
3833
|
level: "warning",
|
|
@@ -5836,7 +3870,7 @@ var BuildWebSocketServer = class {
|
|
|
5836
3870
|
runnerId,
|
|
5837
3871
|
projectIds
|
|
5838
3872
|
});
|
|
5839
|
-
addBreadcrumb({
|
|
3873
|
+
Sentry.addBreadcrumb({
|
|
5840
3874
|
category: "websocket",
|
|
5841
3875
|
message: `Cleaned up processes for disconnected runner`,
|
|
5842
3876
|
level: "info",
|
|
@@ -5851,7 +3885,7 @@ var BuildWebSocketServer = class {
|
|
|
5851
3885
|
}
|
|
5852
3886
|
} catch (error) {
|
|
5853
3887
|
buildLogger$2.websocket.error("Failed to cleanup runner processes", error, { runnerId });
|
|
5854
|
-
captureException(error, {
|
|
3888
|
+
Sentry.captureException(error, {
|
|
5855
3889
|
tags: { runnerId, source: "runner_cleanup" },
|
|
5856
3890
|
level: "error"
|
|
5857
3891
|
});
|
|
@@ -6002,10 +4036,10 @@ var BuildWebSocketServer = class {
|
|
|
6002
4036
|
updates: []
|
|
6003
4037
|
});
|
|
6004
4038
|
}
|
|
6005
|
-
const activeSpan = getActiveSpan();
|
|
4039
|
+
const activeSpan = Sentry.getActiveSpan();
|
|
6006
4040
|
const traceContext = activeSpan ? {
|
|
6007
|
-
trace: getTraceData()["sentry-trace"],
|
|
6008
|
-
baggage: getTraceData().baggage
|
|
4041
|
+
trace: Sentry.getTraceData()["sentry-trace"],
|
|
4042
|
+
baggage: Sentry.getTraceData().baggage
|
|
6009
4043
|
} : void 0;
|
|
6010
4044
|
const batch = this.pendingUpdates.get(key);
|
|
6011
4045
|
batch.updates.push({
|
|
@@ -6037,10 +4071,10 @@ var BuildWebSocketServer = class {
|
|
|
6037
4071
|
updates: []
|
|
6038
4072
|
});
|
|
6039
4073
|
}
|
|
6040
|
-
const activeSpan = getActiveSpan();
|
|
4074
|
+
const activeSpan = Sentry.getActiveSpan();
|
|
6041
4075
|
const traceContext = activeSpan ? {
|
|
6042
|
-
trace: getTraceData()["sentry-trace"],
|
|
6043
|
-
baggage: getTraceData().baggage
|
|
4076
|
+
trace: Sentry.getTraceData()["sentry-trace"],
|
|
4077
|
+
baggage: Sentry.getTraceData().baggage
|
|
6044
4078
|
} : void 0;
|
|
6045
4079
|
const batch = this.pendingUpdates.get(key);
|
|
6046
4080
|
batch.updates.push({
|
|
@@ -6483,34 +4517,34 @@ var AVAILABLE_ICONS = [
|
|
|
6483
4517
|
];
|
|
6484
4518
|
|
|
6485
4519
|
var AgentCore = /*#__PURE__*/Object.freeze({
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
4520
|
+
__proto__: null,
|
|
4521
|
+
AVAILABLE_ICONS: AVAILABLE_ICONS,
|
|
4522
|
+
CLAUDE_SYSTEM_PROMPT: CLAUDE_SYSTEM_PROMPT,
|
|
4523
|
+
CODEX_SYSTEM_PROMPT: CODEX_SYSTEM_PROMPT,
|
|
4524
|
+
DEFAULT_AGENT_ID: DEFAULT_AGENT_ID,
|
|
4525
|
+
DEFAULT_CLAUDE_MODEL_ID: DEFAULT_CLAUDE_MODEL_ID,
|
|
4526
|
+
DEFAULT_OPENCODE_MODEL_ID: DEFAULT_OPENCODE_MODEL_ID,
|
|
4527
|
+
GITHUB_CHAT_MESSAGES: GITHUB_CHAT_MESSAGES,
|
|
4528
|
+
LEGACY_MODEL_MAP: LEGACY_MODEL_MAP,
|
|
4529
|
+
MODEL_METADATA: MODEL_METADATA,
|
|
4530
|
+
NEONDB_CHAT_MESSAGES: NEONDB_CHAT_MESSAGES,
|
|
4531
|
+
ProjectMetadataSchema: ProjectMetadataSchema,
|
|
4532
|
+
ProjectNamingSchema: ProjectNamingSchema,
|
|
4533
|
+
TemplateAnalysisSchema: TemplateAnalysisSchema,
|
|
4534
|
+
buildLogger: buildLogger$2,
|
|
4535
|
+
buildWebSocketServer: buildWebSocketServer,
|
|
4536
|
+
db: db,
|
|
4537
|
+
getDb: getDb,
|
|
4538
|
+
getModelLabel: getModelLabel,
|
|
4539
|
+
initializeDatabase: initializeDatabase,
|
|
4540
|
+
isRunnerCommand: isRunnerCommand,
|
|
4541
|
+
isRunnerEvent: isRunnerEvent,
|
|
4542
|
+
normalizeModelId: normalizeModelId,
|
|
4543
|
+
parseModelId: parseModelId,
|
|
4544
|
+
resetDatabase: resetDatabase,
|
|
4545
|
+
resolveAgentStrategy: resolveAgentStrategy$1,
|
|
4546
|
+
runMigrations: runMigrations,
|
|
4547
|
+
setTemplatesPath: setTemplatesPath$1
|
|
6514
4548
|
});
|
|
6515
4549
|
|
|
6516
4550
|
/**
|
|
@@ -6529,9 +4563,9 @@ var AgentCore = /*#__PURE__*/Object.freeze({
|
|
|
6529
4563
|
* - Direct streaming without adaptation layer
|
|
6530
4564
|
*/
|
|
6531
4565
|
// Debug logging helper - suppressed in TUI mode (SILENT_MODE=1)
|
|
6532
|
-
const debugLog$
|
|
4566
|
+
const debugLog$3 = (message) => {
|
|
6533
4567
|
if (process.env.SILENT_MODE !== '1' && process.env.DEBUG_BUILD === '1') {
|
|
6534
|
-
debugLog$
|
|
4568
|
+
debugLog$3();
|
|
6535
4569
|
}
|
|
6536
4570
|
};
|
|
6537
4571
|
/**
|
|
@@ -6650,10 +4684,10 @@ function buildPromptWithImages(prompt, messageParts) {
|
|
|
6650
4684
|
*/
|
|
6651
4685
|
function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortController) {
|
|
6652
4686
|
return async function* nativeClaudeQuery(prompt, workingDirectory, systemPrompt, _agent, _codexThreadId, messageParts) {
|
|
6653
|
-
debugLog$
|
|
6654
|
-
debugLog$
|
|
6655
|
-
debugLog$
|
|
6656
|
-
debugLog$
|
|
4687
|
+
debugLog$3();
|
|
4688
|
+
debugLog$3();
|
|
4689
|
+
debugLog$3();
|
|
4690
|
+
debugLog$3(`[runner] [native-sdk] Prompt length: ${prompt.length}\n`);
|
|
6657
4691
|
// Build combined system prompt
|
|
6658
4692
|
const systemPromptSegments = [CLAUDE_SYSTEM_PROMPT.trim()];
|
|
6659
4693
|
if (systemPrompt && systemPrompt.trim().length > 0) {
|
|
@@ -6661,7 +4695,7 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
6661
4695
|
}
|
|
6662
4696
|
const appendedSystemPrompt = systemPromptSegments.join('\n\n');
|
|
6663
4697
|
// Ensure working directory exists
|
|
6664
|
-
if (!existsSync
|
|
4698
|
+
if (!existsSync(workingDirectory)) {
|
|
6665
4699
|
console.log(`[native-sdk] Creating working directory: ${workingDirectory}`);
|
|
6666
4700
|
mkdirSync(workingDirectory, { recursive: true });
|
|
6667
4701
|
}
|
|
@@ -6669,7 +4703,7 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
6669
4703
|
const hasImages = messageParts?.some(p => p.type === 'image');
|
|
6670
4704
|
if (hasImages) {
|
|
6671
4705
|
messageParts?.filter(p => p.type === 'image').length || 0;
|
|
6672
|
-
debugLog$
|
|
4706
|
+
debugLog$3();
|
|
6673
4707
|
}
|
|
6674
4708
|
// Build the final prompt
|
|
6675
4709
|
const finalPrompt = buildPromptWithImages(prompt);
|
|
@@ -6703,7 +4737,7 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
6703
4737
|
// See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/46
|
|
6704
4738
|
abortController,
|
|
6705
4739
|
};
|
|
6706
|
-
debugLog$
|
|
4740
|
+
debugLog$3();
|
|
6707
4741
|
let messageCount = 0;
|
|
6708
4742
|
let toolCallCount = 0;
|
|
6709
4743
|
let textBlockCount = 0;
|
|
@@ -6720,7 +4754,7 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
6720
4754
|
for (const block of transformed.message.content) {
|
|
6721
4755
|
if (block.type === 'tool_use') {
|
|
6722
4756
|
toolCallCount++;
|
|
6723
|
-
debugLog$
|
|
4757
|
+
debugLog$3(`[runner] [native-sdk] 🔧 Tool call: ${block.name}\n`);
|
|
6724
4758
|
}
|
|
6725
4759
|
else if (block.type === 'text') {
|
|
6726
4760
|
textBlockCount++;
|
|
@@ -6732,29 +4766,22 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
6732
4766
|
// Log result messages
|
|
6733
4767
|
if (sdkMessage.type === 'result') {
|
|
6734
4768
|
if (sdkMessage.subtype === 'success') {
|
|
6735
|
-
debugLog$
|
|
4769
|
+
debugLog$3(`[runner] [native-sdk] ✅ Query complete - ${sdkMessage.num_turns} turns, $${sdkMessage.total_cost_usd?.toFixed(4)} USD\n`);
|
|
6736
4770
|
}
|
|
6737
4771
|
else {
|
|
6738
|
-
debugLog$
|
|
4772
|
+
debugLog$3(`[runner] [native-sdk] ⚠️ Query ended with: ${sdkMessage.subtype}\n`);
|
|
6739
4773
|
}
|
|
6740
4774
|
}
|
|
6741
4775
|
}
|
|
6742
|
-
debugLog$
|
|
4776
|
+
debugLog$3(`[runner] [native-sdk] 📊 Stream complete - ${messageCount} messages, ${toolCallCount} tool calls, ${textBlockCount} text blocks\n`);
|
|
6743
4777
|
}
|
|
6744
4778
|
catch (error) {
|
|
6745
|
-
debugLog$
|
|
6746
|
-
captureException(error);
|
|
4779
|
+
debugLog$3(`[runner] [native-sdk] ❌ Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
4780
|
+
Sentry.captureException(error);
|
|
6747
4781
|
throw error;
|
|
6748
4782
|
}
|
|
6749
4783
|
};
|
|
6750
4784
|
}
|
|
6751
|
-
/**
|
|
6752
|
-
* Feature flag to control which implementation to use
|
|
6753
|
-
*
|
|
6754
|
-
* Default: Native SDK is enabled (true)
|
|
6755
|
-
* Set USE_LEGACY_AI_SDK=1 to use the old AI SDK + community provider path
|
|
6756
|
-
*/
|
|
6757
|
-
const USE_NATIVE_SDK = process.env.USE_LEGACY_AI_SDK !== '1';
|
|
6758
4785
|
|
|
6759
4786
|
/**
|
|
6760
4787
|
* OpenCode SDK Integration
|
|
@@ -6769,7 +4796,7 @@ const USE_NATIVE_SDK = process.env.USE_LEGACY_AI_SDK !== '1';
|
|
|
6769
4796
|
* - Runner receives events and transforms them to SSE format for the frontend
|
|
6770
4797
|
*/
|
|
6771
4798
|
// Debug logging helper
|
|
6772
|
-
const debugLog$
|
|
4799
|
+
const debugLog$2 = (message) => {
|
|
6773
4800
|
if (process.env.SILENT_MODE !== '1' && process.env.DEBUG_BUILD === '1') {
|
|
6774
4801
|
console.error(message);
|
|
6775
4802
|
}
|
|
@@ -6934,12 +4961,12 @@ function parseSSEEvent(data) {
|
|
|
6934
4961
|
*/
|
|
6935
4962
|
function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
6936
4963
|
return async function* openCodeQuery(prompt, workingDirectory, systemPrompt, _agent, _codexThreadId, messageParts) {
|
|
6937
|
-
debugLog$
|
|
6938
|
-
debugLog$
|
|
6939
|
-
debugLog$
|
|
6940
|
-
debugLog$
|
|
4964
|
+
debugLog$2('[runner] [opencode-sdk] Starting OpenCode query');
|
|
4965
|
+
debugLog$2(`[runner] [opencode-sdk] Model: ${modelId}`);
|
|
4966
|
+
debugLog$2(`[runner] [opencode-sdk] Working dir: ${workingDirectory}`);
|
|
4967
|
+
debugLog$2(`[runner] [opencode-sdk] Prompt length: ${prompt.length}`);
|
|
6941
4968
|
// Ensure working directory exists
|
|
6942
|
-
if (!existsSync
|
|
4969
|
+
if (!existsSync(workingDirectory)) {
|
|
6943
4970
|
console.log(`[opencode-sdk] Creating working directory: ${workingDirectory}`);
|
|
6944
4971
|
mkdirSync(workingDirectory, { recursive: true });
|
|
6945
4972
|
}
|
|
@@ -6963,7 +4990,7 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
6963
4990
|
let sessionId = null;
|
|
6964
4991
|
// Start Sentry AI agent span for the entire OpenCode query
|
|
6965
4992
|
// This provides visibility into AI operations in Sentry's trace view
|
|
6966
|
-
const aiSpan = startInactiveSpan({
|
|
4993
|
+
const aiSpan = Sentry.startInactiveSpan({
|
|
6967
4994
|
name: 'opencode.query',
|
|
6968
4995
|
op: 'ai.pipeline',
|
|
6969
4996
|
attributes: {
|
|
@@ -6977,7 +5004,7 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
6977
5004
|
});
|
|
6978
5005
|
try {
|
|
6979
5006
|
// Step 1: Create a session
|
|
6980
|
-
debugLog$
|
|
5007
|
+
debugLog$2('[runner] [opencode-sdk] Creating session...');
|
|
6981
5008
|
const sessionResponse = await fetch(`${baseUrl}/session`, {
|
|
6982
5009
|
method: 'POST',
|
|
6983
5010
|
headers,
|
|
@@ -6991,13 +5018,13 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
6991
5018
|
}
|
|
6992
5019
|
const session = await sessionResponse.json();
|
|
6993
5020
|
sessionId = session.id;
|
|
6994
|
-
debugLog$
|
|
5021
|
+
debugLog$2(`[runner] [opencode-sdk] Session created: ${sessionId}`);
|
|
6995
5022
|
// Update span with session info
|
|
6996
5023
|
if (sessionId) {
|
|
6997
5024
|
aiSpan?.setAttribute('opencode.session_id', sessionId);
|
|
6998
5025
|
}
|
|
6999
5026
|
// Step 2: Subscribe to events
|
|
7000
|
-
debugLog$
|
|
5027
|
+
debugLog$2('[runner] [opencode-sdk] Subscribing to events...');
|
|
7001
5028
|
const eventResponse = await fetch(`${baseUrl}/event`, {
|
|
7002
5029
|
headers: {
|
|
7003
5030
|
...headers,
|
|
@@ -7018,7 +5045,7 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
7018
5045
|
image: part.image,
|
|
7019
5046
|
mimeType: part.mimeType || 'image/png',
|
|
7020
5047
|
});
|
|
7021
|
-
debugLog$
|
|
5048
|
+
debugLog$2('[runner] [opencode-sdk] Added image part');
|
|
7022
5049
|
}
|
|
7023
5050
|
}
|
|
7024
5051
|
}
|
|
@@ -7028,7 +5055,7 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
7028
5055
|
text: prompt,
|
|
7029
5056
|
});
|
|
7030
5057
|
// Step 4: Send the prompt (don't await - we'll stream the response)
|
|
7031
|
-
debugLog$
|
|
5058
|
+
debugLog$2('[runner] [opencode-sdk] Sending prompt...');
|
|
7032
5059
|
const promptPromise = fetch(`${baseUrl}/session/${sessionId}/message`, {
|
|
7033
5060
|
method: 'POST',
|
|
7034
5061
|
headers,
|
|
@@ -7052,7 +5079,7 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
7052
5079
|
while (!completed) {
|
|
7053
5080
|
const { done, value } = await reader.read();
|
|
7054
5081
|
if (done) {
|
|
7055
|
-
debugLog$
|
|
5082
|
+
debugLog$2('[runner] [opencode-sdk] Event stream ended');
|
|
7056
5083
|
break;
|
|
7057
5084
|
}
|
|
7058
5085
|
buffer += decoder.decode(value, { stream: true });
|
|
@@ -7120,15 +5147,15 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
7120
5147
|
subtype: 'success',
|
|
7121
5148
|
};
|
|
7122
5149
|
}
|
|
7123
|
-
debugLog$
|
|
5150
|
+
debugLog$2('[runner] [opencode-sdk] Query complete');
|
|
7124
5151
|
// Update span with final metrics
|
|
7125
5152
|
aiSpan?.setAttribute('opencode.tool_calls', toolCallCount);
|
|
7126
5153
|
aiSpan?.setAttribute('opencode.messages', messageCount);
|
|
7127
5154
|
aiSpan?.setStatus({ code: 1 }); // OK status
|
|
7128
5155
|
}
|
|
7129
5156
|
catch (error) {
|
|
7130
|
-
debugLog$
|
|
7131
|
-
captureException(error);
|
|
5157
|
+
debugLog$2(`[runner] [opencode-sdk] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
5158
|
+
Sentry.captureException(error);
|
|
7132
5159
|
// Mark span as errored
|
|
7133
5160
|
aiSpan?.setStatus({ code: 2, message: error instanceof Error ? error.message : String(error) });
|
|
7134
5161
|
// Yield error result
|
|
@@ -7155,141 +5182,6 @@ function createOpenCodeQuery(modelId = DEFAULT_OPENCODE_MODEL_ID) {
|
|
|
7155
5182
|
*/
|
|
7156
5183
|
const USE_OPENCODE_SDK = process.env.USE_OPENCODE_SDK === '1' && !!process.env.OPENCODE_URL;
|
|
7157
5184
|
|
|
7158
|
-
// src/lib/claude/tools.ts
|
|
7159
|
-
var genericInput = z.any();
|
|
7160
|
-
var genericObject = z.record(z.string(), z.any());
|
|
7161
|
-
var dynamicMcpTool = {
|
|
7162
|
-
description: "Generic MCP tool passthrough",
|
|
7163
|
-
type: "dynamic",
|
|
7164
|
-
inputSchema: genericObject
|
|
7165
|
-
};
|
|
7166
|
-
var fallbackTool = {
|
|
7167
|
-
description: "Fallback Claude CLI tool",
|
|
7168
|
-
inputSchema: genericInput
|
|
7169
|
-
};
|
|
7170
|
-
var builtinTools = {
|
|
7171
|
-
Agent: {
|
|
7172
|
-
description: "Launches a nested Claude agent",
|
|
7173
|
-
inputSchema: genericObject
|
|
7174
|
-
},
|
|
7175
|
-
Bash: {
|
|
7176
|
-
description: "Execute shell commands via Claude CLI",
|
|
7177
|
-
inputSchema: genericObject
|
|
7178
|
-
},
|
|
7179
|
-
BashOutput: {
|
|
7180
|
-
description: "Fetch output from a background bash process",
|
|
7181
|
-
inputSchema: genericObject
|
|
7182
|
-
},
|
|
7183
|
-
ExitPlanMode: {
|
|
7184
|
-
description: "Exit plan/approval mode with a summary",
|
|
7185
|
-
inputSchema: genericObject
|
|
7186
|
-
},
|
|
7187
|
-
Read: {
|
|
7188
|
-
description: "Read file contents",
|
|
7189
|
-
inputSchema: genericObject
|
|
7190
|
-
},
|
|
7191
|
-
Write: {
|
|
7192
|
-
description: "Write entire file contents",
|
|
7193
|
-
inputSchema: genericObject
|
|
7194
|
-
},
|
|
7195
|
-
Edit: {
|
|
7196
|
-
description: "Edit part of a file by replacing text",
|
|
7197
|
-
inputSchema: genericObject
|
|
7198
|
-
},
|
|
7199
|
-
Glob: {
|
|
7200
|
-
description: "Match files using glob patterns",
|
|
7201
|
-
inputSchema: genericObject
|
|
7202
|
-
},
|
|
7203
|
-
Grep: {
|
|
7204
|
-
description: "Search files for a regex pattern",
|
|
7205
|
-
inputSchema: genericObject
|
|
7206
|
-
},
|
|
7207
|
-
KillShell: {
|
|
7208
|
-
description: "Stop a running bash session",
|
|
7209
|
-
inputSchema: genericObject
|
|
7210
|
-
},
|
|
7211
|
-
ListMcpResources: {
|
|
7212
|
-
description: "Enumerate MCP resources",
|
|
7213
|
-
inputSchema: genericObject
|
|
7214
|
-
},
|
|
7215
|
-
Mcp: {
|
|
7216
|
-
description: "Invoke an MCP tool",
|
|
7217
|
-
inputSchema: genericObject
|
|
7218
|
-
},
|
|
7219
|
-
NotebookEdit: {
|
|
7220
|
-
description: "Edit Jupyter notebooks",
|
|
7221
|
-
inputSchema: genericObject
|
|
7222
|
-
},
|
|
7223
|
-
ReadMcpResource: {
|
|
7224
|
-
description: "Read a particular MCP resource",
|
|
7225
|
-
inputSchema: genericObject
|
|
7226
|
-
},
|
|
7227
|
-
TimeMachine: {
|
|
7228
|
-
description: "Rewind conversation with a course correction",
|
|
7229
|
-
inputSchema: genericObject
|
|
7230
|
-
},
|
|
7231
|
-
TodoWrite: {
|
|
7232
|
-
description: "Emit TODO status updates",
|
|
7233
|
-
inputSchema: genericObject
|
|
7234
|
-
},
|
|
7235
|
-
WebFetch: {
|
|
7236
|
-
description: "Fetch and summarize a URL",
|
|
7237
|
-
inputSchema: genericObject
|
|
7238
|
-
},
|
|
7239
|
-
WebSearch: {
|
|
7240
|
-
description: "Search the web with constraints",
|
|
7241
|
-
inputSchema: genericObject
|
|
7242
|
-
},
|
|
7243
|
-
MultipleChoiceQuestion: {
|
|
7244
|
-
description: "Ask the user a multiple choice question",
|
|
7245
|
-
inputSchema: genericObject
|
|
7246
|
-
},
|
|
7247
|
-
// Common aliases seen in telemetry/logs
|
|
7248
|
-
LS: {
|
|
7249
|
-
description: "List files in the working directory",
|
|
7250
|
-
inputSchema: genericObject
|
|
7251
|
-
},
|
|
7252
|
-
TodoRead: {
|
|
7253
|
-
description: "Read TODO state emitted from runner",
|
|
7254
|
-
inputSchema: genericObject
|
|
7255
|
-
}
|
|
7256
|
-
};
|
|
7257
|
-
var proxyHandler = {
|
|
7258
|
-
get(target, prop) {
|
|
7259
|
-
if (typeof prop !== "string") {
|
|
7260
|
-
return Reflect.get(target, prop);
|
|
7261
|
-
}
|
|
7262
|
-
if (Reflect.has(target, prop)) {
|
|
7263
|
-
return Reflect.get(target, prop);
|
|
7264
|
-
}
|
|
7265
|
-
if (prop.startsWith("mcp__")) {
|
|
7266
|
-
return dynamicMcpTool;
|
|
7267
|
-
}
|
|
7268
|
-
return fallbackTool;
|
|
7269
|
-
},
|
|
7270
|
-
has(target, prop) {
|
|
7271
|
-
if (typeof prop === "string") {
|
|
7272
|
-
if (Reflect.has(target, prop) || prop.startsWith("mcp__")) {
|
|
7273
|
-
return true;
|
|
7274
|
-
}
|
|
7275
|
-
}
|
|
7276
|
-
return Reflect.has(target, prop);
|
|
7277
|
-
},
|
|
7278
|
-
ownKeys(target) {
|
|
7279
|
-
return Reflect.ownKeys(target);
|
|
7280
|
-
},
|
|
7281
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
7282
|
-
if (Reflect.has(target, prop)) {
|
|
7283
|
-
return Object.getOwnPropertyDescriptor(target, prop);
|
|
7284
|
-
}
|
|
7285
|
-
return void 0;
|
|
7286
|
-
}
|
|
7287
|
-
};
|
|
7288
|
-
function createClaudeToolRegistry() {
|
|
7289
|
-
return new Proxy(builtinTools, proxyHandler);
|
|
7290
|
-
}
|
|
7291
|
-
var CLAUDE_CLI_TOOL_REGISTRY = createClaudeToolRegistry();
|
|
7292
|
-
|
|
7293
5185
|
// src/lib/logging/build-logger.ts
|
|
7294
5186
|
var BuildLogger = class {
|
|
7295
5187
|
buildId = null;
|
|
@@ -7344,7 +5236,7 @@ var BuildLogger = class {
|
|
|
7344
5236
|
if (typeof process !== "undefined" && process.env.NODE_ENV !== "test") {
|
|
7345
5237
|
try {
|
|
7346
5238
|
if (level === "warn" || level === "error") {
|
|
7347
|
-
addBreadcrumb({
|
|
5239
|
+
Sentry.addBreadcrumb({
|
|
7348
5240
|
category: `build-logger.${context}`,
|
|
7349
5241
|
message,
|
|
7350
5242
|
level: level === "error" ? "error" : "warning",
|
|
@@ -8830,9 +6722,9 @@ async function resolveAgentStrategy(agentId) {
|
|
|
8830
6722
|
}
|
|
8831
6723
|
|
|
8832
6724
|
// Debug logging helper - suppressed in TUI mode (SILENT_MODE=1)
|
|
8833
|
-
const debugLog$
|
|
6725
|
+
const debugLog$1 = (message) => {
|
|
8834
6726
|
if (process.env.SILENT_MODE !== '1' && process.env.DEBUG_BUILD === '1') {
|
|
8835
|
-
debugLog$
|
|
6727
|
+
debugLog$1();
|
|
8836
6728
|
}
|
|
8837
6729
|
};
|
|
8838
6730
|
/**
|
|
@@ -8843,7 +6735,7 @@ async function createBuildStream(options) {
|
|
|
8843
6735
|
// For Codex on NEW projects, use parent directory as CWD (Codex will create the project dir)
|
|
8844
6736
|
// For everything else, use the project directory
|
|
8845
6737
|
const strategy = await resolveAgentStrategy(agent);
|
|
8846
|
-
const projectName = options.projectName ||
|
|
6738
|
+
const projectName = options.projectName || path$1.basename(workingDirectory);
|
|
8847
6739
|
const strategyContext = {
|
|
8848
6740
|
projectId: options.projectId,
|
|
8849
6741
|
projectName,
|
|
@@ -8858,7 +6750,7 @@ async function createBuildStream(options) {
|
|
|
8858
6750
|
if (process.env.DEBUG_BUILD === '1')
|
|
8859
6751
|
console.log(`[engine] Strategy adjusted CWD to: ${actualWorkingDir}`);
|
|
8860
6752
|
}
|
|
8861
|
-
else if (!existsSync
|
|
6753
|
+
else if (!existsSync(workingDirectory)) {
|
|
8862
6754
|
mkdirSync(workingDirectory, { recursive: true });
|
|
8863
6755
|
}
|
|
8864
6756
|
if (!resolvedDir) {
|
|
@@ -8877,19 +6769,19 @@ async function createBuildStream(options) {
|
|
|
8877
6769
|
// Pass prompt, working directory, and system prompt to the query function
|
|
8878
6770
|
// The buildQuery wrapper will configure the SDK with all options
|
|
8879
6771
|
// Use actualWorkingDir so the query function gets the correct CWD
|
|
8880
|
-
debugLog$
|
|
6772
|
+
debugLog$1();
|
|
8881
6773
|
const generator = query(fullPrompt, actualWorkingDir, systemPrompt, agent, options.codexThreadId, messageParts);
|
|
8882
|
-
debugLog$
|
|
6774
|
+
debugLog$1();
|
|
8883
6775
|
// Create a ReadableStream from the AsyncGenerator
|
|
8884
6776
|
const stream = new ReadableStream({
|
|
8885
6777
|
async start(controller) {
|
|
8886
|
-
debugLog$
|
|
6778
|
+
debugLog$1();
|
|
8887
6779
|
let chunkCount = 0;
|
|
8888
6780
|
try {
|
|
8889
6781
|
for await (const chunk of generator) {
|
|
8890
6782
|
chunkCount++;
|
|
8891
6783
|
if (chunkCount % 5 === 0) {
|
|
8892
|
-
debugLog$
|
|
6784
|
+
debugLog$1(`[runner] [build-engine] Processed ${chunkCount} chunks from generator\n`);
|
|
8893
6785
|
}
|
|
8894
6786
|
// Convert chunk to appropriate format
|
|
8895
6787
|
if (typeof chunk === 'string') {
|
|
@@ -8902,11 +6794,11 @@ async function createBuildStream(options) {
|
|
|
8902
6794
|
controller.enqueue(new TextEncoder().encode(JSON.stringify(chunk)));
|
|
8903
6795
|
}
|
|
8904
6796
|
}
|
|
8905
|
-
debugLog$
|
|
6797
|
+
debugLog$1(`[runner] [build-engine] ✅ Generator exhausted after ${chunkCount} chunks, closing stream\n`);
|
|
8906
6798
|
controller.close();
|
|
8907
6799
|
}
|
|
8908
6800
|
catch (error) {
|
|
8909
|
-
debugLog$
|
|
6801
|
+
debugLog$1();
|
|
8910
6802
|
controller.error(error);
|
|
8911
6803
|
}
|
|
8912
6804
|
finally {
|
|
@@ -8915,7 +6807,7 @@ async function createBuildStream(options) {
|
|
|
8915
6807
|
}
|
|
8916
6808
|
},
|
|
8917
6809
|
});
|
|
8918
|
-
debugLog$
|
|
6810
|
+
debugLog$1();
|
|
8919
6811
|
return stream;
|
|
8920
6812
|
}
|
|
8921
6813
|
|
|
@@ -9134,7 +7026,7 @@ function classifyStartupError(error, processInfo) {
|
|
|
9134
7026
|
suggestion: 'Check file permissions and ownership'
|
|
9135
7027
|
};
|
|
9136
7028
|
}
|
|
9137
|
-
if (processInfo.cwd && !existsSync
|
|
7029
|
+
if (processInfo.cwd && !existsSync(processInfo.cwd)) {
|
|
9138
7030
|
return {
|
|
9139
7031
|
reason: FailureReason.DIRECTORY_MISSING,
|
|
9140
7032
|
message: `Working directory does not exist: ${processInfo.cwd}`,
|
|
@@ -9198,7 +7090,7 @@ function startDevServer(options) {
|
|
|
9198
7090
|
buildLogger$1.processManager.processStarting(projectId, command, cwd);
|
|
9199
7091
|
const emitter = new EventEmitter();
|
|
9200
7092
|
// Verify CWD exists
|
|
9201
|
-
if (!existsSync
|
|
7093
|
+
if (!existsSync(cwd)) {
|
|
9202
7094
|
const error = new Error(`Working directory does not exist: ${cwd}`);
|
|
9203
7095
|
buildLogger$1.processManager.error('Failed to spawn process', error, { projectId });
|
|
9204
7096
|
emitter.emit('error', error);
|
|
@@ -9435,7 +7327,7 @@ async function waitForPortRelease(port, maxWaitMs = 10000, pollIntervalMs = 500)
|
|
|
9435
7327
|
*/
|
|
9436
7328
|
function fixPackageJsonPort(cwd, targetPort) {
|
|
9437
7329
|
const packageJsonPath = join$1(cwd, 'package.json');
|
|
9438
|
-
if (!existsSync
|
|
7330
|
+
if (!existsSync(packageJsonPath)) {
|
|
9439
7331
|
buildLogger$1.log('debug', 'process-manager', 'No package.json found to fix', { cwd });
|
|
9440
7332
|
return false;
|
|
9441
7333
|
}
|
|
@@ -9562,19 +7454,19 @@ async function stopAllDevServers(tunnelManager) {
|
|
|
9562
7454
|
}
|
|
9563
7455
|
|
|
9564
7456
|
var processManager = /*#__PURE__*/Object.freeze({
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
7457
|
+
__proto__: null,
|
|
7458
|
+
get FailureReason () { return FailureReason; },
|
|
7459
|
+
get ProcessState () { return ProcessState; },
|
|
7460
|
+
checkPortInUse: checkPortInUse,
|
|
7461
|
+
findAvailablePort: findAvailablePort,
|
|
7462
|
+
getAllActiveProjectIds: getAllActiveProjectIds,
|
|
7463
|
+
runHealthCheck: runHealthCheck,
|
|
7464
|
+
setSilentMode: setSilentMode$1,
|
|
7465
|
+
startDevServer: startDevServer,
|
|
7466
|
+
startDevServerAsync: startDevServerAsync,
|
|
7467
|
+
stopAllDevServers: stopAllDevServers,
|
|
7468
|
+
stopDevServer: stopDevServer,
|
|
7469
|
+
waitForPortRelease: waitForPortRelease
|
|
9578
7470
|
});
|
|
9579
7471
|
|
|
9580
7472
|
/**
|
|
@@ -9584,7 +7476,7 @@ var processManager = /*#__PURE__*/Object.freeze({
|
|
|
9584
7476
|
function getWorkspaceRoot() {
|
|
9585
7477
|
// Check environment variable first
|
|
9586
7478
|
const envWorkspace = process.env.WORKSPACE_ROOT;
|
|
9587
|
-
if (envWorkspace && existsSync
|
|
7479
|
+
if (envWorkspace && existsSync(envWorkspace)) {
|
|
9588
7480
|
return resolve(envWorkspace);
|
|
9589
7481
|
}
|
|
9590
7482
|
// Default to a workspace directory in the user's home
|
|
@@ -9897,296 +7789,6 @@ function transformAgentMessageToSSE(agentMessage) {
|
|
|
9897
7789
|
return events;
|
|
9898
7790
|
}
|
|
9899
7791
|
|
|
9900
|
-
// Debug logging helper - suppressed in TUI mode (SILENT_MODE=1)
|
|
9901
|
-
const debugLog$1 = (message) => {
|
|
9902
|
-
if (process.env.SILENT_MODE !== '1' && process.env.DEBUG_BUILD === '1') {
|
|
9903
|
-
debugLog$1();
|
|
9904
|
-
}
|
|
9905
|
-
};
|
|
9906
|
-
/**
|
|
9907
|
-
* Truncate strings for logging to prevent excessive output
|
|
9908
|
-
*/
|
|
9909
|
-
function truncate$1(str, maxLength = 200) {
|
|
9910
|
-
if (str.length <= maxLength)
|
|
9911
|
-
return str;
|
|
9912
|
-
return str.substring(0, maxLength) + '...';
|
|
9913
|
-
}
|
|
9914
|
-
/**
|
|
9915
|
-
* Truncate JSON objects for logging
|
|
9916
|
-
*/
|
|
9917
|
-
function truncateJSON$1(obj, maxLength = 200) {
|
|
9918
|
-
const json = JSON.stringify(obj, null, 2);
|
|
9919
|
-
return truncate$1(json, maxLength);
|
|
9920
|
-
}
|
|
9921
|
-
/**
|
|
9922
|
-
* Transforms AI SDK stream events into messages compatible with our message transformer
|
|
9923
|
-
*/
|
|
9924
|
-
async function* transformAISDKStream(stream) {
|
|
9925
|
-
const DEBUG = process.env.DEBUG_BUILD === '1';
|
|
9926
|
-
let currentMessageId = `msg-${Date.now()}`; // Always have a default ID
|
|
9927
|
-
let currentTextContent = '';
|
|
9928
|
-
const toolInputBuffer = new Map();
|
|
9929
|
-
const toolResults = new Map();
|
|
9930
|
-
let eventCount = 0;
|
|
9931
|
-
let yieldCount = 0;
|
|
9932
|
-
// Track active tool spans for proper duration measurement
|
|
9933
|
-
const activeToolSpans = new Map();
|
|
9934
|
-
// ALWAYS log start - write to stderr to bypass TUI console interceptor
|
|
9935
|
-
debugLog$1();
|
|
9936
|
-
streamLog.info('━━━ STREAM TRANSFORMATION STARTED ━━━');
|
|
9937
|
-
for await (const part of stream) {
|
|
9938
|
-
eventCount++;
|
|
9939
|
-
// Log ALL events to file (first 50)
|
|
9940
|
-
if (eventCount <= 50) {
|
|
9941
|
-
streamLog.event(eventCount, part.type, part);
|
|
9942
|
-
}
|
|
9943
|
-
// DEBUG: Log first 20 events with full JSON to see what we're actually getting
|
|
9944
|
-
if (DEBUG && eventCount <= 20) {
|
|
9945
|
-
debugLog$1(`[runner] [ai-sdk-adapter] Event #${eventCount}: type="${part.type}"\n`);
|
|
9946
|
-
debugLog$1(`[runner] [ai-sdk-adapter] Full JSON: ${truncateJSON$1(part, 500)}\n`);
|
|
9947
|
-
}
|
|
9948
|
-
if (DEBUG)
|
|
9949
|
-
console.log('[ai-sdk-adapter] Event:', part.type, part);
|
|
9950
|
-
// DEBUG: Log every 10 events to show progress
|
|
9951
|
-
if (DEBUG && eventCount % 10 === 0) {
|
|
9952
|
-
debugLog$1();
|
|
9953
|
-
}
|
|
9954
|
-
switch (part.type) {
|
|
9955
|
-
case 'start':
|
|
9956
|
-
case 'start-step':
|
|
9957
|
-
// Update message ID if provided
|
|
9958
|
-
if (part.id) {
|
|
9959
|
-
currentMessageId = part.id;
|
|
9960
|
-
if (DEBUG)
|
|
9961
|
-
debugLog$1();
|
|
9962
|
-
}
|
|
9963
|
-
break;
|
|
9964
|
-
case 'text-start':
|
|
9965
|
-
// Capture message ID from text-start event
|
|
9966
|
-
if (part.id) {
|
|
9967
|
-
currentMessageId = part.id;
|
|
9968
|
-
if (DEBUG)
|
|
9969
|
-
debugLog$1();
|
|
9970
|
-
}
|
|
9971
|
-
break;
|
|
9972
|
-
case 'text-delta':
|
|
9973
|
-
// Just accumulate text content - DON'T yield for every token!
|
|
9974
|
-
// We'll only yield when there's a tool call or the message finishes
|
|
9975
|
-
const textChunk = part.delta ?? part.text ?? part.textDelta;
|
|
9976
|
-
if (typeof textChunk === 'string') {
|
|
9977
|
-
currentTextContent += textChunk;
|
|
9978
|
-
// Update message ID if provided in the event
|
|
9979
|
-
if (part.id) {
|
|
9980
|
-
currentMessageId = part.id;
|
|
9981
|
-
}
|
|
9982
|
-
}
|
|
9983
|
-
break;
|
|
9984
|
-
case 'tool-input-start':
|
|
9985
|
-
// Start buffering tool input
|
|
9986
|
-
toolInputBuffer.set(part.id, {
|
|
9987
|
-
name: part.toolName,
|
|
9988
|
-
input: '',
|
|
9989
|
-
});
|
|
9990
|
-
break;
|
|
9991
|
-
case 'tool-input-delta':
|
|
9992
|
-
// Accumulate tool input
|
|
9993
|
-
const buffer = toolInputBuffer.get(part.id);
|
|
9994
|
-
if (buffer) {
|
|
9995
|
-
buffer.input += part.delta || '';
|
|
9996
|
-
}
|
|
9997
|
-
break;
|
|
9998
|
-
case 'tool-input-end':
|
|
9999
|
-
case 'tool-call':
|
|
10000
|
-
// Tool call is complete - emit it
|
|
10001
|
-
const toolCallId = part.toolCallId || part.id;
|
|
10002
|
-
const toolName = part.toolName;
|
|
10003
|
-
let toolInput = part.args || part.input;
|
|
10004
|
-
// If we have buffered input, parse it
|
|
10005
|
-
if (!toolInput && toolInputBuffer.has(toolCallId) && typeof toolCallId === 'string') {
|
|
10006
|
-
const buffered = toolInputBuffer.get(toolCallId);
|
|
10007
|
-
if (buffered?.input) {
|
|
10008
|
-
if (typeof buffered.input === 'string') {
|
|
10009
|
-
try {
|
|
10010
|
-
toolInput = JSON.parse(buffered.input);
|
|
10011
|
-
}
|
|
10012
|
-
catch {
|
|
10013
|
-
toolInput = { raw: buffered.input };
|
|
10014
|
-
}
|
|
10015
|
-
}
|
|
10016
|
-
try {
|
|
10017
|
-
toolInput = JSON.parse(buffered.input);
|
|
10018
|
-
}
|
|
10019
|
-
catch {
|
|
10020
|
-
toolInput = { raw: buffered.input };
|
|
10021
|
-
}
|
|
10022
|
-
}
|
|
10023
|
-
toolInputBuffer.delete(toolCallId);
|
|
10024
|
-
}
|
|
10025
|
-
if (toolName) {
|
|
10026
|
-
// First, yield any accumulated text as a separate message
|
|
10027
|
-
if (currentTextContent.trim().length > 0) {
|
|
10028
|
-
const textMessage = {
|
|
10029
|
-
type: 'assistant',
|
|
10030
|
-
message: {
|
|
10031
|
-
id: currentMessageId,
|
|
10032
|
-
content: [{ type: 'text', text: currentTextContent }],
|
|
10033
|
-
},
|
|
10034
|
-
};
|
|
10035
|
-
yieldCount++;
|
|
10036
|
-
debugLog$1(`[runner] [ai-sdk-adapter] 💬 Yielding text before tool: ${currentTextContent.length} chars\n`);
|
|
10037
|
-
yield textMessage;
|
|
10038
|
-
// Reset so it's not included in the tool message
|
|
10039
|
-
currentTextContent = '';
|
|
10040
|
-
}
|
|
10041
|
-
// Now yield the tool call as a separate message
|
|
10042
|
-
const toolMessage = {
|
|
10043
|
-
type: 'assistant',
|
|
10044
|
-
message: {
|
|
10045
|
-
id: `${currentMessageId}-tool-${toolCallId}`,
|
|
10046
|
-
content: [
|
|
10047
|
-
{
|
|
10048
|
-
type: 'tool_use',
|
|
10049
|
-
id: toolCallId,
|
|
10050
|
-
name: toolName,
|
|
10051
|
-
input: toolInput || {},
|
|
10052
|
-
},
|
|
10053
|
-
],
|
|
10054
|
-
},
|
|
10055
|
-
};
|
|
10056
|
-
yieldCount++;
|
|
10057
|
-
debugLog$1();
|
|
10058
|
-
debugLog$1(`[runner] [ai-sdk-adapter] Tool input: ${truncateJSON$1(toolInput)}\n`);
|
|
10059
|
-
// Log to file
|
|
10060
|
-
streamLog.yield('tool-call', { toolName, toolCallId, toolInput, message: toolMessage });
|
|
10061
|
-
yield toolMessage;
|
|
10062
|
-
}
|
|
10063
|
-
break;
|
|
10064
|
-
case 'tool-result':
|
|
10065
|
-
// Store tool result
|
|
10066
|
-
const resultId = part.toolCallId;
|
|
10067
|
-
const toolResult = part.result ?? part.output;
|
|
10068
|
-
toolResults.set(resultId, toolResult);
|
|
10069
|
-
// Emit tool result as a user message
|
|
10070
|
-
const resultMessage = {
|
|
10071
|
-
type: 'user',
|
|
10072
|
-
message: {
|
|
10073
|
-
id: `result-${resultId}`,
|
|
10074
|
-
content: [
|
|
10075
|
-
{
|
|
10076
|
-
type: 'tool_result',
|
|
10077
|
-
tool_use_id: resultId,
|
|
10078
|
-
content: JSON.stringify(toolResult),
|
|
10079
|
-
},
|
|
10080
|
-
],
|
|
10081
|
-
},
|
|
10082
|
-
};
|
|
10083
|
-
yieldCount++;
|
|
10084
|
-
debugLog$1(`[runner] [ai-sdk-adapter] 📥 Tool result for: ${part.toolName || resultId}\n`);
|
|
10085
|
-
// Special handling for verbose tool results
|
|
10086
|
-
if (part.toolName === 'TodoWrite') {
|
|
10087
|
-
debugLog$1();
|
|
10088
|
-
}
|
|
10089
|
-
else {
|
|
10090
|
-
debugLog$1(`[runner] [ai-sdk-adapter] Result: ${truncateJSON$1(toolResult)}\n`);
|
|
10091
|
-
}
|
|
10092
|
-
yield resultMessage;
|
|
10093
|
-
break;
|
|
10094
|
-
case 'tool-error':
|
|
10095
|
-
// Emit tool error as a user message
|
|
10096
|
-
const errorId = part.toolCallId || part.id;
|
|
10097
|
-
const errorMessage = {
|
|
10098
|
-
type: 'user',
|
|
10099
|
-
message: {
|
|
10100
|
-
id: `error-${errorId}`,
|
|
10101
|
-
content: [
|
|
10102
|
-
{
|
|
10103
|
-
type: 'tool_result',
|
|
10104
|
-
tool_use_id: errorId,
|
|
10105
|
-
content: JSON.stringify({ error: part.error }),
|
|
10106
|
-
is_error: true,
|
|
10107
|
-
},
|
|
10108
|
-
],
|
|
10109
|
-
},
|
|
10110
|
-
};
|
|
10111
|
-
yieldCount++;
|
|
10112
|
-
debugLog$1(`[runner] [ai-sdk-adapter] ⚠️ Tool error: ${part.toolName || errorId}\n`);
|
|
10113
|
-
debugLog$1(`[runner] [ai-sdk-adapter] Error: ${truncate$1(JSON.stringify(part.error), 150)}\n`);
|
|
10114
|
-
yield errorMessage;
|
|
10115
|
-
break;
|
|
10116
|
-
case 'text-end':
|
|
10117
|
-
// Text block is complete - yield the accumulated text
|
|
10118
|
-
if (currentTextContent.trim().length > 0) {
|
|
10119
|
-
const message = {
|
|
10120
|
-
type: 'assistant',
|
|
10121
|
-
message: {
|
|
10122
|
-
id: currentMessageId,
|
|
10123
|
-
content: [{ type: 'text', text: currentTextContent }],
|
|
10124
|
-
},
|
|
10125
|
-
};
|
|
10126
|
-
yieldCount++;
|
|
10127
|
-
debugLog$1(`[runner] [ai-sdk-adapter] Yielding complete text block: ${currentTextContent.length} chars\n`);
|
|
10128
|
-
yield message;
|
|
10129
|
-
// Reset text content for next block
|
|
10130
|
-
currentTextContent = '';
|
|
10131
|
-
}
|
|
10132
|
-
break;
|
|
10133
|
-
case 'finish':
|
|
10134
|
-
case 'finish-step':
|
|
10135
|
-
// Final message - yield any remaining text
|
|
10136
|
-
if (currentTextContent.trim().length > 0) {
|
|
10137
|
-
yieldCount++;
|
|
10138
|
-
if (DEBUG)
|
|
10139
|
-
debugLog$1(`[runner] [ai-sdk-adapter] Yielding final text: ${currentTextContent.length} chars\n`);
|
|
10140
|
-
yield {
|
|
10141
|
-
type: 'assistant',
|
|
10142
|
-
message: {
|
|
10143
|
-
id: currentMessageId,
|
|
10144
|
-
content: [{ type: 'text', text: currentTextContent }],
|
|
10145
|
-
},
|
|
10146
|
-
};
|
|
10147
|
-
}
|
|
10148
|
-
break;
|
|
10149
|
-
case 'error':
|
|
10150
|
-
// Emit error
|
|
10151
|
-
const errorPayload = part.error ?? { message: 'Unknown Claude stream error' };
|
|
10152
|
-
const errorText = typeof errorPayload === 'object' && errorPayload !== null
|
|
10153
|
-
? JSON.stringify(errorPayload)
|
|
10154
|
-
: String(errorPayload);
|
|
10155
|
-
const wrappedError = new Error(`Claude stream error: ${errorText}`);
|
|
10156
|
-
Object.assign(wrappedError, { cause: errorPayload });
|
|
10157
|
-
debugLog$1();
|
|
10158
|
-
throw wrappedError;
|
|
10159
|
-
// Ignore other event types
|
|
10160
|
-
case 'stream-start':
|
|
10161
|
-
case 'response-metadata':
|
|
10162
|
-
break;
|
|
10163
|
-
default:
|
|
10164
|
-
// Log unknown event types for debugging
|
|
10165
|
-
if (process.env.DEBUG_BUILD === '1') {
|
|
10166
|
-
console.log('[ai-sdk-adapter] Unknown event type:', part.type);
|
|
10167
|
-
}
|
|
10168
|
-
break;
|
|
10169
|
-
}
|
|
10170
|
-
}
|
|
10171
|
-
// Clean up any orphaned tool spans (tool calls that never received results)
|
|
10172
|
-
if (activeToolSpans.size > 0) {
|
|
10173
|
-
debugLog$1(`[runner] [ai-sdk-adapter] ⚠️ Cleaning up ${activeToolSpans.size} orphaned tool spans\n`);
|
|
10174
|
-
for (const [toolId, span] of activeToolSpans) {
|
|
10175
|
-
span.setStatus({ code: 2, message: 'Tool execution incomplete - no result received' });
|
|
10176
|
-
span.end();
|
|
10177
|
-
debugLog$1();
|
|
10178
|
-
}
|
|
10179
|
-
activeToolSpans.clear();
|
|
10180
|
-
}
|
|
10181
|
-
// DEBUG: Log completion stats
|
|
10182
|
-
if (DEBUG) {
|
|
10183
|
-
debugLog$1();
|
|
10184
|
-
}
|
|
10185
|
-
streamLog.info('━━━ STREAM TRANSFORMATION COMPLETE ━━━');
|
|
10186
|
-
streamLog.info(`Total events: ${eventCount}`);
|
|
10187
|
-
streamLog.info(`Total yields: ${yieldCount}`);
|
|
10188
|
-
}
|
|
10189
|
-
|
|
10190
7792
|
/**
|
|
10191
7793
|
* Adapter to transform Codex SDK events into the unified format expected by message transformer
|
|
10192
7794
|
*
|
|
@@ -11076,10 +8678,10 @@ async function selectTemplateFromPrompt(userPrompt) {
|
|
|
11076
8678
|
}
|
|
11077
8679
|
|
|
11078
8680
|
var config = /*#__PURE__*/Object.freeze({
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
8681
|
+
__proto__: null,
|
|
8682
|
+
getAllTemplates: getAllTemplates,
|
|
8683
|
+
loadTemplateConfig: loadTemplateConfig,
|
|
8684
|
+
selectTemplateFromPrompt: selectTemplateFromPrompt
|
|
11083
8685
|
});
|
|
11084
8686
|
|
|
11085
8687
|
/**
|
|
@@ -11097,7 +8699,7 @@ async function downloadTemplate(template, targetPath) {
|
|
|
11097
8699
|
console.log(` Target: ${targetPath}`);
|
|
11098
8700
|
// Check if target already exists and add random suffix if needed
|
|
11099
8701
|
let finalTargetPath = targetPath;
|
|
11100
|
-
if (existsSync
|
|
8702
|
+
if (existsSync(targetPath)) {
|
|
11101
8703
|
// Generate a unique 4-character suffix
|
|
11102
8704
|
const randomSuffix = Math.random().toString(36).substring(2, 6);
|
|
11103
8705
|
finalTargetPath = `${targetPath}-${randomSuffix}`;
|
|
@@ -11169,12 +8771,12 @@ async function downloadTemplateWithGit(template, targetPath) {
|
|
|
11169
8771
|
// Handle multi-package projects with client/server subdirectories
|
|
11170
8772
|
const clientPkgPath = join$1(targetPath, 'client', 'package.json');
|
|
11171
8773
|
const serverPkgPath = join$1(targetPath, 'server', 'package.json');
|
|
11172
|
-
if (existsSync
|
|
8774
|
+
if (existsSync(clientPkgPath)) {
|
|
11173
8775
|
await updatePackageName(join$1(targetPath, 'client'), `${projectName}-client`);
|
|
11174
8776
|
await removeHardcodedPorts(join$1(targetPath, 'client'));
|
|
11175
8777
|
await ensureVitePortConfig(join$1(targetPath, 'client'));
|
|
11176
8778
|
}
|
|
11177
|
-
if (existsSync
|
|
8779
|
+
if (existsSync(serverPkgPath)) {
|
|
11178
8780
|
await updatePackageName(join$1(targetPath, 'server'), `${projectName}-server`);
|
|
11179
8781
|
await removeHardcodedPorts(join$1(targetPath, 'server'));
|
|
11180
8782
|
await ensureVitePortConfig(join$1(targetPath, 'server'));
|
|
@@ -11209,7 +8811,7 @@ shamefully-hoist=false
|
|
|
11209
8811
|
*/
|
|
11210
8812
|
async function updatePackageName(projectPath, newName) {
|
|
11211
8813
|
const pkgPath = join$1(projectPath, 'package.json');
|
|
11212
|
-
if (!existsSync
|
|
8814
|
+
if (!existsSync(pkgPath)) {
|
|
11213
8815
|
if (process.env.DEBUG_BUILD === '1')
|
|
11214
8816
|
console.log(` No package.json found in ${projectPath}, skipping name update`);
|
|
11215
8817
|
return;
|
|
@@ -11232,7 +8834,7 @@ async function updatePackageName(projectPath, newName) {
|
|
|
11232
8834
|
*/
|
|
11233
8835
|
async function removeHardcodedPorts(projectPath) {
|
|
11234
8836
|
const pkgPath = join$1(projectPath, 'package.json');
|
|
11235
|
-
if (!existsSync
|
|
8837
|
+
if (!existsSync(pkgPath)) {
|
|
11236
8838
|
return;
|
|
11237
8839
|
}
|
|
11238
8840
|
try {
|
|
@@ -11281,11 +8883,11 @@ async function ensureVitePortConfig(projectPath) {
|
|
|
11281
8883
|
const viteConfigJs = join$1(projectPath, 'vite.config.js');
|
|
11282
8884
|
const viteConfigMts = join$1(projectPath, 'vite.config.mts');
|
|
11283
8885
|
let configPath = null;
|
|
11284
|
-
if (existsSync
|
|
8886
|
+
if (existsSync(viteConfigTs))
|
|
11285
8887
|
configPath = viteConfigTs;
|
|
11286
|
-
else if (existsSync
|
|
8888
|
+
else if (existsSync(viteConfigMts))
|
|
11287
8889
|
configPath = viteConfigMts;
|
|
11288
|
-
else if (existsSync
|
|
8890
|
+
else if (existsSync(viteConfigJs))
|
|
11289
8891
|
configPath = viteConfigJs;
|
|
11290
8892
|
if (!configPath)
|
|
11291
8893
|
return;
|
|
@@ -11423,7 +9025,7 @@ async function orchestrateBuild(context) {
|
|
|
11423
9025
|
}
|
|
11424
9026
|
// Verify directory state for logging
|
|
11425
9027
|
try {
|
|
11426
|
-
if (existsSync
|
|
9028
|
+
if (existsSync(workingDirectory)) {
|
|
11427
9029
|
const files = await readdir(workingDirectory);
|
|
11428
9030
|
if (process.env.DEBUG_BUILD === '1')
|
|
11429
9031
|
console.log(`[orchestrator] Directory status: ${files.length} files found`);
|
|
@@ -11910,7 +9512,7 @@ function getCleanEnv() {
|
|
|
11910
9512
|
* Ensure a directory exists
|
|
11911
9513
|
*/
|
|
11912
9514
|
function ensureDir(dir) {
|
|
11913
|
-
if (!existsSync(dir)) {
|
|
9515
|
+
if (!existsSync$1(dir)) {
|
|
11914
9516
|
mkdirSync$1(dir, { recursive: true });
|
|
11915
9517
|
}
|
|
11916
9518
|
}
|
|
@@ -11954,7 +9556,7 @@ CRITICAL: Your response must START with { and END with }. Output only the JSON o
|
|
|
11954
9556
|
}
|
|
11955
9557
|
catch (error) {
|
|
11956
9558
|
console.error('[project-analyzer] SDK query failed:', error);
|
|
11957
|
-
captureException(error);
|
|
9559
|
+
Sentry.captureException(error);
|
|
11958
9560
|
throw error;
|
|
11959
9561
|
}
|
|
11960
9562
|
if (!responseText) {
|
|
@@ -12344,10 +9946,10 @@ async function waitForPort(port, maxRetries = 10, delayMs = 500) {
|
|
|
12344
9946
|
}
|
|
12345
9947
|
|
|
12346
9948
|
var portChecker = /*#__PURE__*/Object.freeze({
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
9949
|
+
__proto__: null,
|
|
9950
|
+
isPortReady: isPortReady,
|
|
9951
|
+
setSilentMode: setSilentMode,
|
|
9952
|
+
waitForPort: waitForPort
|
|
12351
9953
|
});
|
|
12352
9954
|
|
|
12353
9955
|
/**
|
|
@@ -12492,7 +10094,7 @@ const hmrProxyManager = new HmrProxyManager();
|
|
|
12492
10094
|
|
|
12493
10095
|
// Sentry is initialized via --import flag (see package.json scripts)
|
|
12494
10096
|
// This ensures instrumentation loads before any other modules
|
|
12495
|
-
|
|
10097
|
+
// AI SDK log warnings disabled (legacy - no longer using AI SDK)
|
|
12496
10098
|
/**
|
|
12497
10099
|
* Truncate strings for logging to prevent excessive output
|
|
12498
10100
|
*/
|
|
@@ -12524,7 +10126,7 @@ const DEBUG_BUILD = process.env.DEBUG_BUILD === "1" || false;
|
|
|
12524
10126
|
const log = (...args) => {
|
|
12525
10127
|
const message = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');
|
|
12526
10128
|
try {
|
|
12527
|
-
const logger = getLogger
|
|
10129
|
+
const logger = getLogger();
|
|
12528
10130
|
logger.info('system', message);
|
|
12529
10131
|
}
|
|
12530
10132
|
catch {
|
|
@@ -12538,7 +10140,7 @@ const log = (...args) => {
|
|
|
12538
10140
|
const buildLog = (...args) => {
|
|
12539
10141
|
const message = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');
|
|
12540
10142
|
try {
|
|
12541
|
-
const logger = getLogger
|
|
10143
|
+
const logger = getLogger();
|
|
12542
10144
|
logger.info('build', message);
|
|
12543
10145
|
}
|
|
12544
10146
|
catch {
|
|
@@ -12554,7 +10156,7 @@ const debugLog = (...args) => {
|
|
|
12554
10156
|
return;
|
|
12555
10157
|
const message = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');
|
|
12556
10158
|
try {
|
|
12557
|
-
const logger = getLogger
|
|
10159
|
+
const logger = getLogger();
|
|
12558
10160
|
logger.debug('system', message);
|
|
12559
10161
|
}
|
|
12560
10162
|
catch {
|
|
@@ -12563,146 +10165,6 @@ const debugLog = (...args) => {
|
|
|
12563
10165
|
};
|
|
12564
10166
|
const DEFAULT_AGENT = "claude-code";
|
|
12565
10167
|
const CODEX_MODEL = "gpt-5-codex";
|
|
12566
|
-
// Stderr debug logging - suppressed in TUI mode (SILENT_MODE=1)
|
|
12567
|
-
const stderrDebug = (message) => {
|
|
12568
|
-
if (process.env.SILENT_MODE !== '1' && DEBUG_BUILD) {
|
|
12569
|
-
process.stderr.write(message);
|
|
12570
|
-
}
|
|
12571
|
-
};
|
|
12572
|
-
/**
|
|
12573
|
-
* Create Claude query function using AI SDK
|
|
12574
|
-
*
|
|
12575
|
-
* NOTE: This function prepends CLAUDE_SYSTEM_PROMPT to the systemPrompt from orchestrator.
|
|
12576
|
-
* The orchestrator provides context-specific sections only (no base prompt).
|
|
12577
|
-
*/
|
|
12578
|
-
function createClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID) {
|
|
12579
|
-
return async function* (prompt, workingDirectory, systemPrompt, agent, codexThreadId, messageParts) {
|
|
12580
|
-
// Note: query is auto-instrumented by Sentry's claudeCodeAgentSdkIntegration via OpenTelemetry
|
|
12581
|
-
stderrDebug("[runner] [createClaudeQuery] 🎯 Query function called\n");
|
|
12582
|
-
stderrDebug(`[runner] [createClaudeQuery] Model: ${modelId}\n`);
|
|
12583
|
-
stderrDebug(`[runner] [createClaudeQuery] Working dir: ${workingDirectory}\n`);
|
|
12584
|
-
stderrDebug(`[runner] [createClaudeQuery] Prompt length: ${prompt.length}\n`);
|
|
12585
|
-
// Check if we have image parts
|
|
12586
|
-
const hasImages = messageParts?.some(p => p.type === 'image');
|
|
12587
|
-
if (hasImages) {
|
|
12588
|
-
const imageCount = messageParts?.filter(p => p.type === 'image').length || 0;
|
|
12589
|
-
stderrDebug(`[runner] [createClaudeQuery] 🖼️ Multi-modal message with ${imageCount} image(s)\n`);
|
|
12590
|
-
}
|
|
12591
|
-
// Build combined system prompt
|
|
12592
|
-
const systemPromptSegments = [CLAUDE_SYSTEM_PROMPT.trim()];
|
|
12593
|
-
if (systemPrompt && systemPrompt.trim().length > 0) {
|
|
12594
|
-
systemPromptSegments.push(systemPrompt.trim());
|
|
12595
|
-
}
|
|
12596
|
-
const appendedSystemPrompt = systemPromptSegments.join("\n\n");
|
|
12597
|
-
// Use the full model ID as-is (claude-haiku-4-5, claude-sonnet-4-5, claude-opus-4-5)
|
|
12598
|
-
const aiSdkModelId = modelId || "claude-haiku-4-5";
|
|
12599
|
-
// Ensure working directory exists before passing to Claude Code
|
|
12600
|
-
if (!existsSync$1(workingDirectory)) {
|
|
12601
|
-
console.log(`[createClaudeQuery] Creating working directory: ${workingDirectory}`);
|
|
12602
|
-
mkdirSync(workingDirectory, { recursive: true });
|
|
12603
|
-
}
|
|
12604
|
-
// NOTE: The community provider (ai-sdk-provider-claude-code) bundles an older
|
|
12605
|
-
// version of the SDK types. We cast to `any` to avoid type conflicts.
|
|
12606
|
-
// This legacy path is deprecated in favor of the native SDK implementation.
|
|
12607
|
-
const model = claudeCode(aiSdkModelId, {
|
|
12608
|
-
queryFunction: query, // Cast to avoid SDK version type conflict
|
|
12609
|
-
systemPrompt: {
|
|
12610
|
-
type: "preset",
|
|
12611
|
-
preset: "claude_code",
|
|
12612
|
-
append: appendedSystemPrompt,
|
|
12613
|
-
},
|
|
12614
|
-
cwd: workingDirectory,
|
|
12615
|
-
permissionMode: "bypassPermissions",
|
|
12616
|
-
maxTurns: 100,
|
|
12617
|
-
additionalDirectories: [workingDirectory],
|
|
12618
|
-
env: {
|
|
12619
|
-
// Claude Code CLI default output cap is 32k; bumping to 64k per
|
|
12620
|
-
// https://docs.claude.com/en/docs/claude-code/settings#environment-variables
|
|
12621
|
-
CLAUDE_CODE_MAX_OUTPUT_TOKENS: process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS ?? "64000",
|
|
12622
|
-
},
|
|
12623
|
-
// Explicitly allow all tools to prevent "No tools are available" errors
|
|
12624
|
-
canUseTool: createProjectScopedPermissionHandler(workingDirectory), // Cast for type compat
|
|
12625
|
-
streamingInput: "always", // REQUIRED when using canUseTool - enables tool callbacks
|
|
12626
|
-
includePartialMessages: true,
|
|
12627
|
-
settingSources: ["user", "project"], // Load skills from user and project directories
|
|
12628
|
-
});
|
|
12629
|
-
// Build user message - either simple text or multi-part with images
|
|
12630
|
-
let userMessage;
|
|
12631
|
-
if (messageParts && messageParts.length > 0) {
|
|
12632
|
-
// Multi-part message with images
|
|
12633
|
-
const contentParts = [];
|
|
12634
|
-
// Add image parts first (Claude best practice)
|
|
12635
|
-
for (const part of messageParts) {
|
|
12636
|
-
if (part.type === 'image' && part.image) {
|
|
12637
|
-
// AI SDK expects images as data URLs, which we already have
|
|
12638
|
-
contentParts.push({
|
|
12639
|
-
type: 'image',
|
|
12640
|
-
image: part.image, // Already a base64 data URL
|
|
12641
|
-
});
|
|
12642
|
-
// Extract media type for logging
|
|
12643
|
-
const match = part.image.match(/^data:(.+);base64,/);
|
|
12644
|
-
if (match) {
|
|
12645
|
-
stderrDebug(`[runner] [createClaudeQuery] ✅ Added image part (${match[1]})\n`);
|
|
12646
|
-
}
|
|
12647
|
-
}
|
|
12648
|
-
else if (part.type === 'text' && part.text) {
|
|
12649
|
-
contentParts.push({
|
|
12650
|
-
type: 'text',
|
|
12651
|
-
text: part.text
|
|
12652
|
-
});
|
|
12653
|
-
}
|
|
12654
|
-
}
|
|
12655
|
-
// Add prompt text if not already in parts
|
|
12656
|
-
if (!contentParts.some(p => p.type === 'text')) {
|
|
12657
|
-
contentParts.push({
|
|
12658
|
-
type: 'text',
|
|
12659
|
-
text: prompt
|
|
12660
|
-
});
|
|
12661
|
-
}
|
|
12662
|
-
userMessage = contentParts;
|
|
12663
|
-
stderrDebug(`[runner] [createClaudeQuery] 📦 Built multi-part message with ${contentParts.length} part(s)\n`);
|
|
12664
|
-
}
|
|
12665
|
-
else {
|
|
12666
|
-
// Simple text message (existing behavior)
|
|
12667
|
-
userMessage = prompt;
|
|
12668
|
-
}
|
|
12669
|
-
// Stream with telemetry enabled for Sentry
|
|
12670
|
-
// Use 'messages' param for multi-part content, 'prompt' for simple text
|
|
12671
|
-
// IMPORTANT: experimental_telemetry is REQUIRED for the Vercel AI SDK integration to work
|
|
12672
|
-
const streamOptions = {
|
|
12673
|
-
model,
|
|
12674
|
-
tools: CLAUDE_CLI_TOOL_REGISTRY,
|
|
12675
|
-
experimental_telemetry: {
|
|
12676
|
-
isEnabled: true,
|
|
12677
|
-
functionId: 'createClaudeQuery',
|
|
12678
|
-
recordInputs: true,
|
|
12679
|
-
recordOutputs: true,
|
|
12680
|
-
},
|
|
12681
|
-
};
|
|
12682
|
-
const result = Array.isArray(userMessage)
|
|
12683
|
-
? streamText({
|
|
12684
|
-
...streamOptions,
|
|
12685
|
-
messages: [{ role: 'user', content: userMessage }],
|
|
12686
|
-
})
|
|
12687
|
-
: streamText({
|
|
12688
|
-
...streamOptions,
|
|
12689
|
-
prompt: userMessage,
|
|
12690
|
-
});
|
|
12691
|
-
// Transform AI SDK stream format to our message format
|
|
12692
|
-
if (process.env.DEBUG_BUILD === "1") {
|
|
12693
|
-
console.log("[createClaudeQuery] Starting stream consumption...");
|
|
12694
|
-
}
|
|
12695
|
-
for await (const message of transformAISDKStream(result.fullStream)) {
|
|
12696
|
-
if (process.env.DEBUG_BUILD === "1") {
|
|
12697
|
-
console.log("[createClaudeQuery] Yielding message:", message.type);
|
|
12698
|
-
}
|
|
12699
|
-
yield message;
|
|
12700
|
-
}
|
|
12701
|
-
if (process.env.DEBUG_BUILD === "1") {
|
|
12702
|
-
console.log("[createClaudeQuery] Stream consumption complete");
|
|
12703
|
-
}
|
|
12704
|
-
};
|
|
12705
|
-
}
|
|
12706
10168
|
/**
|
|
12707
10169
|
* Create Codex query function using ORIGINAL Codex SDK (NOT AI SDK)
|
|
12708
10170
|
*
|
|
@@ -12797,7 +10259,7 @@ function createCodexQuery() {
|
|
|
12797
10259
|
log(`🚀 [codex-query] Turn ${turnCount}: ${turnCount === 1 ? 'Initial request' : 'Continuing work'}...`);
|
|
12798
10260
|
// Log full prompt being sent to Codex
|
|
12799
10261
|
if (turnCount === 1) {
|
|
12800
|
-
info(fmt `Full Codex prompt (Turn 1) ${{
|
|
10262
|
+
Sentry.logger.info(Sentry.logger.fmt `Full Codex prompt (Turn 1) ${{
|
|
12801
10263
|
prompt: turnPrompt,
|
|
12802
10264
|
promptLength: turnPrompt.length,
|
|
12803
10265
|
promptPreview: turnPrompt.substring(0, 200),
|
|
@@ -12865,13 +10327,8 @@ function createBuildQuery(agent, modelId, abortController) {
|
|
|
12865
10327
|
console.log('[runner] 🔄 Using direct Codex SDK (fallback mode)');
|
|
12866
10328
|
return createCodexQuery();
|
|
12867
10329
|
}
|
|
12868
|
-
// Use legacy AI SDK path when explicitly requested
|
|
12869
|
-
if (!USE_NATIVE_SDK) {
|
|
12870
|
-
console.log('[runner] 🔄 Using AI SDK with claude-code provider (legacy mode)');
|
|
12871
|
-
return createClaudeQuery(modelId ?? DEFAULT_CLAUDE_MODEL_ID);
|
|
12872
|
-
}
|
|
12873
10330
|
// Default: Use native Claude Agent SDK (direct integration)
|
|
12874
|
-
console.log('[runner] 🔄 Using NATIVE Claude Agent SDK
|
|
10331
|
+
console.log('[runner] 🔄 Using NATIVE Claude Agent SDK');
|
|
12875
10332
|
return createNativeClaudeQuery(modelId ?? DEFAULT_CLAUDE_MODEL_ID, abortController);
|
|
12876
10333
|
}
|
|
12877
10334
|
/**
|
|
@@ -13047,7 +10504,7 @@ async function startRunner(options = {}) {
|
|
|
13047
10504
|
const RUNNER_ID = options.runnerId ||
|
|
13048
10505
|
process.env.RUNNER_ID ||
|
|
13049
10506
|
process.env.RAILWAY_REPLICA_ID ||
|
|
13050
|
-
`runner-${
|
|
10507
|
+
`runner-${os$1.hostname()}`;
|
|
13051
10508
|
// WebSocket URL for direct connection to Next.js server
|
|
13052
10509
|
// Supports both new RUNNER_WS_URL and legacy RUNNER_BROKER_URL for backward compatibility
|
|
13053
10510
|
const WS_URL = options.wsUrl ||
|
|
@@ -13078,7 +10535,7 @@ async function startRunner(options = {}) {
|
|
|
13078
10535
|
}
|
|
13079
10536
|
const runnerSharedSecret = SHARED_SECRET; // Guaranteed to be string after validation check
|
|
13080
10537
|
// Ensure workspace directory exists
|
|
13081
|
-
if (!existsSync
|
|
10538
|
+
if (!existsSync(WORKSPACE_ROOT)) {
|
|
13082
10539
|
console.log(`📁 Creating workspace directory: ${WORKSPACE_ROOT}`);
|
|
13083
10540
|
mkdirSync(WORKSPACE_ROOT, { recursive: true });
|
|
13084
10541
|
}
|
|
@@ -13109,14 +10566,14 @@ async function startRunner(options = {}) {
|
|
|
13109
10566
|
id: RUNNER_ID,
|
|
13110
10567
|
connected: socket?.readyState === WebSocket$1.OPEN,
|
|
13111
10568
|
workspace: WORKSPACE_ROOT,
|
|
13112
|
-
workspaceExists: existsSync
|
|
10569
|
+
workspaceExists: existsSync(WORKSPACE_ROOT),
|
|
13113
10570
|
},
|
|
13114
10571
|
uptime: process.uptime(),
|
|
13115
10572
|
timestamp: new Date().toISOString(),
|
|
13116
10573
|
});
|
|
13117
10574
|
});
|
|
13118
10575
|
healthApp.get('/ready', (req, res) => {
|
|
13119
|
-
const isReady = socket?.readyState === WebSocket$1.OPEN && existsSync
|
|
10576
|
+
const isReady = socket?.readyState === WebSocket$1.OPEN && existsSync(WORKSPACE_ROOT);
|
|
13120
10577
|
res.status(isReady ? 200 : 503).json({
|
|
13121
10578
|
ready: isReady,
|
|
13122
10579
|
runnerId: RUNNER_ID,
|
|
@@ -13182,7 +10639,7 @@ async function startRunner(options = {}) {
|
|
|
13182
10639
|
throw lastError || new Error('Persistence failed after retries');
|
|
13183
10640
|
}
|
|
13184
10641
|
async function persistBuildEventDirect(context, event) {
|
|
13185
|
-
return startSpan({
|
|
10642
|
+
return Sentry.startSpan({
|
|
13186
10643
|
name: `persist.build-event.${event.type}`,
|
|
13187
10644
|
op: 'http.client',
|
|
13188
10645
|
attributes: {
|
|
@@ -13193,7 +10650,7 @@ async function startRunner(options = {}) {
|
|
|
13193
10650
|
},
|
|
13194
10651
|
}, async () => {
|
|
13195
10652
|
// Get trace context for propagation
|
|
13196
|
-
const traceData = getTraceData();
|
|
10653
|
+
const traceData = Sentry.getTraceData();
|
|
13197
10654
|
const headers = {
|
|
13198
10655
|
'Authorization': `Bearer ${runnerSharedSecret}`,
|
|
13199
10656
|
'Content-Type': 'application/json',
|
|
@@ -13301,7 +10758,7 @@ async function startRunner(options = {}) {
|
|
|
13301
10758
|
*/
|
|
13302
10759
|
async function persistRunnerEvent(event) {
|
|
13303
10760
|
// Wrap in Sentry span to create proper trace hierarchy
|
|
13304
|
-
return startSpan({
|
|
10761
|
+
return Sentry.startSpan({
|
|
13305
10762
|
name: `persist.runner-event.${event.type}`,
|
|
13306
10763
|
op: 'http.client',
|
|
13307
10764
|
attributes: {
|
|
@@ -13355,9 +10812,9 @@ async function startRunner(options = {}) {
|
|
|
13355
10812
|
try {
|
|
13356
10813
|
// Attach trace context to ALL events for distributed tracing
|
|
13357
10814
|
// This allows events to be linked back to the originating frontend request
|
|
13358
|
-
const span = getActiveSpan();
|
|
10815
|
+
const span = Sentry.getActiveSpan();
|
|
13359
10816
|
if (span) {
|
|
13360
|
-
const traceData = getTraceData();
|
|
10817
|
+
const traceData = Sentry.getTraceData();
|
|
13361
10818
|
event._sentry = {
|
|
13362
10819
|
trace: traceData['sentry-trace'],
|
|
13363
10820
|
baggage: traceData.baggage,
|
|
@@ -13862,7 +11319,7 @@ async function startRunner(options = {}) {
|
|
|
13862
11319
|
logger.tunnel({ port, url: tunnelUrl, status: 'created' });
|
|
13863
11320
|
// Instrument tunnel startup timing
|
|
13864
11321
|
const tunnelDuration = Date.now() - tunnelStartTime;
|
|
13865
|
-
distribution('tunnel_startup_duration', tunnelDuration, {
|
|
11322
|
+
Sentry.metrics.distribution('tunnel_startup_duration', tunnelDuration, {
|
|
13866
11323
|
unit: 'millisecond',
|
|
13867
11324
|
attributes: {
|
|
13868
11325
|
port: port.toString(),
|
|
@@ -13880,7 +11337,7 @@ async function startRunner(options = {}) {
|
|
|
13880
11337
|
console.error("Failed to create tunnel:", error);
|
|
13881
11338
|
// Instrument failed tunnel startup timing
|
|
13882
11339
|
const tunnelDuration = Date.now() - tunnelStartTime;
|
|
13883
|
-
distribution('tunnel_startup_duration', tunnelDuration, {
|
|
11340
|
+
Sentry.metrics.distribution('tunnel_startup_duration', tunnelDuration, {
|
|
13884
11341
|
unit: 'millisecond',
|
|
13885
11342
|
attributes: {
|
|
13886
11343
|
port: command.payload.port.toString(),
|
|
@@ -14553,7 +12010,7 @@ async function startRunner(options = {}) {
|
|
|
14553
12010
|
// Detect framework from generated files
|
|
14554
12011
|
let detectedFramework = null;
|
|
14555
12012
|
try {
|
|
14556
|
-
const { detectFrameworkFromFilesystem } = await import('./chunks/port-allocator-
|
|
12013
|
+
const { detectFrameworkFromFilesystem } = await import('./chunks/port-allocator-DuAZe2_S.js');
|
|
14557
12014
|
const framework = await detectFrameworkFromFilesystem(projectDirectory);
|
|
14558
12015
|
detectedFramework = framework;
|
|
14559
12016
|
if (framework) {
|
|
@@ -14592,11 +12049,25 @@ ${filesArray.length > 0 ? `Files modified:\n${filesArray.map(f => `- ${f}`).join
|
|
|
14592
12049
|
${completedTodoSnapshot.length > 0 ? `Tasks completed:\n${completedTodoSnapshot.map(t => `- ${t}`).join('\n')}` : ''}
|
|
14593
12050
|
|
|
14594
12051
|
Write a brief, professional summary (1-3 sentences) describing what was accomplished. Focus on the outcome, not the process. Do not use phrases like "I did" or "The assistant". Just describe what was built or changed.`;
|
|
14595
|
-
|
|
14596
|
-
|
|
12052
|
+
// Use native Claude Agent SDK for summary generation
|
|
12053
|
+
let summaryText = '';
|
|
12054
|
+
for await (const msg of query({
|
|
14597
12055
|
prompt: summaryPrompt,
|
|
14598
|
-
|
|
14599
|
-
|
|
12056
|
+
options: {
|
|
12057
|
+
model: 'claude-haiku-4-5',
|
|
12058
|
+
maxTurns: 1,
|
|
12059
|
+
permissionMode: 'default',
|
|
12060
|
+
},
|
|
12061
|
+
})) {
|
|
12062
|
+
if (msg.type === 'assistant' && msg.message?.content) {
|
|
12063
|
+
for (const block of msg.message.content) {
|
|
12064
|
+
if (block.type === 'text') {
|
|
12065
|
+
summaryText += block.text;
|
|
12066
|
+
}
|
|
12067
|
+
}
|
|
12068
|
+
}
|
|
12069
|
+
}
|
|
12070
|
+
summaryText = summaryText.trim();
|
|
14600
12071
|
if (summaryText) {
|
|
14601
12072
|
buildLog(` ✅ AI summary generated (${summaryText.length} chars)`);
|
|
14602
12073
|
sendEvent({
|
|
@@ -14641,7 +12112,7 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
14641
12112
|
catch (error) {
|
|
14642
12113
|
const errorMessage = error instanceof Error ? error.message : "Failed to run build";
|
|
14643
12114
|
logger.buildFailed(errorMessage);
|
|
14644
|
-
getActiveSpan()?.setStatus({
|
|
12115
|
+
Sentry.getActiveSpan()?.setStatus({
|
|
14645
12116
|
code: 2, // SPAN_STATUS_ERROR
|
|
14646
12117
|
message: "Build failed",
|
|
14647
12118
|
});
|
|
@@ -14858,15 +12329,15 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
14858
12329
|
}
|
|
14859
12330
|
function publishStatus(projectId, commandId) {
|
|
14860
12331
|
const uptimeSeconds = Math.round(process.uptime());
|
|
14861
|
-
const load =
|
|
12332
|
+
const load = os$1.loadavg?.()[0];
|
|
14862
12333
|
sendEvent({
|
|
14863
12334
|
type: "runner-status",
|
|
14864
12335
|
...buildEventBase(projectId, commandId),
|
|
14865
12336
|
payload: {
|
|
14866
12337
|
status: "online",
|
|
14867
12338
|
version: "prototype",
|
|
14868
|
-
hostname:
|
|
14869
|
-
platform:
|
|
12339
|
+
hostname: os$1.hostname(),
|
|
12340
|
+
platform: os$1.platform(),
|
|
14870
12341
|
uptimeSeconds,
|
|
14871
12342
|
load,
|
|
14872
12343
|
},
|
|
@@ -14994,12 +12465,12 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
14994
12465
|
// This creates a span within the continued trace for the runner's work
|
|
14995
12466
|
if (command._sentry?.trace) {
|
|
14996
12467
|
console.log("[runner] Continuing trace from frontend:", command._sentry.trace.substring(0, 50));
|
|
14997
|
-
await continueTrace({
|
|
12468
|
+
await Sentry.continueTrace({
|
|
14998
12469
|
sentryTrace: command._sentry.trace,
|
|
14999
12470
|
baggage: command._sentry.baggage,
|
|
15000
12471
|
}, async () => {
|
|
15001
12472
|
// Create a span for this command execution within the continued trace
|
|
15002
|
-
await startSpan({
|
|
12473
|
+
await Sentry.startSpan({
|
|
15003
12474
|
name: `runner.${command.type}`,
|
|
15004
12475
|
op: command.type === 'start-build' ? 'build.runner' : `runner.${command.type}`,
|
|
15005
12476
|
attributes: {
|
|
@@ -15010,9 +12481,9 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
15010
12481
|
},
|
|
15011
12482
|
}, async (span) => {
|
|
15012
12483
|
try {
|
|
15013
|
-
setTag("command_type", command.type);
|
|
15014
|
-
setTag("project_id", projectIdForTelemetry);
|
|
15015
|
-
setTag("command_id", command.id);
|
|
12484
|
+
Sentry.setTag("command_type", command.type);
|
|
12485
|
+
Sentry.setTag("project_id", projectIdForTelemetry);
|
|
12486
|
+
Sentry.setTag("command_id", command.id);
|
|
15016
12487
|
// Capture build metrics for start-build commands
|
|
15017
12488
|
if (command.type === 'start-build' && command.payload) {
|
|
15018
12489
|
const agent = command.payload.agent ?? 'claude-code';
|
|
@@ -15022,7 +12493,7 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
15022
12493
|
command.payload.claudeModel === 'claude-opus-4-5')
|
|
15023
12494
|
? command.payload.claudeModel
|
|
15024
12495
|
: 'claude-sonnet-4-5';
|
|
15025
|
-
count('runner.build.started', 1, {
|
|
12496
|
+
Sentry.metrics.count('runner.build.started', 1, {
|
|
15026
12497
|
attributes: {
|
|
15027
12498
|
project_id: command.projectId,
|
|
15028
12499
|
model: agent === 'claude-code' ? claudeModel : agent,
|
|
@@ -15043,7 +12514,7 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
15043
12514
|
else {
|
|
15044
12515
|
console.log("[runner] No trace context - starting isolated span");
|
|
15045
12516
|
// Create an isolated span when no trace context is provided
|
|
15046
|
-
await startSpan({
|
|
12517
|
+
await Sentry.startSpan({
|
|
15047
12518
|
name: `runner.${command.type}`,
|
|
15048
12519
|
op: command.type === 'start-build' ? 'build.runner' : `runner.${command.type}`,
|
|
15049
12520
|
attributes: {
|
|
@@ -15054,9 +12525,9 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
15054
12525
|
},
|
|
15055
12526
|
}, async (span) => {
|
|
15056
12527
|
try {
|
|
15057
|
-
setTag("command_type", command.type);
|
|
15058
|
-
setTag("project_id", projectIdForTelemetry);
|
|
15059
|
-
setTag("command_id", command.id);
|
|
12528
|
+
Sentry.setTag("command_type", command.type);
|
|
12529
|
+
Sentry.setTag("project_id", projectIdForTelemetry);
|
|
12530
|
+
Sentry.setTag("command_id", command.id);
|
|
15060
12531
|
// Capture build metrics for start-build commands
|
|
15061
12532
|
if (command.type === 'start-build' && command.payload) {
|
|
15062
12533
|
const agent = command.payload.agent ?? 'claude-code';
|
|
@@ -15066,7 +12537,7 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
15066
12537
|
command.payload.claudeModel === 'claude-opus-4-5')
|
|
15067
12538
|
? command.payload.claudeModel
|
|
15068
12539
|
: 'claude-sonnet-4-5';
|
|
15069
|
-
count('runner.build.started', 1, {
|
|
12540
|
+
Sentry.metrics.count('runner.build.started', 1, {
|
|
15070
12541
|
attributes: {
|
|
15071
12542
|
project_id: command.projectId,
|
|
15072
12543
|
model: agent === 'claude-code' ? claudeModel : agent,
|
|
@@ -15086,7 +12557,7 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
15086
12557
|
}
|
|
15087
12558
|
catch (error) {
|
|
15088
12559
|
console.error("Failed to parse command", error);
|
|
15089
|
-
captureException(error);
|
|
12560
|
+
Sentry.captureException(error);
|
|
15090
12561
|
sendEvent({
|
|
15091
12562
|
type: "error",
|
|
15092
12563
|
...buildEventBase(undefined, randomUUID$1()),
|
|
@@ -15178,7 +12649,7 @@ Write a brief, professional summary (1-3 sentences) describing what was accompli
|
|
|
15178
12649
|
// Final cleanup of any remaining tunnels
|
|
15179
12650
|
await tunnelManager.closeAll();
|
|
15180
12651
|
// Flush Sentry events before exiting
|
|
15181
|
-
await flush(2000);
|
|
12652
|
+
await Sentry.flush(2000);
|
|
15182
12653
|
log("shutdown complete");
|
|
15183
12654
|
};
|
|
15184
12655
|
process.on("SIGINT", async () => {
|