ak-claude 0.0.1

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/index.cjs ADDED
@@ -0,0 +1,2348 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // index.js
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
+ AgentQuery: () => agent_query_default,
33
+ BaseClaude: () => base_default,
34
+ Chat: () => chat_default,
35
+ CodeAgent: () => code_agent_default,
36
+ Message: () => message_default,
37
+ RagAgent: () => rag_agent_default,
38
+ ToolAgent: () => tool_agent_default,
39
+ Transformer: () => transformer_default,
40
+ attemptJSONRecovery: () => attemptJSONRecovery,
41
+ default: () => index_default,
42
+ extractJSON: () => extractJSON,
43
+ log: () => logger_default
44
+ });
45
+ module.exports = __toCommonJS(index_exports);
46
+
47
+ // base.js
48
+ var import_dotenv = __toESM(require("dotenv"), 1);
49
+ var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
50
+
51
+ // logger.js
52
+ var import_pino = __toESM(require("pino"), 1);
53
+ var isDev = process.env.NODE_ENV !== "production";
54
+ var logger = (0, import_pino.default)({
55
+ level: process.env.LOG_LEVEL || "info",
56
+ // Supports 'fatal', 'error', 'warn', 'info', 'debug', 'trace'
57
+ messageKey: "message",
58
+ // GCP expects 'message' instead of Pino's default 'msg'
59
+ transport: isDev ? {
60
+ target: "pino-pretty",
61
+ // Prettified output for local dev
62
+ options: { colorize: true, translateTime: true }
63
+ } : void 0
64
+ // In prod/cloud, keep as JSON for cloud logging
65
+ });
66
+ var logger_default = logger;
67
+
68
+ // json-helpers.js
69
+ function isJSON(data) {
70
+ try {
71
+ const attempt = JSON.stringify(data);
72
+ if (attempt?.startsWith("{") || attempt?.startsWith("[")) {
73
+ if (attempt?.endsWith("}") || attempt?.endsWith("]")) {
74
+ return true;
75
+ }
76
+ }
77
+ return false;
78
+ } catch (e) {
79
+ return false;
80
+ }
81
+ }
82
+ function isJSONStr(string) {
83
+ if (typeof string !== "string") return false;
84
+ try {
85
+ const result = JSON.parse(string);
86
+ const type = Object.prototype.toString.call(result);
87
+ return type === "[object Object]" || type === "[object Array]";
88
+ } catch (err) {
89
+ return false;
90
+ }
91
+ }
92
+ function attemptJSONRecovery(text, maxAttempts = 100) {
93
+ if (!text || typeof text !== "string") return null;
94
+ try {
95
+ return JSON.parse(text);
96
+ } catch (e) {
97
+ }
98
+ let workingText = text.trim();
99
+ let braces = 0;
100
+ let brackets = 0;
101
+ let inString = false;
102
+ let escapeNext = false;
103
+ for (let j = 0; j < workingText.length; j++) {
104
+ const char = workingText[j];
105
+ if (escapeNext) {
106
+ escapeNext = false;
107
+ continue;
108
+ }
109
+ if (char === "\\") {
110
+ escapeNext = true;
111
+ continue;
112
+ }
113
+ if (char === '"') {
114
+ inString = !inString;
115
+ continue;
116
+ }
117
+ if (!inString) {
118
+ if (char === "{") braces++;
119
+ else if (char === "}") braces--;
120
+ else if (char === "[") brackets++;
121
+ else if (char === "]") brackets--;
122
+ }
123
+ }
124
+ if ((braces > 0 || brackets > 0 || inString) && workingText.length > 2) {
125
+ let fixedText = workingText;
126
+ if (inString) {
127
+ fixedText += '"';
128
+ }
129
+ while (braces > 0) {
130
+ fixedText += "}";
131
+ braces--;
132
+ }
133
+ while (brackets > 0) {
134
+ fixedText += "]";
135
+ brackets--;
136
+ }
137
+ try {
138
+ const result = JSON.parse(fixedText);
139
+ if (logger_default.level !== "silent") {
140
+ logger_default.warn(`JSON response appears truncated (possibly hit maxTokens limit). Recovered by adding closing characters.`);
141
+ }
142
+ return result;
143
+ } catch (e) {
144
+ }
145
+ }
146
+ for (let i = 0; i < maxAttempts && workingText.length > 2; i++) {
147
+ workingText = workingText.slice(0, -1);
148
+ let braces2 = 0;
149
+ let brackets2 = 0;
150
+ let inString2 = false;
151
+ let escapeNext2 = false;
152
+ for (let j = 0; j < workingText.length; j++) {
153
+ const char = workingText[j];
154
+ if (escapeNext2) {
155
+ escapeNext2 = false;
156
+ continue;
157
+ }
158
+ if (char === "\\") {
159
+ escapeNext2 = true;
160
+ continue;
161
+ }
162
+ if (char === '"') {
163
+ inString2 = !inString2;
164
+ continue;
165
+ }
166
+ if (!inString2) {
167
+ if (char === "{") braces2++;
168
+ else if (char === "}") braces2--;
169
+ else if (char === "[") brackets2++;
170
+ else if (char === "]") brackets2--;
171
+ }
172
+ }
173
+ if (braces2 === 0 && brackets2 === 0 && !inString2) {
174
+ try {
175
+ const result = JSON.parse(workingText);
176
+ if (logger_default.level !== "silent") {
177
+ logger_default.warn(`JSON response appears truncated (possibly hit maxTokens limit). Recovered by removing ${i + 1} characters from the end.`);
178
+ }
179
+ return result;
180
+ } catch (e) {
181
+ }
182
+ }
183
+ if (i > 5) {
184
+ let fixedText = workingText;
185
+ if (inString2) {
186
+ fixedText += '"';
187
+ }
188
+ while (braces2 > 0) {
189
+ fixedText += "}";
190
+ braces2--;
191
+ }
192
+ while (brackets2 > 0) {
193
+ fixedText += "]";
194
+ brackets2--;
195
+ }
196
+ try {
197
+ const result = JSON.parse(fixedText);
198
+ if (logger_default.level !== "silent") {
199
+ logger_default.warn(`JSON response appears truncated (possibly hit maxTokens limit). Recovered by adding closing characters.`);
200
+ }
201
+ return result;
202
+ } catch (e) {
203
+ }
204
+ }
205
+ }
206
+ return null;
207
+ }
208
+ function extractCompleteStructure(text, startPos) {
209
+ const startChar = text[startPos];
210
+ const endChar = startChar === "{" ? "}" : "]";
211
+ let depth = 0;
212
+ let inString = false;
213
+ let escaped = false;
214
+ for (let i = startPos; i < text.length; i++) {
215
+ const char = text[i];
216
+ if (escaped) {
217
+ escaped = false;
218
+ continue;
219
+ }
220
+ if (char === "\\" && inString) {
221
+ escaped = true;
222
+ continue;
223
+ }
224
+ if (char === '"' && !escaped) {
225
+ inString = !inString;
226
+ continue;
227
+ }
228
+ if (!inString) {
229
+ if (char === startChar) {
230
+ depth++;
231
+ } else if (char === endChar) {
232
+ depth--;
233
+ if (depth === 0) {
234
+ return text.substring(startPos, i + 1);
235
+ }
236
+ }
237
+ }
238
+ }
239
+ return null;
240
+ }
241
+ function findCompleteJSONStructures(text) {
242
+ const results = [];
243
+ const startChars = ["{", "["];
244
+ for (let i = 0; i < text.length; i++) {
245
+ if (startChars.includes(text[i])) {
246
+ const extracted = extractCompleteStructure(text, i);
247
+ if (extracted) {
248
+ results.push(extracted);
249
+ }
250
+ }
251
+ }
252
+ return results;
253
+ }
254
+ function extractJSON(text) {
255
+ if (!text || typeof text !== "string") {
256
+ throw new Error("No text provided for JSON extraction");
257
+ }
258
+ if (isJSONStr(text.trim())) {
259
+ return JSON.parse(text.trim());
260
+ }
261
+ const codeBlockPatterns = [
262
+ /```json\s*\n?([\s\S]*?)\n?\s*```/gi,
263
+ /```\s*\n?([\s\S]*?)\n?\s*```/gi
264
+ ];
265
+ for (const pattern of codeBlockPatterns) {
266
+ const matches = text.match(pattern);
267
+ if (matches) {
268
+ for (const match of matches) {
269
+ const jsonContent = match.replace(/```json\s*\n?/gi, "").replace(/```\s*\n?/gi, "").trim();
270
+ if (isJSONStr(jsonContent)) {
271
+ return JSON.parse(jsonContent);
272
+ }
273
+ }
274
+ }
275
+ }
276
+ const jsonPatterns = [
277
+ /\{[\s\S]*\}/g,
278
+ /\[[\s\S]*\]/g
279
+ ];
280
+ for (const pattern of jsonPatterns) {
281
+ const matches = text.match(pattern);
282
+ if (matches) {
283
+ for (const match of matches) {
284
+ const candidate = match.trim();
285
+ if (isJSONStr(candidate)) {
286
+ return JSON.parse(candidate);
287
+ }
288
+ }
289
+ }
290
+ }
291
+ const advancedExtract = findCompleteJSONStructures(text);
292
+ if (advancedExtract.length > 0) {
293
+ for (const candidate of advancedExtract) {
294
+ if (isJSONStr(candidate)) {
295
+ return JSON.parse(candidate);
296
+ }
297
+ }
298
+ }
299
+ const cleanedText = text.replace(/^\s*Sure,?\s*here\s+is\s+your?\s+.*?[:\n]/gi, "").replace(/^\s*Here\s+is\s+the\s+.*?[:\n]/gi, "").replace(/^\s*The\s+.*?is\s*[:\n]/gi, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").trim();
300
+ if (isJSONStr(cleanedText)) {
301
+ return JSON.parse(cleanedText);
302
+ }
303
+ const recoveredJSON = attemptJSONRecovery(text);
304
+ if (recoveredJSON !== null) {
305
+ return recoveredJSON;
306
+ }
307
+ throw new Error(`Could not extract valid JSON from model response. Response preview: ${text.substring(0, 200)}...`);
308
+ }
309
+
310
+ // base.js
311
+ import_dotenv.default.config({ quiet: true });
312
+ var { NODE_ENV = "unknown", LOG_LEVEL = "" } = process.env;
313
+ var DEFAULT_MAX_TOKENS = 8192;
314
+ var MODEL_PRICING = {
315
+ "claude-sonnet-4-6": { input: 3, output: 15 },
316
+ "claude-sonnet-4-5-20250514": { input: 3, output: 15 },
317
+ "claude-haiku-4-5-20251001": { input: 0.8, output: 4 },
318
+ "claude-opus-4-6": { input: 15, output: 75 },
319
+ "claude-opus-4-5-20250514": { input: 15, output: 75 }
320
+ };
321
+ var BaseClaude = class {
322
+ /**
323
+ * @param {BaseClaudeOptions} [options={}]
324
+ */
325
+ constructor(options = {}) {
326
+ this.modelName = options.modelName || "claude-sonnet-4-6";
327
+ if (options.systemPrompt !== void 0) {
328
+ this.systemPrompt = options.systemPrompt;
329
+ } else {
330
+ this.systemPrompt = null;
331
+ }
332
+ this.vertexai = options.vertexai ?? false;
333
+ this.vertexProjectId = options.vertexProjectId ?? process.env.GOOGLE_CLOUD_PROJECT ?? void 0;
334
+ this.vertexRegion = options.vertexRegion ?? process.env.GOOGLE_CLOUD_LOCATION ?? "us-east5";
335
+ if (!this.vertexai) {
336
+ this.apiKey = options.apiKey !== void 0 && options.apiKey !== null ? options.apiKey : process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_API_KEY;
337
+ if (!this.apiKey) {
338
+ throw new Error("Missing Anthropic API key. Provide via options.apiKey, ANTHROPIC_API_KEY, or CLAUDE_API_KEY env var.");
339
+ }
340
+ } else {
341
+ this.apiKey = null;
342
+ }
343
+ this.maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
344
+ this.temperature = options.temperature ?? 0.7;
345
+ this.topP = options.topP ?? 0.95;
346
+ this.topK = options.topK ?? void 0;
347
+ this.thinking = options.thinking ?? null;
348
+ this.cacheSystemPrompt = options.cacheSystemPrompt ?? false;
349
+ this.enableWebSearch = options.enableWebSearch ?? false;
350
+ this.webSearchConfig = options.webSearchConfig ?? {};
351
+ this.healthCheck = options.healthCheck ?? false;
352
+ this.maxRetries = options.maxRetries ?? 5;
353
+ this._configureLogLevel(options.logLevel);
354
+ this.client = null;
355
+ this._clientReady = false;
356
+ if (!this.vertexai) {
357
+ this.client = new import_sdk.default({
358
+ apiKey: this.apiKey,
359
+ maxRetries: this.maxRetries
360
+ });
361
+ this._clientReady = true;
362
+ }
363
+ this.history = [];
364
+ this.lastResponseMetadata = null;
365
+ this.exampleCount = 0;
366
+ this._initialized = false;
367
+ this._cumulativeUsage = {
368
+ promptTokens: 0,
369
+ responseTokens: 0,
370
+ totalTokens: 0,
371
+ attempts: 0
372
+ };
373
+ logger_default.debug(`${this.constructor.name} created with model: ${this.modelName}`);
374
+ }
375
+ // ── Client Bootstrap ─────────────────────────────────────────────────────
376
+ /**
377
+ * Ensures the Anthropic client is ready. For direct API usage this is
378
+ * synchronous (client created in constructor). For Vertex AI this lazily
379
+ * imports @anthropic-ai/vertex-sdk and creates the AnthropicVertex client.
380
+ */
381
+ async _ensureClient() {
382
+ if (this._clientReady) return;
383
+ if (this.vertexai) {
384
+ const { AnthropicVertex } = await import("@anthropic-ai/vertex-sdk");
385
+ this.client = new AnthropicVertex({
386
+ projectId: this.vertexProjectId,
387
+ region: this.vertexRegion
388
+ });
389
+ const MODEL_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/messages", "/v1/messages?beta=true"]);
390
+ const vertexClient = this.client;
391
+ const superBuildRequest = Object.getPrototypeOf(Object.getPrototypeOf(vertexClient)).buildRequest;
392
+ Object.getPrototypeOf(vertexClient).buildRequest = function(options, extra) {
393
+ if (typeof options.body === "object" && options.body !== null) {
394
+ options.body = { ...options.body };
395
+ if (!options.body["anthropic_version"]) {
396
+ options.body["anthropic_version"] = "vertex-2023-10-16";
397
+ }
398
+ }
399
+ if (MODEL_ENDPOINTS.has(options.path) && options.method === "post" && typeof options.body === "object") {
400
+ const model = options.body["model"];
401
+ options.body["model"] = void 0;
402
+ const stream = options.body["stream"] ?? false;
403
+ const specifier = stream ? "streamRawPredict" : "rawPredict";
404
+ options.path = `/projects/${this.projectId}/locations/${this.region}/publishers/anthropic/models/${model}:${specifier}`;
405
+ }
406
+ if (options.path === "/v1/messages/count_tokens" || options.path === "/v1/messages/count_tokens?beta=true" && options.method === "post") {
407
+ options.path = `/projects/${this.projectId}/locations/${this.region}/publishers/anthropic/models/count-tokens:rawPredict`;
408
+ }
409
+ return superBuildRequest.call(this, options, extra);
410
+ };
411
+ this._clientReady = true;
412
+ logger_default.debug(`${this.constructor.name}: Vertex AI client created (project=${this.vertexProjectId}, region=${this.vertexRegion})`);
413
+ }
414
+ }
415
+ // ── Initialization ───────────────────────────────────────────────────────
416
+ /**
417
+ * Initializes the instance. Idempotent unless force=true.
418
+ * Claude has no chat sessions to create — this just validates connectivity.
419
+ * @param {boolean} [force=false]
420
+ * @returns {Promise<void>}
421
+ */
422
+ async init(force = false) {
423
+ if (this._initialized && !force) return;
424
+ await this._ensureClient();
425
+ logger_default.debug(`Initializing ${this.constructor.name} with model: ${this.modelName}...`);
426
+ if (this.healthCheck) {
427
+ try {
428
+ await this.client.messages.create({
429
+ model: this.modelName,
430
+ max_tokens: 1,
431
+ messages: [{ role: "user", content: "hi" }]
432
+ });
433
+ logger_default.debug(`${this.constructor.name}: API connection successful.`);
434
+ } catch (e) {
435
+ throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
436
+ }
437
+ }
438
+ this._initialized = true;
439
+ logger_default.debug(`${this.constructor.name}: Initialized.`);
440
+ }
441
+ // ── Core Message Sending ─────────────────────────────────────────────────
442
+ /**
443
+ * Builds the system parameter for messages.create().
444
+ * Supports string or array with cache_control.
445
+ * @returns {string|Array|undefined}
446
+ * @protected
447
+ */
448
+ _buildSystemParam() {
449
+ if (!this.systemPrompt) return void 0;
450
+ if (this.cacheSystemPrompt) {
451
+ return [{ type: "text", text: this.systemPrompt, cache_control: { type: "ephemeral" } }];
452
+ }
453
+ return this.systemPrompt;
454
+ }
455
+ /**
456
+ * Builds the tools array, prepending the web search server tool if enabled.
457
+ * @param {Array} [tools] - User-provided tools array
458
+ * @returns {Array|undefined} The final tools array, or undefined if empty
459
+ * @protected
460
+ */
461
+ _buildTools(tools) {
462
+ if (!this.enableWebSearch && !tools) return void 0;
463
+ if (!this.enableWebSearch) return tools;
464
+ const webSearchTool = {
465
+ type: "web_search_20250305",
466
+ name: "web_search",
467
+ ...this.webSearchConfig
468
+ };
469
+ if (!tools || tools.length === 0) return [webSearchTool];
470
+ return [webSearchTool, ...tools];
471
+ }
472
+ /**
473
+ * Core method: sends a message via messages.create(), manages history.
474
+ * Handles both string content and content block arrays (for tool_result).
475
+ *
476
+ * @param {string|Array} userContent - String message or array of content blocks
477
+ * @param {Object} [opts={}] - Additional params (tools, tool_choice, maxTokens, etc.)
478
+ * @returns {Promise<Object>} The API response
479
+ * @protected
480
+ */
481
+ async _sendMessage(userContent, opts = {}) {
482
+ if (!this._initialized) await this.init();
483
+ const userMsg = { role: "user", content: userContent };
484
+ this.history.push(userMsg);
485
+ const tools = this._buildTools(opts.tools);
486
+ const params = {
487
+ model: opts.model || this.modelName,
488
+ max_tokens: opts.maxTokens || this.maxTokens,
489
+ messages: [...this.history],
490
+ ...this._buildSystemParam() && { system: this._buildSystemParam() },
491
+ ...this.topK !== void 0 && { top_k: this.topK },
492
+ ...tools && { tools },
493
+ ...opts.tool_choice && { tool_choice: opts.tool_choice }
494
+ };
495
+ if (this.thinking) {
496
+ params.thinking = this.thinking;
497
+ } else {
498
+ if (this.temperature !== void 0) params.temperature = this.temperature;
499
+ if (this.topP !== void 0) params.top_p = this.topP;
500
+ }
501
+ const response = await this.client.messages.create(params);
502
+ this.history.push({ role: "assistant", content: response.content });
503
+ this._captureMetadata(response);
504
+ return response;
505
+ }
506
+ /**
507
+ * Streaming variant of _sendMessage. Returns a stream object.
508
+ *
509
+ * @param {string|Array} userContent - String message or array of content blocks
510
+ * @param {Object} [opts={}] - Additional params
511
+ * @returns {Promise<Object>} The stream object with .on() and .finalMessage()
512
+ * @protected
513
+ */
514
+ async _streamMessage(userContent, opts = {}) {
515
+ if (!this._initialized) await this.init();
516
+ const userMsg = { role: "user", content: userContent };
517
+ this.history.push(userMsg);
518
+ const tools = this._buildTools(opts.tools);
519
+ const params = {
520
+ model: opts.model || this.modelName,
521
+ max_tokens: opts.maxTokens || this.maxTokens,
522
+ messages: [...this.history],
523
+ ...this._buildSystemParam() && { system: this._buildSystemParam() },
524
+ ...this.topK !== void 0 && { top_k: this.topK },
525
+ ...tools && { tools },
526
+ ...opts.tool_choice && { tool_choice: opts.tool_choice }
527
+ };
528
+ if (this.thinking) {
529
+ params.thinking = this.thinking;
530
+ } else {
531
+ if (this.temperature !== void 0) params.temperature = this.temperature;
532
+ if (this.topP !== void 0) params.top_p = this.topP;
533
+ }
534
+ const stream = this.client.messages.stream(params);
535
+ return stream;
536
+ }
537
+ // ── Text Extraction ──────────────────────────────────────────────────────
538
+ /**
539
+ * Extracts text from a Claude response's content blocks.
540
+ * Filters for type: 'text' and joins.
541
+ * @param {Object} response - The API response
542
+ * @returns {string}
543
+ * @protected
544
+ */
545
+ _extractText(response) {
546
+ if (!response?.content) return "";
547
+ return response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
548
+ }
549
+ // ── History Management ───────────────────────────────────────────────────
550
+ /**
551
+ * Retrieves the current conversation history.
552
+ * @param {boolean} [curated=false] - If true, returns text-only simplified history
553
+ * @returns {Array<Object>}
554
+ */
555
+ getHistory(curated = false) {
556
+ if (curated) {
557
+ return this.history.map((m) => ({
558
+ role: m.role,
559
+ content: typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.filter((b) => b.type === "text").map((b) => b.text).join("") : String(m.content)
560
+ }));
561
+ }
562
+ return [...this.history];
563
+ }
564
+ /**
565
+ * Clears conversation history.
566
+ * Subclasses may override to preserve seeded examples.
567
+ * @returns {Promise<void>}
568
+ */
569
+ async clearHistory() {
570
+ this.history = [];
571
+ this.lastResponseMetadata = null;
572
+ this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 0 };
573
+ logger_default.debug(`${this.constructor.name}: Conversation history cleared.`);
574
+ }
575
+ // ── Few-Shot Seeding ─────────────────────────────────────────────────────
576
+ /**
577
+ * Seeds the conversation with example input/output pairs for few-shot learning.
578
+ * Injects user/assistant message pairs into history.
579
+ *
580
+ * @param {TransformationExample[]} examples - Array of example objects
581
+ * @param {Object} [opts={}] - Key configuration
582
+ * @param {string} [opts.promptKey='PROMPT'] - Key for input data
583
+ * @param {string} [opts.answerKey='ANSWER'] - Key for output data
584
+ * @param {string} [opts.contextKey='CONTEXT'] - Key for optional context
585
+ * @param {string} [opts.explanationKey='EXPLANATION'] - Key for optional explanations
586
+ * @param {string} [opts.systemPromptKey='SYSTEM'] - Key for system prompt overrides
587
+ * @returns {Promise<Array>} The updated history
588
+ */
589
+ async seed(examples, opts = {}) {
590
+ await this.init();
591
+ if (!examples || !Array.isArray(examples) || examples.length === 0) {
592
+ logger_default.debug("No examples provided. Skipping seeding.");
593
+ return this.getHistory();
594
+ }
595
+ const promptKey = opts.promptKey || "PROMPT";
596
+ const answerKey = opts.answerKey || "ANSWER";
597
+ const contextKey = opts.contextKey || "CONTEXT";
598
+ const explanationKey = opts.explanationKey || "EXPLANATION";
599
+ const systemPromptKey = opts.systemPromptKey || "SYSTEM";
600
+ const instructionExample = examples.find((ex) => ex[systemPromptKey]);
601
+ if (instructionExample) {
602
+ logger_default.debug(`Found system prompt in examples; updating.`);
603
+ this.systemPrompt = instructionExample[systemPromptKey];
604
+ }
605
+ logger_default.debug(`Seeding conversation with ${examples.length} examples...`);
606
+ const historyToAdd = [];
607
+ for (const example of examples) {
608
+ const contextValue = example[contextKey] || "";
609
+ const promptValue = example[promptKey] || "";
610
+ const answerValue = example[answerKey] || "";
611
+ const explanationValue = example[explanationKey] || "";
612
+ let userText = "";
613
+ let modelResponse = {};
614
+ if (contextValue) {
615
+ let contextText = isJSON(contextValue) ? JSON.stringify(contextValue, null, 2) : contextValue;
616
+ userText += `CONTEXT:
617
+ ${contextText}
618
+
619
+ `;
620
+ }
621
+ if (promptValue) {
622
+ let promptText = isJSON(promptValue) ? JSON.stringify(promptValue, null, 2) : promptValue;
623
+ userText += promptText;
624
+ }
625
+ if (answerValue) modelResponse.data = answerValue;
626
+ if (explanationValue) modelResponse.explanation = explanationValue;
627
+ const modelText = JSON.stringify(modelResponse, null, 2);
628
+ if (userText.trim().length && modelText.trim().length > 0) {
629
+ historyToAdd.push({ role: "user", content: userText.trim() });
630
+ historyToAdd.push({ role: "assistant", content: modelText.trim() });
631
+ }
632
+ }
633
+ logger_default.debug(`Adding ${historyToAdd.length} items to history (${this.history.length} existing)...`);
634
+ this.history = [...this.history, ...historyToAdd];
635
+ this.exampleCount = this.history.length;
636
+ logger_default.debug(`History now has ${this.history.length} items.`);
637
+ return this.getHistory();
638
+ }
639
+ // ── Response Metadata ────────────────────────────────────────────────────
640
+ /**
641
+ * Captures response metadata from an API response.
642
+ * @param {Object} response - The API response object
643
+ * @protected
644
+ */
645
+ _captureMetadata(response) {
646
+ this.lastResponseMetadata = {
647
+ modelVersion: response.model || null,
648
+ requestedModel: this.modelName,
649
+ promptTokens: response.usage?.input_tokens || 0,
650
+ responseTokens: response.usage?.output_tokens || 0,
651
+ totalTokens: (response.usage?.input_tokens || 0) + (response.usage?.output_tokens || 0),
652
+ cacheCreationTokens: response.usage?.cache_creation_input_tokens || 0,
653
+ cacheReadTokens: response.usage?.cache_read_input_tokens || 0,
654
+ stopReason: response.stop_reason || null,
655
+ timestamp: Date.now()
656
+ };
657
+ }
658
+ /**
659
+ * Returns structured usage data from the last API call.
660
+ * Includes CUMULATIVE token counts across all retry attempts.
661
+ * @returns {UsageData|null}
662
+ */
663
+ getLastUsage() {
664
+ if (!this.lastResponseMetadata) return null;
665
+ const meta = this.lastResponseMetadata;
666
+ const cumulative = this._cumulativeUsage || { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 1 };
667
+ const useCumulative = cumulative.attempts > 0;
668
+ return {
669
+ promptTokens: useCumulative ? cumulative.promptTokens : meta.promptTokens,
670
+ responseTokens: useCumulative ? cumulative.responseTokens : meta.responseTokens,
671
+ totalTokens: useCumulative ? cumulative.totalTokens : meta.totalTokens,
672
+ cacheCreationTokens: meta.cacheCreationTokens,
673
+ cacheReadTokens: meta.cacheReadTokens,
674
+ attempts: useCumulative ? cumulative.attempts : 1,
675
+ modelVersion: meta.modelVersion,
676
+ requestedModel: meta.requestedModel,
677
+ stopReason: meta.stopReason,
678
+ timestamp: meta.timestamp
679
+ };
680
+ }
681
+ // ── Token Estimation ────────────────────────────────────────────────────
682
+ /**
683
+ * Estimates INPUT token count for a payload before sending.
684
+ * Includes system prompt + chat history + your new message.
685
+ * Uses Claude's token counting API.
686
+ * @param {Object|string} nextPayload - The next message to estimate
687
+ * @returns {Promise<{ inputTokens: number }>}
688
+ */
689
+ async estimate(nextPayload) {
690
+ if (!this._initialized) await this.init();
691
+ const nextMessage = typeof nextPayload === "string" ? nextPayload : JSON.stringify(nextPayload, null, 2);
692
+ const messages = [
693
+ ...this.history,
694
+ { role: "user", content: nextMessage }
695
+ ];
696
+ const params = {
697
+ model: this.modelName,
698
+ messages,
699
+ ...this._buildSystemParam() && { system: this._buildSystemParam() }
700
+ };
701
+ if (
702
+ /** @type {any} */
703
+ this.tools?.length > 0
704
+ ) {
705
+ params.tools = /** @type {any} */
706
+ this.tools;
707
+ }
708
+ const resp = await this.client.messages.countTokens(params);
709
+ return { inputTokens: resp.input_tokens };
710
+ }
711
+ /**
712
+ * Estimates the INPUT cost of sending a payload based on model pricing.
713
+ * @param {Object|string} nextPayload - The next message to estimate
714
+ * @returns {Promise<{ inputTokens: number, model: string, pricing: { input: number, output: number }, estimatedInputCost: number, note: string }>}
715
+ */
716
+ async estimateCost(nextPayload) {
717
+ const tokenInfo = await this.estimate(nextPayload);
718
+ const pricing = MODEL_PRICING[this.modelName] || { input: 0, output: 0 };
719
+ return {
720
+ inputTokens: tokenInfo.inputTokens,
721
+ model: this.modelName,
722
+ pricing,
723
+ estimatedInputCost: tokenInfo.inputTokens / 1e6 * pricing.input,
724
+ note: "Cost is for input tokens only; output cost depends on response length"
725
+ };
726
+ }
727
+ // ── Application-Level Retry ──────────────────────────────────────────────
728
+ /**
729
+ * Wraps an async function with retry logic.
730
+ * Note: The Anthropic SDK handles 429s natively via maxRetries.
731
+ * This is for application-level retries (e.g., Transformer self-healing).
732
+ * @param {() => Promise<T>} fn - The async function to execute
733
+ * @returns {Promise<T>}
734
+ * @template T
735
+ * @protected
736
+ */
737
+ async _withRetry(fn) {
738
+ return await fn();
739
+ }
740
+ // ── Private Helpers ──────────────────────────────────────────────────────
741
+ /**
742
+ * Configures the log level based on options, env vars, or NODE_ENV.
743
+ * @param {string} [logLevel]
744
+ * @private
745
+ */
746
+ _configureLogLevel(logLevel) {
747
+ if (logLevel) {
748
+ if (logLevel === "none") {
749
+ logger_default.level = "silent";
750
+ } else {
751
+ logger_default.level = logLevel;
752
+ }
753
+ } else if (LOG_LEVEL) {
754
+ logger_default.level = LOG_LEVEL;
755
+ } else if (NODE_ENV === "dev") {
756
+ logger_default.level = "debug";
757
+ } else if (NODE_ENV === "test") {
758
+ logger_default.level = "warn";
759
+ } else if (NODE_ENV.startsWith("prod")) {
760
+ logger_default.level = "error";
761
+ } else {
762
+ logger_default.level = "info";
763
+ }
764
+ }
765
+ };
766
+ var base_default = BaseClaude;
767
+
768
+ // transformer.js
769
+ var import_promises = __toESM(require("fs/promises"), 1);
770
+ var import_path = __toESM(require("path"), 1);
771
+ var DEFAULT_SYSTEM_INSTRUCTIONS = `
772
+ You are an expert JSON transformation engine. Your task is to accurately convert data payloads from one format to another.
773
+
774
+ You will be provided with example transformations (Source JSON -> Target JSON).
775
+
776
+ Learn the mapping rules from these examples.
777
+
778
+ When presented with new Source JSON, apply the learned transformation rules to produce a new Target JSON payload.
779
+
780
+ Always respond ONLY with a valid JSON object that strictly adheres to the expected output format.
781
+
782
+ Do not include any additional text, explanations, or formatting before or after the JSON object.
783
+
784
+ Do not wrap your response in markdown code blocks.
785
+ `;
786
+ var Transformer = class extends base_default {
787
+ /**
788
+ * @param {TransformerOptions} [options={}]
789
+ */
790
+ constructor(options = {}) {
791
+ if (options.systemPrompt === void 0) {
792
+ options = { ...options, systemPrompt: DEFAULT_SYSTEM_INSTRUCTIONS };
793
+ }
794
+ super(options);
795
+ this.onlyJSON = options.onlyJSON !== void 0 ? options.onlyJSON : true;
796
+ this.promptKey = options.promptKey || options.sourceKey || "PROMPT";
797
+ this.answerKey = options.answerKey || options.targetKey || "ANSWER";
798
+ this.contextKey = options.contextKey || "CONTEXT";
799
+ this.explanationKey = options.explanationKey || "EXPLANATION";
800
+ this.systemPromptKey = options.systemPromptKey || "SYSTEM";
801
+ if (this.promptKey === this.answerKey) {
802
+ throw new Error("Source and target keys cannot be the same. Please provide distinct keys.");
803
+ }
804
+ this.examplesFile = options.examplesFile || null;
805
+ this.exampleData = options.exampleData || null;
806
+ this.asyncValidator = options.asyncValidator || null;
807
+ this.validationRetries = options.maxRetries || 3;
808
+ this.retryDelay = options.retryDelay || 1e3;
809
+ logger_default.debug(`Transformer keys \u2014 Source: "${this.promptKey}", Target: "${this.answerKey}", Context: "${this.contextKey}"`);
810
+ }
811
+ // ── Seeding ──────────────────────────────────────────────────────────────
812
+ /**
813
+ * Seeds the conversation with transformation examples using the configured key mapping.
814
+ * Overrides base seed() to use Transformer-specific keys and support
815
+ * examplesFile/exampleData fallbacks.
816
+ *
817
+ * @param {TransformationExample[]} [examples] - Array of example objects
818
+ * @returns {Promise<Array>} The updated history
819
+ */
820
+ async seed(examples) {
821
+ await this.init();
822
+ if (!examples || !Array.isArray(examples) || examples.length === 0) {
823
+ if (this.examplesFile) {
824
+ logger_default.debug(`No examples provided, loading from file: ${this.examplesFile}`);
825
+ try {
826
+ const filePath = import_path.default.resolve(this.examplesFile);
827
+ const raw = await import_promises.default.readFile(filePath, "utf-8");
828
+ examples = JSON.parse(raw);
829
+ } catch (err) {
830
+ throw new Error(`Could not load examples from file: ${this.examplesFile}. ${err.message}`);
831
+ }
832
+ } else if (this.exampleData) {
833
+ logger_default.debug(`Using example data provided in options.`);
834
+ if (Array.isArray(this.exampleData)) {
835
+ examples = this.exampleData;
836
+ } else {
837
+ throw new Error(`Invalid example data provided. Expected an array of examples.`);
838
+ }
839
+ } else {
840
+ logger_default.debug("No examples provided and no examples file specified. Skipping seeding.");
841
+ return this.getHistory();
842
+ }
843
+ }
844
+ return await super.seed(examples, {
845
+ promptKey: this.promptKey,
846
+ answerKey: this.answerKey,
847
+ contextKey: this.contextKey,
848
+ explanationKey: this.explanationKey,
849
+ systemPromptKey: this.systemPromptKey
850
+ });
851
+ }
852
+ // ── Primary Send Method ──────────────────────────────────────────────────
853
+ /**
854
+ * Transforms a payload using the seeded examples and model.
855
+ * Includes validation and automatic retry with AI-powered error correction.
856
+ *
857
+ * @param {Object|string} payload - The source payload to transform
858
+ * @param {Object} [opts={}] - Per-message options
859
+ * @param {AsyncValidatorFunction|null} [validatorFn] - Validator for this call (overrides constructor validator)
860
+ * @returns {Promise<Object>} The transformed payload
861
+ */
862
+ async send(payload, opts = {}, validatorFn = null) {
863
+ if (!this._initialized) await this.init();
864
+ const validator = validatorFn || this.asyncValidator;
865
+ if (opts.stateless) {
866
+ return await this._statelessSend(payload, opts, validator);
867
+ }
868
+ const maxRetries = opts.maxRetries ?? this.validationRetries;
869
+ const retryDelay = opts.retryDelay ?? this.retryDelay;
870
+ let lastPayload = this._preparePayload(payload);
871
+ this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 0 };
872
+ let lastError = null;
873
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
874
+ try {
875
+ const transformedPayload = attempt === 0 ? await this.rawSend(lastPayload) : await this.rebuild(lastPayload, lastError.message);
876
+ if (this.lastResponseMetadata) {
877
+ this._cumulativeUsage.promptTokens += this.lastResponseMetadata.promptTokens || 0;
878
+ this._cumulativeUsage.responseTokens += this.lastResponseMetadata.responseTokens || 0;
879
+ this._cumulativeUsage.totalTokens += this.lastResponseMetadata.totalTokens || 0;
880
+ this._cumulativeUsage.attempts = attempt + 1;
881
+ }
882
+ lastPayload = transformedPayload;
883
+ if (validator) {
884
+ await validator(transformedPayload);
885
+ }
886
+ logger_default.debug(`Transformation succeeded on attempt ${attempt + 1}`);
887
+ return transformedPayload;
888
+ } catch (error) {
889
+ lastError = error;
890
+ logger_default.warn(`Attempt ${attempt + 1} failed: ${error.message}`);
891
+ if (attempt >= maxRetries) {
892
+ logger_default.error(`All ${maxRetries + 1} attempts failed.`);
893
+ throw new Error(`Transformation failed after ${maxRetries + 1} attempts. Last error: ${error.message}`);
894
+ }
895
+ const delay = retryDelay * Math.pow(2, attempt);
896
+ await new Promise((res) => setTimeout(res, delay));
897
+ }
898
+ }
899
+ }
900
+ // ── Raw Send ─────────────────────────────────────────────────────────────
901
+ /**
902
+ * Sends a single prompt to the model and parses the JSON response.
903
+ * No validation or retry logic.
904
+ *
905
+ * @param {Object|string} payload - The source payload
906
+ * @returns {Promise<Object>} The transformed payload
907
+ */
908
+ async rawSend(payload) {
909
+ if (!this._initialized) await this.init();
910
+ const actualPayload = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
911
+ try {
912
+ const response = await this._sendMessage(actualPayload);
913
+ const modelResponse = this._extractText(response);
914
+ if (response.usage && logger_default.level !== "silent") {
915
+ logger_default.debug(`API response: model=${response.model || "unknown"}, tokens=${response.usage.input_tokens + response.usage.output_tokens}`);
916
+ }
917
+ const extractedJSON = extractJSON(modelResponse);
918
+ if (extractedJSON?.data) {
919
+ return extractedJSON.data;
920
+ }
921
+ return extractedJSON;
922
+ } catch (error) {
923
+ if (this.onlyJSON && error.message.includes("Could not extract valid JSON")) {
924
+ throw new Error(`Invalid JSON response from Claude: ${error.message}`);
925
+ }
926
+ throw new Error(`Transformation failed: ${error.message}`);
927
+ }
928
+ }
929
+ // ── Rebuild ──────────────────────────────────────────────────────────────
930
+ /**
931
+ * Asks the model to fix a payload that failed validation.
932
+ *
933
+ * @param {Object} lastPayload - The payload that failed
934
+ * @param {string} serverError - The error message
935
+ * @returns {Promise<Object>} Corrected payload
936
+ */
937
+ async rebuild(lastPayload, serverError) {
938
+ await this.init();
939
+ const prompt = `
940
+ The previous JSON payload (below) failed validation.
941
+ The server's error message is quoted afterward.
942
+
943
+ ---------------- BAD PAYLOAD ----------------
944
+ ${JSON.stringify(lastPayload, null, 2)}
945
+
946
+
947
+ ---------------- SERVER ERROR ----------------
948
+ ${serverError}
949
+
950
+ Please return a NEW JSON payload that corrects the issue.
951
+ Respond with JSON only \u2013 no comments or explanations.
952
+ `;
953
+ let response;
954
+ try {
955
+ response = await this._sendMessage(prompt);
956
+ } catch (err) {
957
+ throw new Error(`Claude call failed while repairing payload: ${err.message}`);
958
+ }
959
+ try {
960
+ const text = this._extractText(response);
961
+ return extractJSON(text);
962
+ } catch (parseErr) {
963
+ throw new Error(`Claude returned non-JSON while repairing payload: ${parseErr.message}`);
964
+ }
965
+ }
966
+ // ── Stateless Send ───────────────────────────────────────────────────────
967
+ /**
968
+ * Sends a one-off message (not using chat history).
969
+ * Does NOT affect conversation history.
970
+ * @param {Object|string} payload
971
+ * @param {Object} [opts={}]
972
+ * @param {AsyncValidatorFunction|null} [validatorFn]
973
+ * @returns {Promise<Object>}
974
+ * @private
975
+ */
976
+ async _statelessSend(payload, opts = {}, validatorFn = null) {
977
+ const payloadStr = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
978
+ const messages = [];
979
+ if (this.exampleCount > 0) {
980
+ const exampleHistory = this.history.slice(0, this.exampleCount);
981
+ messages.push(...exampleHistory);
982
+ }
983
+ messages.push({ role: "user", content: payloadStr });
984
+ const params = {
985
+ model: this.modelName,
986
+ max_tokens: opts.maxTokens || this.maxTokens,
987
+ messages,
988
+ ...this._buildSystemParam() && { system: this._buildSystemParam() }
989
+ };
990
+ if (this.thinking) {
991
+ params.thinking = this.thinking;
992
+ } else {
993
+ if (this.temperature !== void 0) params.temperature = this.temperature;
994
+ if (this.topP !== void 0) params.top_p = this.topP;
995
+ }
996
+ const response = await this.client.messages.create(params);
997
+ this._captureMetadata(response);
998
+ this._cumulativeUsage = {
999
+ promptTokens: this.lastResponseMetadata.promptTokens,
1000
+ responseTokens: this.lastResponseMetadata.responseTokens,
1001
+ totalTokens: this.lastResponseMetadata.totalTokens,
1002
+ attempts: 1
1003
+ };
1004
+ const modelResponse = this._extractText(response);
1005
+ const extractedJSON = extractJSON(modelResponse);
1006
+ let transformedPayload = extractedJSON?.data ? extractedJSON.data : extractedJSON;
1007
+ if (validatorFn) {
1008
+ await validatorFn(transformedPayload);
1009
+ }
1010
+ return transformedPayload;
1011
+ }
1012
+ // ── History Management ───────────────────────────────────────────────────
1013
+ /**
1014
+ * Clears conversation history while preserving seeded examples.
1015
+ * @returns {Promise<void>}
1016
+ */
1017
+ async clearHistory() {
1018
+ const exampleHistory = this.history.slice(0, this.exampleCount || 0);
1019
+ this.history = exampleHistory;
1020
+ this.lastResponseMetadata = null;
1021
+ this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 0 };
1022
+ logger_default.debug(`Conversation cleared. Preserved ${exampleHistory.length} example items.`);
1023
+ }
1024
+ /**
1025
+ * Fully resets the conversation, clearing all history including examples.
1026
+ * @returns {Promise<void>}
1027
+ */
1028
+ async reset() {
1029
+ this.history = [];
1030
+ this.exampleCount = 0;
1031
+ this.lastResponseMetadata = null;
1032
+ this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 0 };
1033
+ logger_default.debug("Conversation fully reset.");
1034
+ }
1035
+ /**
1036
+ * Updates system prompt.
1037
+ * @param {string} newPrompt - The new system prompt
1038
+ * @returns {Promise<void>}
1039
+ */
1040
+ async updateSystemPrompt(newPrompt) {
1041
+ if (!newPrompt || typeof newPrompt !== "string") {
1042
+ throw new Error("System prompt must be a non-empty string");
1043
+ }
1044
+ this.systemPrompt = newPrompt.trim();
1045
+ logger_default.debug("System prompt updated.");
1046
+ }
1047
+ // ── Private Helpers ──────────────────────────────────────────────────────
1048
+ /**
1049
+ * Normalizes a payload to a string for sending.
1050
+ * @param {*} payload
1051
+ * @returns {string}
1052
+ * @private
1053
+ */
1054
+ _preparePayload(payload) {
1055
+ if (payload && isJSON(payload)) {
1056
+ return JSON.stringify(payload, null, 2);
1057
+ } else if (typeof payload === "string") {
1058
+ return payload;
1059
+ } else if (typeof payload === "boolean" || typeof payload === "number") {
1060
+ return payload.toString();
1061
+ } else if (payload === null || payload === void 0) {
1062
+ return JSON.stringify({});
1063
+ } else {
1064
+ throw new Error("Invalid source payload. Must be a JSON object or string.");
1065
+ }
1066
+ }
1067
+ };
1068
+ var transformer_default = Transformer;
1069
+
1070
+ // chat.js
1071
+ var Chat = class extends base_default {
1072
+ /**
1073
+ * @param {ChatOptions} [options={}]
1074
+ */
1075
+ constructor(options = {}) {
1076
+ if (options.systemPrompt === void 0) {
1077
+ options = { ...options, systemPrompt: "You are a helpful AI assistant." };
1078
+ }
1079
+ super(options);
1080
+ logger_default.debug(`Chat created with model: ${this.modelName}`);
1081
+ }
1082
+ /**
1083
+ * Send a text message and get a response. Adds to conversation history.
1084
+ *
1085
+ * @param {string} message - The user's message
1086
+ * @param {Object} [opts={}] - Per-message options
1087
+ * @returns {Promise<ChatResponse>} Response with text and usage data
1088
+ */
1089
+ async send(message, opts = {}) {
1090
+ const response = await this._sendMessage(message, opts);
1091
+ const text = this._extractText(response);
1092
+ this._cumulativeUsage = {
1093
+ promptTokens: this.lastResponseMetadata.promptTokens,
1094
+ responseTokens: this.lastResponseMetadata.responseTokens,
1095
+ totalTokens: this.lastResponseMetadata.totalTokens,
1096
+ attempts: 1
1097
+ };
1098
+ return {
1099
+ text,
1100
+ usage: this.getLastUsage()
1101
+ };
1102
+ }
1103
+ };
1104
+ var chat_default = Chat;
1105
+
1106
+ // message.js
1107
+ var Message = class extends base_default {
1108
+ /**
1109
+ * @param {MessageOptions} [options={}]
1110
+ */
1111
+ constructor(options = {}) {
1112
+ super(options);
1113
+ this._responseSchema = options.responseSchema || null;
1114
+ this._isStructured = !!(this._responseSchema || options.responseFormat === "json");
1115
+ logger_default.debug(`Message created (structured=${this._isStructured}, nativeSchema=${!!this._responseSchema})`);
1116
+ }
1117
+ /**
1118
+ * Initialize the Message client.
1119
+ * Override: stateless, no history needed.
1120
+ * @param {boolean} [force=false]
1121
+ * @returns {Promise<void>}
1122
+ */
1123
+ async init(force = false) {
1124
+ if (this._initialized && !force) return;
1125
+ await this._ensureClient();
1126
+ logger_default.debug(`Initializing ${this.constructor.name} with model: ${this.modelName}...`);
1127
+ if (this.healthCheck) {
1128
+ try {
1129
+ await this.client.messages.create({
1130
+ model: this.modelName,
1131
+ max_tokens: 1,
1132
+ messages: [{ role: "user", content: "hi" }]
1133
+ });
1134
+ logger_default.debug(`${this.constructor.name}: API connection successful.`);
1135
+ } catch (e) {
1136
+ throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
1137
+ }
1138
+ }
1139
+ this._initialized = true;
1140
+ logger_default.debug(`${this.constructor.name}: Initialized (stateless mode).`);
1141
+ }
1142
+ /**
1143
+ * Send a stateless message and get a response.
1144
+ * Each call is independent — no history is maintained.
1145
+ *
1146
+ * @param {Object|string} payload - The message or data to send
1147
+ * @param {Object} [opts={}] - Per-message options
1148
+ * @returns {Promise<MessageResponse>} Response with text, optional data, and usage
1149
+ */
1150
+ async send(payload, opts = {}) {
1151
+ if (!this._initialized) await this.init();
1152
+ const payloadStr = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
1153
+ let systemParam = this._buildSystemParam();
1154
+ if (this._isStructured && !this._responseSchema) {
1155
+ if (systemParam) {
1156
+ const jsonInstruction = "\n\nAlways respond ONLY with valid JSON. No markdown code blocks, no preamble text.";
1157
+ if (typeof systemParam === "string") {
1158
+ systemParam = systemParam + jsonInstruction;
1159
+ } else if (Array.isArray(systemParam)) {
1160
+ systemParam = [...systemParam, { type: "text", text: jsonInstruction }];
1161
+ }
1162
+ } else {
1163
+ systemParam = "Always respond ONLY with valid JSON. No markdown code blocks, no preamble text.";
1164
+ }
1165
+ }
1166
+ const params = {
1167
+ model: this.modelName,
1168
+ max_tokens: opts.maxTokens || this.maxTokens,
1169
+ messages: [{ role: (
1170
+ /** @type {'user'} */
1171
+ "user"
1172
+ ), content: payloadStr }],
1173
+ ...systemParam && { system: systemParam }
1174
+ };
1175
+ if (this._responseSchema && !this.vertexai) {
1176
+ params.output_config = {
1177
+ format: {
1178
+ type: "json_schema",
1179
+ schema: this._responseSchema
1180
+ }
1181
+ };
1182
+ } else if (this._responseSchema && this.vertexai) {
1183
+ const schemaInstruction = `
1184
+
1185
+ Respond ONLY with valid JSON matching this schema:
1186
+ ${JSON.stringify(this._responseSchema, null, 2)}
1187
+ No markdown code blocks, no preamble text.`;
1188
+ if (typeof params.system === "string") {
1189
+ params.system += schemaInstruction;
1190
+ } else if (Array.isArray(params.system)) {
1191
+ params.system = [...params.system, { type: "text", text: schemaInstruction }];
1192
+ } else {
1193
+ params.system = schemaInstruction.trim();
1194
+ }
1195
+ }
1196
+ if (this.thinking) {
1197
+ params.thinking = this.thinking;
1198
+ } else {
1199
+ if (this.temperature !== void 0) params.temperature = this.temperature;
1200
+ if (this.topP !== void 0) params.top_p = this.topP;
1201
+ }
1202
+ const response = await this.client.messages.create(params);
1203
+ this._captureMetadata(response);
1204
+ this._cumulativeUsage = {
1205
+ promptTokens: this.lastResponseMetadata.promptTokens,
1206
+ responseTokens: this.lastResponseMetadata.responseTokens,
1207
+ totalTokens: this.lastResponseMetadata.totalTokens,
1208
+ attempts: 1
1209
+ };
1210
+ const text = this._extractText(response);
1211
+ const result = {
1212
+ text,
1213
+ usage: this.getLastUsage()
1214
+ };
1215
+ if (this._isStructured) {
1216
+ try {
1217
+ if (this._responseSchema) {
1218
+ result.data = JSON.parse(text);
1219
+ } else {
1220
+ result.data = extractJSON(text);
1221
+ }
1222
+ } catch (e) {
1223
+ logger_default.warn(`Could not parse structured response: ${e.message}`);
1224
+ result.data = null;
1225
+ }
1226
+ }
1227
+ return result;
1228
+ }
1229
+ // ── No-ops for stateless class ──
1230
+ /** @returns {Array} Always returns empty array (stateless). */
1231
+ getHistory() {
1232
+ return [];
1233
+ }
1234
+ /** No-op (stateless). */
1235
+ async clearHistory() {
1236
+ }
1237
+ /** Not supported on Message (stateless). */
1238
+ async seed() {
1239
+ logger_default.warn("Message is stateless \u2014 seed() has no effect. Use Transformer or Chat for few-shot learning.");
1240
+ return [];
1241
+ }
1242
+ };
1243
+ var message_default = Message;
1244
+
1245
+ // tool-agent.js
1246
+ var ToolAgent = class extends base_default {
1247
+ /**
1248
+ * @param {ToolAgentOptions} [options={}]
1249
+ */
1250
+ constructor(options = {}) {
1251
+ if (options.systemPrompt === void 0) {
1252
+ options = { ...options, systemPrompt: "You are a helpful AI assistant." };
1253
+ }
1254
+ super(options);
1255
+ this.tools = (options.tools || []).map((t) => ({
1256
+ name: t.name,
1257
+ description: t.description,
1258
+ input_schema: t.input_schema || t.inputSchema || t.parametersJsonSchema
1259
+ }));
1260
+ this.toolExecutor = options.toolExecutor || null;
1261
+ if (this.tools.length > 0 && !this.toolExecutor) {
1262
+ throw new Error("ToolAgent: tools provided without a toolExecutor. Provide a toolExecutor function to handle tool calls.");
1263
+ }
1264
+ if (this.toolExecutor && this.tools.length === 0) {
1265
+ throw new Error("ToolAgent: toolExecutor provided without tools. Provide tool declarations so the model knows what tools are available.");
1266
+ }
1267
+ this.toolChoice = options.toolChoice ?? void 0;
1268
+ this.disableParallelToolUse = options.disableParallelToolUse ?? false;
1269
+ this.maxToolRounds = options.maxToolRounds || 10;
1270
+ this.onToolCall = options.onToolCall || null;
1271
+ this.onBeforeExecution = options.onBeforeExecution || null;
1272
+ this._stopped = false;
1273
+ logger_default.debug(`ToolAgent created with ${this.tools.length} tools`);
1274
+ }
1275
+ /**
1276
+ * Builds the tool_choice parameter for API calls.
1277
+ * @returns {Object|undefined}
1278
+ * @private
1279
+ */
1280
+ _buildToolChoice() {
1281
+ let choice = this.toolChoice;
1282
+ if (!choice && !this.disableParallelToolUse) return void 0;
1283
+ if (!choice) choice = { type: "auto" };
1284
+ const result = { ...choice };
1285
+ if (this.disableParallelToolUse) {
1286
+ result.disable_parallel_tool_use = true;
1287
+ }
1288
+ return result;
1289
+ }
1290
+ // ── Non-Streaming Chat ───────────────────────────────────────────────────
1291
+ /**
1292
+ * Send a message and get a complete response (non-streaming).
1293
+ * Automatically handles the tool-use loop.
1294
+ *
1295
+ * @param {string} message - The user's message
1296
+ * @param {Object} [opts={}] - Per-message options
1297
+ * @returns {Promise<AgentResponse>} Response with text, toolCalls, and usage
1298
+ */
1299
+ async chat(message, opts = {}) {
1300
+ if (!this._initialized) await this.init();
1301
+ this._stopped = false;
1302
+ const allToolCalls = [];
1303
+ const toolChoice = this._buildToolChoice();
1304
+ let response = await this._sendMessage(message, { tools: this.tools, ...toolChoice && { tool_choice: toolChoice } });
1305
+ for (let round = 0; round < this.maxToolRounds; round++) {
1306
+ if (this._stopped) break;
1307
+ if (response.stop_reason !== "tool_use") break;
1308
+ const toolUseBlocks = response.content.filter((b) => b.type === "tool_use");
1309
+ if (toolUseBlocks.length === 0) break;
1310
+ const toolResults = [];
1311
+ for (const block of toolUseBlocks) {
1312
+ if (this.onToolCall) {
1313
+ try {
1314
+ this.onToolCall(block.name, block.input);
1315
+ } catch (e) {
1316
+ logger_default.warn(`onToolCall callback error: ${e.message}`);
1317
+ }
1318
+ }
1319
+ if (this.onBeforeExecution) {
1320
+ try {
1321
+ const allowed = await this.onBeforeExecution(block.name, block.input);
1322
+ if (allowed === false) {
1323
+ const result2 = { error: "Execution denied by onBeforeExecution callback" };
1324
+ allToolCalls.push({ name: block.name, args: block.input, result: result2 });
1325
+ toolResults.push({
1326
+ type: "tool_result",
1327
+ tool_use_id: block.id,
1328
+ content: JSON.stringify(result2)
1329
+ });
1330
+ continue;
1331
+ }
1332
+ } catch (e) {
1333
+ logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
1334
+ }
1335
+ }
1336
+ let result;
1337
+ try {
1338
+ result = await this.toolExecutor(block.name, block.input);
1339
+ } catch (err) {
1340
+ logger_default.warn(`Tool ${block.name} failed: ${err.message}`);
1341
+ result = { error: err.message };
1342
+ }
1343
+ allToolCalls.push({ name: block.name, args: block.input, result });
1344
+ toolResults.push({
1345
+ type: "tool_result",
1346
+ tool_use_id: block.id,
1347
+ content: typeof result === "string" ? result : JSON.stringify(result)
1348
+ });
1349
+ }
1350
+ response = await this._sendMessage(toolResults, { tools: this.tools, ...toolChoice && { tool_choice: toolChoice } });
1351
+ }
1352
+ this._cumulativeUsage = {
1353
+ promptTokens: this.lastResponseMetadata.promptTokens,
1354
+ responseTokens: this.lastResponseMetadata.responseTokens,
1355
+ totalTokens: this.lastResponseMetadata.totalTokens,
1356
+ attempts: 1
1357
+ };
1358
+ return {
1359
+ text: this._extractText(response),
1360
+ toolCalls: allToolCalls,
1361
+ usage: this.getLastUsage()
1362
+ };
1363
+ }
1364
+ // ── Streaming ────────────────────────────────────────────────────────────
1365
+ /**
1366
+ * Send a message and stream the response as events.
1367
+ * Automatically handles the tool-use loop between streamed rounds.
1368
+ *
1369
+ * Event types:
1370
+ * - `text` — A chunk of the agent's text response
1371
+ * - `tool_call` — The agent is about to call a tool
1372
+ * - `tool_result` — A tool finished executing
1373
+ * - `done` — The agent finished
1374
+ *
1375
+ * @param {string} message - The user's message
1376
+ * @param {Object} [opts={}] - Per-message options
1377
+ * @yields {AgentStreamEvent}
1378
+ */
1379
+ async *stream(message, opts = {}) {
1380
+ if (!this._initialized) await this.init();
1381
+ this._stopped = false;
1382
+ const allToolCalls = [];
1383
+ let fullText = "";
1384
+ const toolChoice = this._buildToolChoice();
1385
+ let stream = await this._streamMessage(message, { tools: this.tools, ...toolChoice && { tool_choice: toolChoice } });
1386
+ for (let round = 0; round < this.maxToolRounds; round++) {
1387
+ if (this._stopped) break;
1388
+ const finalMessage = await stream.finalMessage();
1389
+ const toolUseBlocks = [];
1390
+ for (const block of finalMessage.content) {
1391
+ if (block.type === "text") {
1392
+ fullText += block.text;
1393
+ yield { type: "text", text: block.text };
1394
+ } else if (block.type === "tool_use") {
1395
+ toolUseBlocks.push(block);
1396
+ }
1397
+ }
1398
+ this.history.push({ role: "assistant", content: finalMessage.content });
1399
+ this._captureMetadata(finalMessage);
1400
+ if (finalMessage.stop_reason !== "tool_use" || toolUseBlocks.length === 0) {
1401
+ yield {
1402
+ type: "done",
1403
+ fullText,
1404
+ usage: this.getLastUsage()
1405
+ };
1406
+ return;
1407
+ }
1408
+ const toolResults = [];
1409
+ for (const block of toolUseBlocks) {
1410
+ if (this._stopped) break;
1411
+ yield { type: "tool_call", toolName: block.name, args: block.input };
1412
+ if (this.onToolCall) {
1413
+ try {
1414
+ this.onToolCall(block.name, block.input);
1415
+ } catch (e) {
1416
+ logger_default.warn(`onToolCall callback error: ${e.message}`);
1417
+ }
1418
+ }
1419
+ let denied = false;
1420
+ if (this.onBeforeExecution) {
1421
+ try {
1422
+ const allowed = await this.onBeforeExecution(block.name, block.input);
1423
+ if (allowed === false) denied = true;
1424
+ } catch (e) {
1425
+ logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
1426
+ }
1427
+ }
1428
+ let result;
1429
+ if (denied) {
1430
+ result = { error: "Execution denied by onBeforeExecution callback" };
1431
+ } else {
1432
+ try {
1433
+ result = await this.toolExecutor(block.name, block.input);
1434
+ } catch (err) {
1435
+ logger_default.warn(`Tool ${block.name} failed: ${err.message}`);
1436
+ result = { error: err.message };
1437
+ }
1438
+ }
1439
+ allToolCalls.push({ name: block.name, args: block.input, result });
1440
+ yield { type: "tool_result", toolName: block.name, result };
1441
+ toolResults.push({
1442
+ type: "tool_result",
1443
+ tool_use_id: block.id,
1444
+ content: typeof result === "string" ? result : JSON.stringify(result)
1445
+ });
1446
+ }
1447
+ stream = await this._streamMessage(toolResults, { tools: this.tools, ...toolChoice && { tool_choice: toolChoice } });
1448
+ }
1449
+ yield {
1450
+ type: "done",
1451
+ fullText,
1452
+ usage: this.getLastUsage(),
1453
+ warning: this._stopped ? "Agent was stopped" : "Max tool rounds reached"
1454
+ };
1455
+ }
1456
+ // ── Stop ────────────────────────────────────────────────────────────────
1457
+ /**
1458
+ * Stop the agent before the next tool execution round.
1459
+ */
1460
+ stop() {
1461
+ this._stopped = true;
1462
+ logger_default.info("ToolAgent stopped");
1463
+ }
1464
+ };
1465
+ var tool_agent_default = ToolAgent;
1466
+
1467
+ // code-agent.js
1468
+ var import_node_child_process = require("node:child_process");
1469
+ var import_promises2 = require("node:fs/promises");
1470
+ var import_node_path = require("node:path");
1471
+ var import_node_crypto = require("node:crypto");
1472
+ var MAX_OUTPUT_CHARS = 5e4;
1473
+ var MAX_FILE_TREE_LINES = 500;
1474
+ var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "coverage", ".next", "build", "__pycache__"]);
1475
+ var CodeAgent = class extends base_default {
1476
+ /**
1477
+ * @param {CodeAgentOptions} [options={}]
1478
+ */
1479
+ constructor(options = {}) {
1480
+ if (options.systemPrompt === void 0) {
1481
+ options = { ...options, systemPrompt: "" };
1482
+ }
1483
+ super(options);
1484
+ this.workingDirectory = options.workingDirectory || process.cwd();
1485
+ this.maxRounds = options.maxRounds || 10;
1486
+ this.timeout = options.timeout || 3e4;
1487
+ this.onBeforeExecution = options.onBeforeExecution || null;
1488
+ this.onCodeExecution = options.onCodeExecution || null;
1489
+ this.importantFiles = options.importantFiles || [];
1490
+ this.writeDir = options.writeDir || (0, import_node_path.join)(this.workingDirectory, "tmp");
1491
+ this.keepArtifacts = options.keepArtifacts ?? false;
1492
+ this.comments = options.comments ?? false;
1493
+ this.codeMaxRetries = options.maxRetries ?? 3;
1494
+ this._codebaseContext = null;
1495
+ this._contextGathered = false;
1496
+ this._stopped = false;
1497
+ this._activeProcess = null;
1498
+ this._userSystemPrompt = options.systemPrompt || "";
1499
+ this._allExecutions = [];
1500
+ this._tools = [{
1501
+ name: "execute_code",
1502
+ description: "Execute JavaScript code in a Node.js child process. The code has access to all Node.js built-in modules (fs, path, child_process, http, etc.). Use console.log() to produce output that will be returned to you. The code runs in the working directory with the same environment variables as the parent process.",
1503
+ input_schema: {
1504
+ type: "object",
1505
+ properties: {
1506
+ code: {
1507
+ type: "string",
1508
+ description: "JavaScript code to execute. Use console.log() for output. You can import any built-in Node.js module."
1509
+ },
1510
+ purpose: {
1511
+ type: "string",
1512
+ description: 'A short 2-4 word slug describing what this script does (e.g., "read-config", "parse-logs", "fetch-api-data"). Used for naming the script file.'
1513
+ }
1514
+ },
1515
+ required: ["code"]
1516
+ }
1517
+ }];
1518
+ logger_default.debug(`CodeAgent created for directory: ${this.workingDirectory}`);
1519
+ }
1520
+ // ── Init ─────────────────────────────────────────────────────────────────
1521
+ /**
1522
+ * Initialize the agent: gather codebase context and build system prompt.
1523
+ * @param {boolean} [force=false]
1524
+ */
1525
+ async init(force = false) {
1526
+ if (this._initialized && !force) return;
1527
+ await this._ensureClient();
1528
+ if (!this._contextGathered || force) {
1529
+ await this._gatherCodebaseContext();
1530
+ }
1531
+ this.systemPrompt = this._buildSystemPrompt();
1532
+ await super.init(force);
1533
+ }
1534
+ // ── Context Gathering ────────────────────────────────────────────────────
1535
+ /**
1536
+ * @private
1537
+ */
1538
+ async _gatherCodebaseContext() {
1539
+ let fileTree = "";
1540
+ try {
1541
+ fileTree = await this._getFileTreeGit();
1542
+ } catch {
1543
+ logger_default.debug("git ls-files failed, falling back to readdir");
1544
+ fileTree = await this._getFileTreeReaddir(this.workingDirectory, 0, 3);
1545
+ }
1546
+ const lines = fileTree.split("\n");
1547
+ if (lines.length > MAX_FILE_TREE_LINES) {
1548
+ const truncated = lines.slice(0, MAX_FILE_TREE_LINES).join("\n");
1549
+ fileTree = `${truncated}
1550
+ ... (${lines.length - MAX_FILE_TREE_LINES} more files)`;
1551
+ }
1552
+ let npmPackages = [];
1553
+ try {
1554
+ const pkgPath = (0, import_node_path.join)(this.workingDirectory, "package.json");
1555
+ const pkg = JSON.parse(await (0, import_promises2.readFile)(pkgPath, "utf-8"));
1556
+ npmPackages = [
1557
+ ...Object.keys(pkg.dependencies || {}),
1558
+ ...Object.keys(pkg.devDependencies || {})
1559
+ ];
1560
+ } catch {
1561
+ }
1562
+ const importantFileContents = [];
1563
+ if (this.importantFiles.length > 0) {
1564
+ const fileTreeLines = fileTree.split("\n").map((l) => l.trim()).filter(Boolean);
1565
+ for (const requested of this.importantFiles) {
1566
+ const resolved = this._resolveImportantFile(requested, fileTreeLines);
1567
+ if (!resolved) {
1568
+ logger_default.warn(`importantFiles: could not locate "${requested}"`);
1569
+ continue;
1570
+ }
1571
+ try {
1572
+ const fullPath = (0, import_node_path.join)(this.workingDirectory, resolved);
1573
+ const content = await (0, import_promises2.readFile)(fullPath, "utf-8");
1574
+ importantFileContents.push({ path: resolved, content });
1575
+ } catch (e) {
1576
+ logger_default.warn(`importantFiles: could not read "${resolved}": ${e.message}`);
1577
+ }
1578
+ }
1579
+ }
1580
+ this._codebaseContext = { fileTree, npmPackages, importantFileContents };
1581
+ this._contextGathered = true;
1582
+ }
1583
+ /**
1584
+ * @private
1585
+ */
1586
+ _resolveImportantFile(filename, fileTreeLines) {
1587
+ const exact = fileTreeLines.find((line) => line === filename);
1588
+ if (exact) return exact;
1589
+ const partial = fileTreeLines.find(
1590
+ (line) => line.endsWith("/" + filename) || line.endsWith(import_node_path.sep + filename)
1591
+ );
1592
+ return partial || null;
1593
+ }
1594
+ /**
1595
+ * @private
1596
+ */
1597
+ async _getFileTreeGit() {
1598
+ return new Promise((resolve2, reject) => {
1599
+ (0, import_node_child_process.execFile)("git", ["ls-files"], {
1600
+ cwd: this.workingDirectory,
1601
+ timeout: 5e3,
1602
+ maxBuffer: 5 * 1024 * 1024
1603
+ }, (err, stdout) => {
1604
+ if (err) return reject(err);
1605
+ resolve2(stdout.trim());
1606
+ });
1607
+ });
1608
+ }
1609
+ /**
1610
+ * @private
1611
+ */
1612
+ async _getFileTreeReaddir(dir, depth, maxDepth) {
1613
+ if (depth >= maxDepth) return "";
1614
+ const entries = [];
1615
+ try {
1616
+ const items = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
1617
+ for (const item of items) {
1618
+ if (IGNORE_DIRS.has(item.name)) continue;
1619
+ if (item.name.startsWith(".") && depth === 0 && item.isDirectory()) continue;
1620
+ const relativePath = (0, import_node_path.join)(dir, item.name).replace(this.workingDirectory + "/", "");
1621
+ if (item.isFile()) {
1622
+ entries.push(relativePath);
1623
+ } else if (item.isDirectory()) {
1624
+ entries.push(relativePath + "/");
1625
+ const subEntries = await this._getFileTreeReaddir((0, import_node_path.join)(dir, item.name), depth + 1, maxDepth);
1626
+ if (subEntries) entries.push(subEntries);
1627
+ }
1628
+ }
1629
+ } catch {
1630
+ }
1631
+ return entries.join("\n");
1632
+ }
1633
+ /**
1634
+ * @private
1635
+ */
1636
+ _buildSystemPrompt() {
1637
+ const { fileTree, npmPackages, importantFileContents } = this._codebaseContext || { fileTree: "", npmPackages: [], importantFileContents: [] };
1638
+ let prompt = `You are a coding agent working in ${this.workingDirectory}.
1639
+
1640
+ ## Instructions
1641
+ - Use the execute_code tool to accomplish tasks by writing JavaScript code
1642
+ - Always provide a short descriptive \`purpose\` parameter (2-4 word slug like "read-config") when calling execute_code
1643
+ - Your code runs in a Node.js child process with access to all built-in modules
1644
+ - IMPORTANT: Your code runs as an ES module (.mjs). Use import syntax, NOT require():
1645
+ - import fs from 'fs';
1646
+ - import path from 'path';
1647
+ - import { execSync } from 'child_process';
1648
+ - Use console.log() to produce output \u2014 that's how results are returned to you
1649
+ - Write efficient scripts that do multiple things per execution when possible
1650
+ - For parallel async operations, use Promise.all():
1651
+ const [a, b] = await Promise.all([fetchA(), fetchB()]);
1652
+ - Read files with fs.readFileSync() when you need to understand their contents
1653
+ - Handle errors in your scripts with try/catch so you get useful error messages
1654
+ - Top-level await is supported
1655
+ - The working directory is: ${this.workingDirectory}`;
1656
+ if (this.comments) {
1657
+ prompt += `
1658
+ - Add a JSDoc @fileoverview comment at the top of each script explaining what it does
1659
+ - Add brief JSDoc @param comments for any functions you define`;
1660
+ } else {
1661
+ prompt += `
1662
+ - Do NOT write any comments in your code \u2014 save tokens. The code should be self-explanatory.`;
1663
+ }
1664
+ if (fileTree) {
1665
+ prompt += `
1666
+
1667
+ ## File Tree
1668
+ \`\`\`
1669
+ ${fileTree}
1670
+ \`\`\``;
1671
+ }
1672
+ if (npmPackages.length > 0) {
1673
+ prompt += `
1674
+
1675
+ ## Available Packages
1676
+ These npm packages are installed and can be imported: ${npmPackages.join(", ")}`;
1677
+ }
1678
+ if (importantFileContents && importantFileContents.length > 0) {
1679
+ prompt += `
1680
+
1681
+ ## Key Files`;
1682
+ for (const { path: filePath, content } of importantFileContents) {
1683
+ prompt += `
1684
+
1685
+ ### ${filePath}
1686
+ \`\`\`javascript
1687
+ ${content}
1688
+ \`\`\``;
1689
+ }
1690
+ }
1691
+ if (this._userSystemPrompt) {
1692
+ prompt += `
1693
+
1694
+ ## Additional Instructions
1695
+ ${this._userSystemPrompt}`;
1696
+ }
1697
+ return prompt;
1698
+ }
1699
+ // ── Code Execution ───────────────────────────────────────────────────────
1700
+ /**
1701
+ * @private
1702
+ */
1703
+ _slugify(purpose) {
1704
+ if (!purpose) return (0, import_node_crypto.randomUUID)().slice(0, 8);
1705
+ return purpose.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
1706
+ }
1707
+ /**
1708
+ * @private
1709
+ */
1710
+ async _executeCode(code, purpose) {
1711
+ if (this._stopped) {
1712
+ return { stdout: "", stderr: "Agent was stopped", exitCode: -1 };
1713
+ }
1714
+ if (this.onBeforeExecution) {
1715
+ try {
1716
+ const allowed = await this.onBeforeExecution(code);
1717
+ if (allowed === false) {
1718
+ return { stdout: "", stderr: "Execution denied by onBeforeExecution callback", exitCode: -1, denied: true };
1719
+ }
1720
+ } catch (e) {
1721
+ logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
1722
+ }
1723
+ }
1724
+ await (0, import_promises2.mkdir)(this.writeDir, { recursive: true });
1725
+ const slug = this._slugify(purpose);
1726
+ const tempFile = (0, import_node_path.join)(this.writeDir, `agent-${slug}-${Date.now()}.mjs`);
1727
+ try {
1728
+ await (0, import_promises2.writeFile)(tempFile, code, "utf-8");
1729
+ const result = await new Promise((resolve2) => {
1730
+ const child = (0, import_node_child_process.execFile)("node", [tempFile], {
1731
+ cwd: this.workingDirectory,
1732
+ timeout: this.timeout,
1733
+ env: process.env,
1734
+ maxBuffer: 10 * 1024 * 1024
1735
+ }, (err, stdout, stderr) => {
1736
+ this._activeProcess = null;
1737
+ if (err) {
1738
+ resolve2({
1739
+ stdout: err.stdout || stdout || "",
1740
+ stderr: (err.stderr || stderr || "") + (err.killed ? "\n[EXECUTION TIMED OUT]" : ""),
1741
+ exitCode: err.code || 1
1742
+ });
1743
+ } else {
1744
+ resolve2({ stdout: stdout || "", stderr: stderr || "", exitCode: 0 });
1745
+ }
1746
+ });
1747
+ this._activeProcess = child;
1748
+ });
1749
+ const totalLen = result.stdout.length + result.stderr.length;
1750
+ if (totalLen > MAX_OUTPUT_CHARS) {
1751
+ const half = Math.floor(MAX_OUTPUT_CHARS / 2);
1752
+ if (result.stdout.length > half) {
1753
+ result.stdout = result.stdout.slice(0, half) + "\n...[OUTPUT TRUNCATED]";
1754
+ }
1755
+ if (result.stderr.length > half) {
1756
+ result.stderr = result.stderr.slice(0, half) + "\n...[STDERR TRUNCATED]";
1757
+ }
1758
+ }
1759
+ this._allExecutions.push({
1760
+ code,
1761
+ purpose: purpose || null,
1762
+ output: result.stdout,
1763
+ stderr: result.stderr,
1764
+ exitCode: result.exitCode,
1765
+ filePath: this.keepArtifacts ? tempFile : null
1766
+ });
1767
+ if (this.onCodeExecution) {
1768
+ try {
1769
+ this.onCodeExecution(code, result);
1770
+ } catch (e) {
1771
+ logger_default.warn(`onCodeExecution callback error: ${e.message}`);
1772
+ }
1773
+ }
1774
+ return result;
1775
+ } finally {
1776
+ if (!this.keepArtifacts) {
1777
+ try {
1778
+ await (0, import_promises2.unlink)(tempFile);
1779
+ } catch {
1780
+ }
1781
+ }
1782
+ }
1783
+ }
1784
+ /**
1785
+ * @private
1786
+ */
1787
+ _formatOutput(result) {
1788
+ let output = "";
1789
+ if (result.stdout) output += result.stdout;
1790
+ if (result.stderr) output += (output ? "\n" : "") + `[STDERR]: ${result.stderr}`;
1791
+ if (result.exitCode !== 0) output += (output ? "\n" : "") + `[EXIT CODE]: ${result.exitCode}`;
1792
+ return output || "(no output)";
1793
+ }
1794
+ // ── Non-Streaming Chat ───────────────────────────────────────────────────
1795
+ /**
1796
+ * Send a message and get a complete response (non-streaming).
1797
+ * Automatically handles the code execution loop.
1798
+ *
1799
+ * @param {string} message - The user's message
1800
+ * @param {Object} [opts={}] - Per-message options
1801
+ * @returns {Promise<CodeAgentResponse>}
1802
+ */
1803
+ async chat(message, opts = {}) {
1804
+ if (!this._initialized) await this.init();
1805
+ this._stopped = false;
1806
+ const codeExecutions = [];
1807
+ let consecutiveFailures = 0;
1808
+ let response = await this._sendMessage(message, { tools: this._tools });
1809
+ for (let round = 0; round < this.maxRounds; round++) {
1810
+ if (this._stopped) break;
1811
+ if (response.stop_reason !== "tool_use") break;
1812
+ const toolUseBlocks = response.content.filter((b) => b.type === "tool_use");
1813
+ if (toolUseBlocks.length === 0) break;
1814
+ const toolResults = [];
1815
+ for (const block of toolUseBlocks) {
1816
+ if (this._stopped) break;
1817
+ const code = block.input?.code || "";
1818
+ const purpose = block.input?.purpose;
1819
+ const result = await this._executeCode(code, purpose);
1820
+ codeExecutions.push({
1821
+ code,
1822
+ purpose: this._slugify(purpose),
1823
+ output: result.stdout,
1824
+ stderr: result.stderr,
1825
+ exitCode: result.exitCode
1826
+ });
1827
+ if (result.exitCode !== 0 && !result.denied) {
1828
+ consecutiveFailures++;
1829
+ } else {
1830
+ consecutiveFailures = 0;
1831
+ }
1832
+ let output = this._formatOutput(result);
1833
+ if (consecutiveFailures >= this.codeMaxRetries) {
1834
+ output += `
1835
+
1836
+ [RETRY LIMIT REACHED] You have failed ${this.codeMaxRetries} consecutive attempts. STOP trying to execute code. Instead, respond with: 1) What you were trying to do, 2) The errors you encountered, 3) Questions for the user about how to resolve it.`;
1837
+ }
1838
+ toolResults.push({
1839
+ type: "tool_result",
1840
+ tool_use_id: block.id,
1841
+ content: output
1842
+ });
1843
+ }
1844
+ if (this._stopped) break;
1845
+ response = await this._sendMessage(toolResults, { tools: this._tools });
1846
+ if (consecutiveFailures >= this.codeMaxRetries) break;
1847
+ }
1848
+ this._cumulativeUsage = {
1849
+ promptTokens: this.lastResponseMetadata.promptTokens,
1850
+ responseTokens: this.lastResponseMetadata.responseTokens,
1851
+ totalTokens: this.lastResponseMetadata.totalTokens,
1852
+ attempts: 1
1853
+ };
1854
+ return {
1855
+ text: this._extractText(response),
1856
+ codeExecutions,
1857
+ usage: this.getLastUsage()
1858
+ };
1859
+ }
1860
+ // ── Streaming ────────────────────────────────────────────────────────────
1861
+ /**
1862
+ * Send a message and stream the response as events.
1863
+ *
1864
+ * Event types:
1865
+ * - `text` — A chunk of the agent's text response
1866
+ * - `code` — The agent is about to execute code
1867
+ * - `output` — Code finished executing
1868
+ * - `done` — The agent finished
1869
+ *
1870
+ * @param {string} message - The user's message
1871
+ * @param {Object} [opts={}]
1872
+ * @yields {CodeAgentStreamEvent}
1873
+ */
1874
+ async *stream(message, opts = {}) {
1875
+ if (!this._initialized) await this.init();
1876
+ this._stopped = false;
1877
+ const codeExecutions = [];
1878
+ let fullText = "";
1879
+ let consecutiveFailures = 0;
1880
+ let stream = await this._streamMessage(message, { tools: this._tools });
1881
+ for (let round = 0; round < this.maxRounds; round++) {
1882
+ if (this._stopped) break;
1883
+ const finalMessage = await stream.finalMessage();
1884
+ const toolUseBlocks = [];
1885
+ for (const block of finalMessage.content) {
1886
+ if (block.type === "text") {
1887
+ fullText += block.text;
1888
+ yield { type: "text", text: block.text };
1889
+ } else if (block.type === "tool_use") {
1890
+ toolUseBlocks.push(block);
1891
+ }
1892
+ }
1893
+ this.history.push({ role: "assistant", content: finalMessage.content });
1894
+ this._captureMetadata(finalMessage);
1895
+ if (finalMessage.stop_reason !== "tool_use" || toolUseBlocks.length === 0) {
1896
+ yield { type: "done", fullText, codeExecutions, usage: this.getLastUsage() };
1897
+ return;
1898
+ }
1899
+ const toolResults = [];
1900
+ for (const block of toolUseBlocks) {
1901
+ if (this._stopped) break;
1902
+ const code = block.input?.code || "";
1903
+ const purpose = block.input?.purpose;
1904
+ yield { type: "code", code };
1905
+ const result = await this._executeCode(code, purpose);
1906
+ codeExecutions.push({
1907
+ code,
1908
+ purpose: this._slugify(purpose),
1909
+ output: result.stdout,
1910
+ stderr: result.stderr,
1911
+ exitCode: result.exitCode
1912
+ });
1913
+ yield {
1914
+ type: "output",
1915
+ code,
1916
+ stdout: result.stdout,
1917
+ stderr: result.stderr,
1918
+ exitCode: result.exitCode
1919
+ };
1920
+ if (result.exitCode !== 0 && !result.denied) {
1921
+ consecutiveFailures++;
1922
+ } else {
1923
+ consecutiveFailures = 0;
1924
+ }
1925
+ let output = this._formatOutput(result);
1926
+ if (consecutiveFailures >= this.codeMaxRetries) {
1927
+ output += `
1928
+
1929
+ [RETRY LIMIT REACHED] You have failed ${this.codeMaxRetries} consecutive attempts. STOP trying to execute code. Instead, respond with: 1) What you were trying to do, 2) The errors you encountered, 3) Questions for the user about how to resolve it.`;
1930
+ }
1931
+ toolResults.push({
1932
+ type: "tool_result",
1933
+ tool_use_id: block.id,
1934
+ content: output
1935
+ });
1936
+ }
1937
+ if (this._stopped) break;
1938
+ stream = await this._streamMessage(toolResults, { tools: this._tools });
1939
+ if (consecutiveFailures >= this.codeMaxRetries) break;
1940
+ }
1941
+ let warning = "Max tool rounds reached";
1942
+ if (this._stopped) warning = "Agent was stopped";
1943
+ else if (consecutiveFailures >= this.codeMaxRetries) warning = "Retry limit reached";
1944
+ yield { type: "done", fullText, codeExecutions, usage: this.getLastUsage(), warning };
1945
+ }
1946
+ // ── Dump ─────────────────────────────────────────────────────────────────
1947
+ /**
1948
+ * Returns all code scripts the agent has written.
1949
+ * @returns {Array<{fileName: string, script: string}>}
1950
+ */
1951
+ dump() {
1952
+ return this._allExecutions.map((exec, i) => ({
1953
+ fileName: exec.purpose ? `agent-${exec.purpose}.mjs` : `script-${i + 1}.mjs`,
1954
+ purpose: exec.purpose || null,
1955
+ script: exec.code,
1956
+ filePath: exec.filePath || null
1957
+ }));
1958
+ }
1959
+ // ── Stop ─────────────────────────────────────────────────────────────────
1960
+ /**
1961
+ * Stop the agent. Kills any running child process.
1962
+ */
1963
+ stop() {
1964
+ this._stopped = true;
1965
+ if (this._activeProcess) {
1966
+ try {
1967
+ this._activeProcess.kill("SIGTERM");
1968
+ } catch {
1969
+ }
1970
+ }
1971
+ logger_default.info("CodeAgent stopped");
1972
+ }
1973
+ };
1974
+ var code_agent_default = CodeAgent;
1975
+
1976
+ // rag-agent.js
1977
+ var import_node_path2 = require("node:path");
1978
+ var import_promises3 = require("node:fs/promises");
1979
+ var MIME_TYPES = {
1980
+ // Text (read as UTF-8, injected as text)
1981
+ ".txt": "text/plain",
1982
+ ".md": "text/plain",
1983
+ ".csv": "text/csv",
1984
+ ".html": "text/html",
1985
+ ".htm": "text/html",
1986
+ ".xml": "text/xml",
1987
+ ".json": "application/json",
1988
+ ".js": "text/javascript",
1989
+ ".mjs": "text/javascript",
1990
+ ".ts": "text/plain",
1991
+ ".css": "text/css",
1992
+ ".yaml": "text/plain",
1993
+ ".yml": "text/plain",
1994
+ ".py": "text/x-python",
1995
+ ".rb": "text/plain",
1996
+ ".sh": "text/plain",
1997
+ // Images (base64 encoded for Claude vision)
1998
+ ".png": "image/png",
1999
+ ".jpg": "image/jpeg",
2000
+ ".jpeg": "image/jpeg",
2001
+ ".gif": "image/gif",
2002
+ ".webp": "image/webp",
2003
+ // Documents (base64 encoded)
2004
+ ".pdf": "application/pdf"
2005
+ };
2006
+ var DEFAULT_SYSTEM_PROMPT = "You are a helpful AI assistant. Answer questions based on the provided documents and data. When referencing information, mention which document or data source it comes from.";
2007
+ var RagAgent = class extends base_default {
2008
+ /**
2009
+ * @param {RagAgentOptions} [options={}]
2010
+ */
2011
+ constructor(options = {}) {
2012
+ if (options.systemPrompt === void 0) {
2013
+ options = { ...options, systemPrompt: DEFAULT_SYSTEM_PROMPT };
2014
+ }
2015
+ super(options);
2016
+ this.localFiles = options.localFiles || [];
2017
+ this.localData = options.localData || [];
2018
+ this.mediaFiles = options.mediaFiles || [];
2019
+ this.enableCitations = options.enableCitations ?? false;
2020
+ this._localFileContents = [];
2021
+ this._mediaContentBlocks = [];
2022
+ const total = this.localFiles.length + this.localData.length + this.mediaFiles.length;
2023
+ logger_default.debug(`RagAgent created with ${total} context sources`);
2024
+ }
2025
+ // ── Initialization ───────────────────────────────────────────────────────
2026
+ /**
2027
+ * Reads local files, encodes media, and seeds all context into conversation.
2028
+ * @param {boolean} [force=false]
2029
+ * @returns {Promise<void>}
2030
+ */
2031
+ async init(force = false) {
2032
+ if (this._initialized && !force) return;
2033
+ await this._ensureClient();
2034
+ this._localFileContents = [];
2035
+ for (const filePath of this.localFiles) {
2036
+ const resolvedPath = (0, import_node_path2.resolve)(filePath);
2037
+ logger_default.debug(`Reading local file: ${resolvedPath}`);
2038
+ const content = await (0, import_promises3.readFile)(resolvedPath, "utf-8");
2039
+ this._localFileContents.push({
2040
+ name: (0, import_node_path2.basename)(resolvedPath),
2041
+ content,
2042
+ path: resolvedPath
2043
+ });
2044
+ logger_default.debug(`Local file read: ${(0, import_node_path2.basename)(resolvedPath)} (${content.length} chars)`);
2045
+ }
2046
+ this._mediaContentBlocks = [];
2047
+ for (const filePath of this.mediaFiles) {
2048
+ const resolvedPath = (0, import_node_path2.resolve)(filePath);
2049
+ const ext = (0, import_node_path2.extname)(resolvedPath).toLowerCase();
2050
+ const mimeType = MIME_TYPES[ext] || "application/octet-stream";
2051
+ logger_default.debug(`Encoding media file: ${resolvedPath} (${mimeType})`);
2052
+ const buffer = await (0, import_promises3.readFile)(resolvedPath);
2053
+ const base64 = buffer.toString("base64");
2054
+ if (mimeType.startsWith("image/")) {
2055
+ this._mediaContentBlocks.push({
2056
+ type: "image",
2057
+ source: { type: "base64", media_type: mimeType, data: base64 }
2058
+ });
2059
+ } else if (mimeType === "application/pdf") {
2060
+ this._mediaContentBlocks.push({
2061
+ type: "document",
2062
+ source: { type: "base64", media_type: mimeType, data: base64 }
2063
+ });
2064
+ }
2065
+ logger_default.debug(`Media file encoded: ${(0, import_node_path2.basename)(resolvedPath)}`);
2066
+ }
2067
+ const contentParts = [];
2068
+ for (let i = 0; i < this._mediaContentBlocks.length; i++) {
2069
+ const block = this._mediaContentBlocks[i];
2070
+ if (this.enableCitations && block.type === "document") {
2071
+ contentParts.push({
2072
+ ...block,
2073
+ title: (0, import_node_path2.basename)(this.mediaFiles[i]),
2074
+ citations: { enabled: true }
2075
+ });
2076
+ } else {
2077
+ contentParts.push(block);
2078
+ }
2079
+ }
2080
+ for (const lf of this._localFileContents) {
2081
+ if (this.enableCitations) {
2082
+ contentParts.push({
2083
+ type: "document",
2084
+ source: { type: "text", media_type: "text/plain", data: lf.content },
2085
+ title: lf.name,
2086
+ citations: { enabled: true }
2087
+ });
2088
+ } else {
2089
+ contentParts.push({ type: "text", text: `--- File: ${lf.name} ---
2090
+ ${lf.content}` });
2091
+ }
2092
+ }
2093
+ for (const ld of this.localData) {
2094
+ const serialized = typeof ld.data === "string" ? ld.data : JSON.stringify(ld.data, null, 2);
2095
+ if (this.enableCitations) {
2096
+ contentParts.push({
2097
+ type: "document",
2098
+ source: { type: "text", media_type: "text/plain", data: serialized },
2099
+ title: ld.name,
2100
+ citations: { enabled: true }
2101
+ });
2102
+ } else {
2103
+ contentParts.push({ type: "text", text: `--- Data: ${ld.name} ---
2104
+ ${serialized}` });
2105
+ }
2106
+ }
2107
+ if (contentParts.length > 0) {
2108
+ contentParts.push({ type: "text", text: "Here are the documents and data to analyze." });
2109
+ this.history = [
2110
+ { role: "user", content: contentParts },
2111
+ { role: "assistant", content: [{ type: "text", text: "I have reviewed all the provided documents and data. I am ready to answer your questions about them." }] }
2112
+ ];
2113
+ }
2114
+ this._initialized = true;
2115
+ logger_default.debug(`RagAgent initialized with ${this._localFileContents.length} local files, ${this.localData.length} data entries, ${this._mediaContentBlocks.length} media files`);
2116
+ }
2117
+ // ── Non-Streaming Chat ───────────────────────────────────────────────────
2118
+ /**
2119
+ * Send a message and get a response grounded in the loaded context.
2120
+ *
2121
+ * @param {string} message - The user's question
2122
+ * @param {Object} [opts={}]
2123
+ * @returns {Promise<RagResponse>}
2124
+ */
2125
+ async chat(message, opts = {}) {
2126
+ if (!this._initialized) await this.init();
2127
+ const response = await this._sendMessage(message, opts);
2128
+ this._cumulativeUsage = {
2129
+ promptTokens: this.lastResponseMetadata.promptTokens,
2130
+ responseTokens: this.lastResponseMetadata.responseTokens,
2131
+ totalTokens: this.lastResponseMetadata.totalTokens,
2132
+ attempts: 1
2133
+ };
2134
+ const result = {
2135
+ text: this._extractText(response),
2136
+ usage: this.getLastUsage()
2137
+ };
2138
+ if (this.enableCitations) {
2139
+ result.citations = this._extractCitations(response);
2140
+ }
2141
+ return result;
2142
+ }
2143
+ // ── Citation Extraction ─────────────────────────────────────────────────
2144
+ /**
2145
+ * Extracts citation data from a Claude response when citations are enabled.
2146
+ * Claude returns citations as content blocks with type 'cite' interspersed
2147
+ * with text blocks in the response.
2148
+ *
2149
+ * @param {Object} response - The API response
2150
+ * @returns {Array<Object>} Array of citation objects
2151
+ * @private
2152
+ */
2153
+ _extractCitations(response) {
2154
+ if (!response?.content) return [];
2155
+ const citations = [];
2156
+ for (const block of response.content) {
2157
+ if (block.type === "text" && Array.isArray(block.citations)) {
2158
+ for (const cite of block.citations) {
2159
+ citations.push(cite);
2160
+ }
2161
+ }
2162
+ }
2163
+ return citations;
2164
+ }
2165
+ // ── Streaming ────────────────────────────────────────────────────────────
2166
+ /**
2167
+ * Send a message and stream the response as events.
2168
+ *
2169
+ * @param {string} message - The user's question
2170
+ * @param {Object} [opts={}]
2171
+ * @yields {RagStreamEvent}
2172
+ */
2173
+ async *stream(message, opts = {}) {
2174
+ if (!this._initialized) await this.init();
2175
+ let fullText = "";
2176
+ const stream = await this._streamMessage(message, opts);
2177
+ const finalMessage = await stream.finalMessage();
2178
+ for (const block of finalMessage.content) {
2179
+ if (block.type === "text") {
2180
+ fullText += block.text;
2181
+ yield { type: "text", text: block.text };
2182
+ }
2183
+ }
2184
+ this.history.push({ role: "assistant", content: finalMessage.content });
2185
+ this._captureMetadata(finalMessage);
2186
+ yield {
2187
+ type: "done",
2188
+ fullText,
2189
+ usage: this.getLastUsage()
2190
+ };
2191
+ }
2192
+ // ── Context Management ──────────────────────────────────────────────────
2193
+ /**
2194
+ * Add local text files (read from disk). Triggers reinitialize.
2195
+ * @param {string[]} paths
2196
+ * @returns {Promise<void>}
2197
+ */
2198
+ async addLocalFiles(paths) {
2199
+ this.localFiles.push(...paths);
2200
+ await this.init(true);
2201
+ }
2202
+ /**
2203
+ * Add in-memory data entries. Triggers reinitialize.
2204
+ * @param {LocalDataEntry[]} entries
2205
+ * @returns {Promise<void>}
2206
+ */
2207
+ async addLocalData(entries) {
2208
+ this.localData.push(...entries);
2209
+ await this.init(true);
2210
+ }
2211
+ /**
2212
+ * Add media files (images, PDFs). Triggers reinitialize.
2213
+ * @param {string[]} paths
2214
+ * @returns {Promise<void>}
2215
+ */
2216
+ async addMediaFiles(paths) {
2217
+ this.mediaFiles.push(...paths);
2218
+ await this.init(true);
2219
+ }
2220
+ /**
2221
+ * Returns metadata about all context sources.
2222
+ * @returns {{ localFiles: Array<Object>, localData: Array<Object>, mediaFiles: Array<Object> }}
2223
+ */
2224
+ getContext() {
2225
+ return {
2226
+ localFiles: this._localFileContents.map((lf) => ({
2227
+ name: lf.name,
2228
+ path: lf.path,
2229
+ size: lf.content.length
2230
+ })),
2231
+ localData: this.localData.map((ld) => ({
2232
+ name: ld.name,
2233
+ type: typeof ld.data === "object" && ld.data !== null ? Array.isArray(ld.data) ? "array" : "object" : typeof ld.data
2234
+ })),
2235
+ mediaFiles: this.mediaFiles.map((f) => ({
2236
+ path: (0, import_node_path2.resolve)(f),
2237
+ name: (0, import_node_path2.basename)(f),
2238
+ ext: (0, import_node_path2.extname)(f).toLowerCase()
2239
+ }))
2240
+ };
2241
+ }
2242
+ };
2243
+ var rag_agent_default = RagAgent;
2244
+
2245
+ // agent-query.js
2246
+ var AgentQuery = class {
2247
+ /**
2248
+ * @param {AgentQueryOptions} [options={}]
2249
+ */
2250
+ constructor(options = {}) {
2251
+ this.model = options.model || "claude-sonnet-4-6";
2252
+ this.allowedTools = options.allowedTools || void 0;
2253
+ this.disallowedTools = options.disallowedTools || void 0;
2254
+ this.cwd = options.cwd || process.cwd();
2255
+ this.maxTurns = options.maxTurns || void 0;
2256
+ this.maxBudgetUsd = options.maxBudgetUsd || void 0;
2257
+ this.systemPrompt = options.systemPrompt || void 0;
2258
+ this.permissionMode = options.permissionMode || void 0;
2259
+ this.mcpServers = options.mcpServers || void 0;
2260
+ this.hooks = options.hooks || void 0;
2261
+ this._lastSessionId = null;
2262
+ this._queryFn = null;
2263
+ logger_default.debug(`AgentQuery created with model: ${this.model}`);
2264
+ }
2265
+ /**
2266
+ * Lazily imports the query function from claude-agent-sdk.
2267
+ * @private
2268
+ */
2269
+ async _getQueryFn() {
2270
+ if (this._queryFn) return this._queryFn;
2271
+ try {
2272
+ const sdk = await import(
2273
+ /** @type {any} */
2274
+ "@anthropic-ai/claude-agent-sdk"
2275
+ );
2276
+ this._queryFn = sdk.query;
2277
+ return this._queryFn;
2278
+ } catch (e) {
2279
+ throw new Error(
2280
+ `Failed to import @anthropic-ai/claude-agent-sdk. Install it with: npm install @anthropic-ai/claude-agent-sdk
2281
+ Error: ${e.message}`
2282
+ );
2283
+ }
2284
+ }
2285
+ /**
2286
+ * Run an autonomous agent query. Yields messages as they arrive.
2287
+ *
2288
+ * @param {string} prompt - The task for the agent
2289
+ * @param {AgentQueryRunOptions} [opts={}] - Per-run overrides
2290
+ * @yields {Object} Messages from the agent (system, assistant, tool_progress, result)
2291
+ */
2292
+ async *run(prompt, opts = {}) {
2293
+ const queryFn = await this._getQueryFn();
2294
+ const options = {
2295
+ model: opts.model || this.model,
2296
+ cwd: opts.cwd || this.cwd,
2297
+ ...opts.allowedTools || this.allowedTools ? { allowedTools: opts.allowedTools || this.allowedTools } : {},
2298
+ ...opts.disallowedTools || this.disallowedTools ? { disallowedTools: opts.disallowedTools || this.disallowedTools } : {},
2299
+ ...opts.maxTurns || this.maxTurns ? { maxTurns: opts.maxTurns || this.maxTurns } : {},
2300
+ ...opts.maxBudgetUsd || this.maxBudgetUsd ? { maxBudgetUsd: opts.maxBudgetUsd || this.maxBudgetUsd } : {},
2301
+ ...opts.systemPrompt || this.systemPrompt ? { systemPrompt: opts.systemPrompt || this.systemPrompt } : {},
2302
+ ...opts.permissionMode || this.permissionMode ? { permissionMode: opts.permissionMode || this.permissionMode } : {},
2303
+ ...opts.mcpServers || this.mcpServers ? { mcpServers: opts.mcpServers || this.mcpServers } : {},
2304
+ ...opts.hooks || this.hooks ? { hooks: opts.hooks || this.hooks } : {},
2305
+ ...opts.sessionId ? { resume: opts.sessionId } : {}
2306
+ };
2307
+ for await (const message of queryFn({ prompt, options })) {
2308
+ this._lastSessionId = message.session_id || message.sessionId || this._lastSessionId;
2309
+ yield message;
2310
+ }
2311
+ }
2312
+ /**
2313
+ * Resume a previous agent session with a new prompt.
2314
+ *
2315
+ * @param {string} sessionId - The session ID to resume
2316
+ * @param {string} prompt - The follow-up prompt
2317
+ * @param {AgentQueryRunOptions} [opts={}]
2318
+ * @yields {Object} Messages from the agent
2319
+ */
2320
+ async *resume(sessionId, prompt, opts = {}) {
2321
+ yield* this.run(prompt, { ...opts, sessionId });
2322
+ }
2323
+ /**
2324
+ * The session ID from the last run.
2325
+ * @returns {string|null}
2326
+ */
2327
+ get lastSessionId() {
2328
+ return this._lastSessionId;
2329
+ }
2330
+ };
2331
+ var agent_query_default = AgentQuery;
2332
+
2333
+ // index.js
2334
+ var index_default = { Transformer: transformer_default, Chat: chat_default, Message: message_default, ToolAgent: tool_agent_default, CodeAgent: code_agent_default, RagAgent: rag_agent_default, AgentQuery: agent_query_default };
2335
+ // Annotate the CommonJS export names for ESM import in node:
2336
+ 0 && (module.exports = {
2337
+ AgentQuery,
2338
+ BaseClaude,
2339
+ Chat,
2340
+ CodeAgent,
2341
+ Message,
2342
+ RagAgent,
2343
+ ToolAgent,
2344
+ Transformer,
2345
+ attemptJSONRecovery,
2346
+ extractJSON,
2347
+ log
2348
+ });