llmist 0.1.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.
@@ -0,0 +1,4450 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/core/model-shortcuts.ts
23
+ function isKnownModelPattern(model) {
24
+ const normalized = model.toLowerCase();
25
+ if (MODEL_ALIASES[normalized]) {
26
+ return true;
27
+ }
28
+ return KNOWN_MODEL_PATTERNS.some((pattern) => pattern.test(model));
29
+ }
30
+ function resolveModel(model, options = {}) {
31
+ if (model.includes(":")) {
32
+ return model;
33
+ }
34
+ const normalized = model.toLowerCase();
35
+ if (MODEL_ALIASES[normalized]) {
36
+ return MODEL_ALIASES[normalized];
37
+ }
38
+ const modelLower = model.toLowerCase();
39
+ if (modelLower.startsWith("gpt")) {
40
+ return `openai:${model}`;
41
+ }
42
+ if (modelLower.startsWith("claude")) {
43
+ return `anthropic:${model}`;
44
+ }
45
+ if (modelLower.startsWith("gemini")) {
46
+ return `gemini:${model}`;
47
+ }
48
+ if (modelLower.match(/^o\d/)) {
49
+ return `openai:${model}`;
50
+ }
51
+ if (!isKnownModelPattern(model)) {
52
+ if (options.strict) {
53
+ throw new Error(
54
+ `Unknown model '${model}'. Did you mean one of: gpt4, sonnet, haiku, flash? Use explicit provider prefix like 'openai:${model}' to bypass this check.`
55
+ );
56
+ }
57
+ if (!options.silent) {
58
+ console.warn(
59
+ `\u26A0\uFE0F Unknown model '${model}', falling back to 'openai:${model}'. This might be a typo. Did you mean: gpt4, gpt5, gpt5-nano, sonnet, haiku, flash? Use { strict: true } to error on unknown models, or { silent: true } to suppress this warning.`
60
+ );
61
+ }
62
+ }
63
+ return `openai:${model}`;
64
+ }
65
+ function hasProviderPrefix(model) {
66
+ return model.includes(":");
67
+ }
68
+ function getProvider(model) {
69
+ const separatorIndex = model.indexOf(":");
70
+ if (separatorIndex === -1) {
71
+ return void 0;
72
+ }
73
+ return model.slice(0, separatorIndex);
74
+ }
75
+ function getModelId(model) {
76
+ const separatorIndex = model.indexOf(":");
77
+ if (separatorIndex === -1) {
78
+ return model;
79
+ }
80
+ return model.slice(separatorIndex + 1);
81
+ }
82
+ var MODEL_ALIASES, KNOWN_MODEL_PATTERNS;
83
+ var init_model_shortcuts = __esm({
84
+ "src/core/model-shortcuts.ts"() {
85
+ "use strict";
86
+ MODEL_ALIASES = {
87
+ // OpenAI aliases
88
+ gpt4: "openai:gpt-4o",
89
+ gpt4o: "openai:gpt-4o",
90
+ gpt5: "openai:gpt-5",
91
+ "gpt5-mini": "openai:gpt-5-mini",
92
+ "gpt5-nano": "openai:gpt-5-nano",
93
+ // Anthropic aliases
94
+ sonnet: "anthropic:claude-3-5-sonnet-latest",
95
+ "claude-sonnet": "anthropic:claude-3-5-sonnet-latest",
96
+ haiku: "anthropic:claude-3-5-haiku-latest",
97
+ "claude-haiku": "anthropic:claude-3-5-haiku-latest",
98
+ opus: "anthropic:claude-3-opus-latest",
99
+ "claude-opus": "anthropic:claude-3-opus-latest",
100
+ // Gemini aliases
101
+ flash: "gemini:gemini-2.0-flash",
102
+ "gemini-flash": "gemini:gemini-2.0-flash",
103
+ "gemini-pro": "gemini:gemini-2.0-pro",
104
+ pro: "gemini:gemini-2.0-pro"
105
+ };
106
+ KNOWN_MODEL_PATTERNS = [
107
+ /^gpt-?\d/i,
108
+ // gpt-4, gpt-3.5, gpt4, etc.
109
+ /^claude-?\d/i,
110
+ // claude-3, claude-2, etc.
111
+ /^gemini-?(\d|pro|flash)/i,
112
+ // gemini-2.0, gemini-pro, gemini-flash, etc.
113
+ /^o\d/i
114
+ // OpenAI o1, o3, etc.
115
+ ];
116
+ }
117
+ });
118
+
119
+ // src/gadgets/schema-validator.ts
120
+ import * as z from "zod";
121
+ function validateGadgetSchema(schema, gadgetName) {
122
+ let jsonSchema;
123
+ try {
124
+ jsonSchema = z.toJSONSchema(schema, { target: "draft-7" });
125
+ } catch (error) {
126
+ const errorMessage = error instanceof Error ? error.message : String(error);
127
+ throw new Error(
128
+ `Gadget "${gadgetName}" has a schema that cannot be serialized to JSON Schema.
129
+ This usually happens with unsupported patterns like:
130
+ - z.record() - use z.object({}).passthrough() instead
131
+ - Complex transforms or custom refinements
132
+ - Circular references
133
+
134
+ Original error: ${errorMessage}
135
+
136
+ Only use schema patterns that Zod v4's native toJSONSchema() supports.`
137
+ );
138
+ }
139
+ const issues = findUnknownTypes(jsonSchema);
140
+ if (issues.length > 0) {
141
+ const fieldList = issues.join(", ");
142
+ throw new Error(
143
+ `Gadget "${gadgetName}" uses z.unknown() which produces incomplete schemas.
144
+ Problematic fields: ${fieldList}
145
+
146
+ z.unknown() doesn't generate type information in JSON Schema, making it unclear
147
+ to the LLM what data structure to provide.
148
+
149
+ Suggestions:
150
+ - Use z.object({}).passthrough() for flexible objects
151
+ - Use z.record(z.string()) for key-value objects with string values
152
+ - Define specific structure if possible
153
+
154
+ Example fixes:
155
+ // \u274C Bad
156
+ content: z.unknown()
157
+
158
+ // \u2705 Good
159
+ content: z.object({}).passthrough() // for flexible objects
160
+ content: z.record(z.string()) // for key-value objects
161
+ content: z.array(z.string()) // for arrays of strings
162
+ `
163
+ );
164
+ }
165
+ }
166
+ function findUnknownTypes(schema, path = []) {
167
+ const issues = [];
168
+ if (!schema || typeof schema !== "object") {
169
+ return issues;
170
+ }
171
+ if (schema.definitions) {
172
+ for (const defSchema of Object.values(schema.definitions)) {
173
+ issues.push(...findUnknownTypes(defSchema, []));
174
+ }
175
+ }
176
+ if (schema.properties) {
177
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
178
+ const propPath = [...path, propName];
179
+ if (hasNoType(propSchema)) {
180
+ issues.push(propPath.join(".") || propName);
181
+ }
182
+ issues.push(...findUnknownTypes(propSchema, propPath));
183
+ }
184
+ }
185
+ if (schema.items) {
186
+ const itemPath = [...path, "[]"];
187
+ if (hasNoType(schema.items)) {
188
+ issues.push(itemPath.join("."));
189
+ }
190
+ issues.push(...findUnknownTypes(schema.items, itemPath));
191
+ }
192
+ if (schema.anyOf) {
193
+ schema.anyOf.forEach((subSchema, index) => {
194
+ issues.push(...findUnknownTypes(subSchema, [...path, `anyOf[${index}]`]));
195
+ });
196
+ }
197
+ if (schema.oneOf) {
198
+ schema.oneOf.forEach((subSchema, index) => {
199
+ issues.push(...findUnknownTypes(subSchema, [...path, `oneOf[${index}]`]));
200
+ });
201
+ }
202
+ if (schema.allOf) {
203
+ schema.allOf.forEach((subSchema, index) => {
204
+ issues.push(...findUnknownTypes(subSchema, [...path, `allOf[${index}]`]));
205
+ });
206
+ }
207
+ return issues;
208
+ }
209
+ function hasNoType(prop) {
210
+ if (!prop || typeof prop !== "object") {
211
+ return false;
212
+ }
213
+ const hasType = prop.type !== void 0;
214
+ const hasRef = prop.$ref !== void 0;
215
+ const hasUnion = prop.anyOf !== void 0 || prop.oneOf !== void 0 || prop.allOf !== void 0;
216
+ if (hasType || hasRef || hasUnion) {
217
+ return false;
218
+ }
219
+ const keys = Object.keys(prop);
220
+ const metadataKeys = ["description", "title", "default", "examples"];
221
+ const hasOnlyMetadata = keys.every((key) => metadataKeys.includes(key));
222
+ return hasOnlyMetadata || keys.length === 0;
223
+ }
224
+ var init_schema_validator = __esm({
225
+ "src/gadgets/schema-validator.ts"() {
226
+ "use strict";
227
+ }
228
+ });
229
+
230
+ // src/gadgets/registry.ts
231
+ var GadgetRegistry;
232
+ var init_registry = __esm({
233
+ "src/gadgets/registry.ts"() {
234
+ "use strict";
235
+ init_schema_validator();
236
+ GadgetRegistry = class _GadgetRegistry {
237
+ gadgets = /* @__PURE__ */ new Map();
238
+ /**
239
+ * Creates a registry from an array of gadget classes or instances,
240
+ * or an object mapping names to gadgets.
241
+ *
242
+ * @param gadgets - Array of gadgets/classes or object with custom names
243
+ * @returns New GadgetRegistry with all gadgets registered
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * // From array of classes
248
+ * const registry = GadgetRegistry.from([Calculator, Weather]);
249
+ *
250
+ * // From array of instances
251
+ * const registry = GadgetRegistry.from([new Calculator(), new Weather()]);
252
+ *
253
+ * // From object with custom names
254
+ * const registry = GadgetRegistry.from({
255
+ * calc: Calculator,
256
+ * weather: new Weather({ apiKey: "..." })
257
+ * });
258
+ * ```
259
+ */
260
+ static from(gadgets) {
261
+ const registry = new _GadgetRegistry();
262
+ if (Array.isArray(gadgets)) {
263
+ registry.registerMany(gadgets);
264
+ } else {
265
+ for (const [name, gadget] of Object.entries(gadgets)) {
266
+ const instance = typeof gadget === "function" ? new gadget() : gadget;
267
+ registry.register(name, instance);
268
+ }
269
+ }
270
+ return registry;
271
+ }
272
+ /**
273
+ * Registers multiple gadgets at once from an array.
274
+ *
275
+ * @param gadgets - Array of gadget instances or classes
276
+ * @returns This registry for chaining
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * registry.registerMany([Calculator, Weather, Email]);
281
+ * registry.registerMany([new Calculator(), new Weather()]);
282
+ * ```
283
+ */
284
+ registerMany(gadgets) {
285
+ for (const gadget of gadgets) {
286
+ const instance = typeof gadget === "function" ? new gadget() : gadget;
287
+ this.registerByClass(instance);
288
+ }
289
+ return this;
290
+ }
291
+ // Register a gadget by name
292
+ register(name, gadget) {
293
+ const normalizedName = name.toLowerCase();
294
+ if (this.gadgets.has(normalizedName)) {
295
+ throw new Error(`Gadget '${name}' is already registered`);
296
+ }
297
+ if (gadget.parameterSchema) {
298
+ validateGadgetSchema(gadget.parameterSchema, name);
299
+ }
300
+ this.gadgets.set(normalizedName, gadget);
301
+ }
302
+ // Register a gadget using its name property or class name
303
+ registerByClass(gadget) {
304
+ const name = gadget.name ?? gadget.constructor.name;
305
+ this.register(name, gadget);
306
+ }
307
+ // Get gadget by name (case-insensitive)
308
+ get(name) {
309
+ return this.gadgets.get(name.toLowerCase());
310
+ }
311
+ // Check if gadget exists (case-insensitive)
312
+ has(name) {
313
+ return this.gadgets.has(name.toLowerCase());
314
+ }
315
+ // Get all registered gadget names
316
+ getNames() {
317
+ return Array.from(this.gadgets.keys());
318
+ }
319
+ // Get all gadgets for instruction generation
320
+ getAll() {
321
+ return Array.from(this.gadgets.values());
322
+ }
323
+ // Unregister gadget (useful for testing, case-insensitive)
324
+ unregister(name) {
325
+ return this.gadgets.delete(name.toLowerCase());
326
+ }
327
+ // Clear all gadgets (useful for testing)
328
+ clear() {
329
+ this.gadgets.clear();
330
+ }
331
+ };
332
+ }
333
+ });
334
+
335
+ // src/core/prompt-config.ts
336
+ function resolvePromptTemplate(template, defaultValue, context) {
337
+ const resolved = template ?? defaultValue;
338
+ return typeof resolved === "function" ? resolved(context) : resolved;
339
+ }
340
+ function resolveRulesTemplate(rules, context) {
341
+ const resolved = rules ?? DEFAULT_PROMPTS.rules;
342
+ if (Array.isArray(resolved)) {
343
+ return resolved;
344
+ }
345
+ if (typeof resolved === "function") {
346
+ const result = resolved(context);
347
+ return Array.isArray(result) ? result : [result];
348
+ }
349
+ return [resolved];
350
+ }
351
+ var DEFAULT_PROMPTS;
352
+ var init_prompt_config = __esm({
353
+ "src/core/prompt-config.ts"() {
354
+ "use strict";
355
+ DEFAULT_PROMPTS = {
356
+ mainInstruction: [
357
+ "\u26A0\uFE0F CRITICAL: RESPOND ONLY WITH GADGET INVOCATIONS",
358
+ "DO NOT use function calling or tool calling",
359
+ "You must output the exact text markers shown below in plain text.",
360
+ "EACH MARKER MUST START WITH A NEWLINE."
361
+ ].join("\n"),
362
+ criticalUsage: "INVOKE gadgets using the markers - do not describe what you want to do.",
363
+ formatDescriptionYaml: "Parameters in YAML format (one per line)",
364
+ formatDescriptionJson: "Parameters in JSON format (valid JSON object)",
365
+ rules: () => [
366
+ "Output ONLY plain text with the exact markers - never use function/tool calling",
367
+ "You can invoke multiple gadgets in a single response",
368
+ "For dependent gadgets, invoke the first one and wait for the result"
369
+ ],
370
+ schemaLabelJson: "\n\nInput Schema (JSON):",
371
+ schemaLabelYaml: "\n\nInput Schema (YAML):",
372
+ customExamples: null
373
+ };
374
+ }
375
+ });
376
+
377
+ // src/core/constants.ts
378
+ var GADGET_START_PREFIX, GADGET_END_PREFIX;
379
+ var init_constants = __esm({
380
+ "src/core/constants.ts"() {
381
+ "use strict";
382
+ GADGET_START_PREFIX = "!!!GADGET_START:";
383
+ GADGET_END_PREFIX = "!!!GADGET_END";
384
+ }
385
+ });
386
+
387
+ // src/core/messages.ts
388
+ var LLMMessageBuilder;
389
+ var init_messages = __esm({
390
+ "src/core/messages.ts"() {
391
+ "use strict";
392
+ init_constants();
393
+ init_prompt_config();
394
+ LLMMessageBuilder = class {
395
+ messages = [];
396
+ startPrefix = GADGET_START_PREFIX;
397
+ endPrefix = GADGET_END_PREFIX;
398
+ promptConfig;
399
+ constructor(promptConfig) {
400
+ this.promptConfig = promptConfig ?? {};
401
+ }
402
+ addSystem(content, metadata) {
403
+ this.messages.push({ role: "system", content, metadata });
404
+ return this;
405
+ }
406
+ addGadgets(gadgets, parameterFormat = "json", options) {
407
+ if (options?.startPrefix) {
408
+ this.startPrefix = options.startPrefix;
409
+ }
410
+ if (options?.endPrefix) {
411
+ this.endPrefix = options.endPrefix;
412
+ }
413
+ const context = {
414
+ parameterFormat,
415
+ startPrefix: this.startPrefix,
416
+ endPrefix: this.endPrefix,
417
+ gadgetCount: gadgets.length,
418
+ gadgetNames: gadgets.map((g) => g.name ?? g.constructor.name)
419
+ };
420
+ const parts = [];
421
+ const mainInstruction = resolvePromptTemplate(
422
+ this.promptConfig.mainInstruction,
423
+ DEFAULT_PROMPTS.mainInstruction,
424
+ context
425
+ );
426
+ parts.push(mainInstruction);
427
+ parts.push(this.buildGadgetsXmlSection(gadgets, parameterFormat));
428
+ parts.push(this.buildUsageSection(parameterFormat, context));
429
+ this.messages.push({ role: "system", content: parts.join("") });
430
+ return this;
431
+ }
432
+ buildGadgetsXmlSection(gadgets, parameterFormat) {
433
+ const parts = [];
434
+ parts.push("<GADGETS>");
435
+ for (const gadget of gadgets) {
436
+ const gadgetName = gadget.name ?? gadget.constructor.name;
437
+ const instruction = gadget.getInstruction(parameterFormat);
438
+ const schemaMarker = parameterFormat === "yaml" ? "\n\nInput Schema (YAML):" : "\n\nInput Schema (JSON):";
439
+ const schemaIndex = instruction.indexOf(schemaMarker);
440
+ const description = (schemaIndex !== -1 ? instruction.substring(0, schemaIndex) : instruction).trim();
441
+ const schema = schemaIndex !== -1 ? instruction.substring(schemaIndex + schemaMarker.length).trim() : "";
442
+ parts.push("\n <gadget>");
443
+ parts.push(`
444
+ <name>${gadgetName}</name>`);
445
+ parts.push(`
446
+ <description>${description}</description>`);
447
+ if (schema) {
448
+ parts.push(`
449
+ <schema format="${parameterFormat}">
450
+ ${schema}
451
+ </schema>`);
452
+ }
453
+ parts.push("\n </gadget>");
454
+ }
455
+ return parts.join("");
456
+ }
457
+ buildUsageSection(parameterFormat, context) {
458
+ const parts = [];
459
+ const formatDescription = parameterFormat === "yaml" ? resolvePromptTemplate(
460
+ this.promptConfig.formatDescriptionYaml,
461
+ DEFAULT_PROMPTS.formatDescriptionYaml,
462
+ context
463
+ ) : resolvePromptTemplate(
464
+ this.promptConfig.formatDescriptionJson,
465
+ DEFAULT_PROMPTS.formatDescriptionJson,
466
+ context
467
+ );
468
+ parts.push("<usage>");
469
+ const criticalUsage = resolvePromptTemplate(
470
+ this.promptConfig.criticalUsage,
471
+ DEFAULT_PROMPTS.criticalUsage,
472
+ context
473
+ );
474
+ parts.push(`
475
+ <critical>${criticalUsage}</critical>`);
476
+ parts.push("\n <format>");
477
+ parts.push(`
478
+ <step>Start marker: ${this.startPrefix}gadget_name</step>`);
479
+ parts.push(`
480
+ <step>${formatDescription}</step>`);
481
+ parts.push(`
482
+ <step>End marker: ${this.endPrefix}</step>`);
483
+ parts.push("\n </format>");
484
+ parts.push(this.buildExamplesSection(parameterFormat, context));
485
+ parts.push(this.buildRulesSection(context));
486
+ parts.push("\n</usage>");
487
+ parts.push("\n</GADGETS>\n\n");
488
+ return parts.join("");
489
+ }
490
+ buildExamplesSection(parameterFormat, context) {
491
+ if (this.promptConfig.customExamples) {
492
+ return this.promptConfig.customExamples(context);
493
+ }
494
+ const parts = [];
495
+ parts.push("\n <examples>");
496
+ const singleExample = parameterFormat === "yaml" ? `${this.startPrefix}translate
497
+ from: English
498
+ to: Polish
499
+ content: Paris is the capital of France.
500
+ ${this.endPrefix}` : `${this.startPrefix}translate
501
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France."}
502
+ ${this.endPrefix}`;
503
+ parts.push(`
504
+ <example title="Single Gadget">
505
+ ${singleExample}
506
+ </example>`);
507
+ const multipleExample = parameterFormat === "yaml" ? `${this.startPrefix}translate
508
+ from: English
509
+ to: Polish
510
+ content: Paris is the capital of France.
511
+ ${this.endPrefix}
512
+ ${this.startPrefix}analyze
513
+ type: economic_analysis
514
+ matter: "Polish Economy"
515
+ question: Polish arms exports 2025.
516
+ ${this.endPrefix}` : `${this.startPrefix}translate
517
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France."}
518
+ ${this.endPrefix}
519
+ ${this.startPrefix}analyze
520
+ {"type": "economic_analysis", "matter": "Polish Economy", "question": "Polish arms exports 2025."}
521
+ ${this.endPrefix}`;
522
+ parts.push(`
523
+ <example title="Multiple Gadgets">
524
+ ${multipleExample}
525
+ </example>`);
526
+ parts.push("\n </examples>");
527
+ return parts.join("");
528
+ }
529
+ buildRulesSection(context) {
530
+ const parts = [];
531
+ parts.push("\n <rules>");
532
+ const rules = resolveRulesTemplate(this.promptConfig.rules, context);
533
+ for (const rule of rules) {
534
+ parts.push(`
535
+ <rule>${rule}</rule>`);
536
+ }
537
+ parts.push("\n </rules>");
538
+ return parts.join("");
539
+ }
540
+ addUser(content, metadata) {
541
+ this.messages.push({ role: "user", content, metadata });
542
+ return this;
543
+ }
544
+ addAssistant(content, metadata) {
545
+ this.messages.push({ role: "assistant", content, metadata });
546
+ return this;
547
+ }
548
+ addGadgetCall(gadget, parameters, result, parameterFormat = "json") {
549
+ const paramStr = this.formatParameters(parameters, parameterFormat);
550
+ this.messages.push({
551
+ role: "assistant",
552
+ content: `${this.startPrefix}${gadget}
553
+ ${paramStr}
554
+ ${this.endPrefix}`
555
+ });
556
+ this.messages.push({
557
+ role: "user",
558
+ content: `Result: ${result}`
559
+ });
560
+ return this;
561
+ }
562
+ formatParameters(parameters, format) {
563
+ if (format === "yaml") {
564
+ return Object.entries(parameters).map(([key, value]) => {
565
+ if (typeof value === "string") {
566
+ return `${key}: ${value}`;
567
+ }
568
+ return `${key}: ${JSON.stringify(value)}`;
569
+ }).join("\n");
570
+ }
571
+ return JSON.stringify(parameters);
572
+ }
573
+ build() {
574
+ return [...this.messages];
575
+ }
576
+ };
577
+ }
578
+ });
579
+
580
+ // src/logging/logger.ts
581
+ import { createWriteStream, mkdirSync } from "node:fs";
582
+ import { dirname } from "node:path";
583
+ import { Logger } from "tslog";
584
+ function parseLogLevel(value) {
585
+ if (!value) {
586
+ return void 0;
587
+ }
588
+ const normalized = value.trim().toLowerCase();
589
+ if (normalized === "") {
590
+ return void 0;
591
+ }
592
+ const numericLevel = Number(normalized);
593
+ if (Number.isFinite(numericLevel)) {
594
+ return Math.max(0, Math.min(6, Math.floor(numericLevel)));
595
+ }
596
+ return LEVEL_NAME_TO_ID[normalized];
597
+ }
598
+ function createLogger(options = {}) {
599
+ const envMinLevel = parseLogLevel(process.env.LLMIST_LOG_LEVEL);
600
+ const envLogFile = process.env.LLMIST_LOG_FILE?.trim() ?? "";
601
+ const minLevel = options.minLevel ?? envMinLevel ?? 4;
602
+ const defaultType = options.type ?? "pretty";
603
+ const name = options.name ?? "llmist";
604
+ let logFileStream;
605
+ let finalType = defaultType;
606
+ if (envLogFile) {
607
+ try {
608
+ mkdirSync(dirname(envLogFile), { recursive: true });
609
+ logFileStream = createWriteStream(envLogFile, { flags: "a" });
610
+ finalType = "hidden";
611
+ } catch (error) {
612
+ console.error("Failed to initialize LLMIST_LOG_FILE output:", error);
613
+ }
614
+ }
615
+ const logger = new Logger({
616
+ name,
617
+ minLevel,
618
+ type: finalType,
619
+ // Optimize for production
620
+ hideLogPositionForProduction: finalType !== "pretty",
621
+ // Pretty output settings
622
+ prettyLogTemplate: finalType === "pretty" ? "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}} {{logLevelName}} [{{name}}] " : void 0
623
+ });
624
+ if (logFileStream) {
625
+ logger.attachTransport((logObj) => {
626
+ logFileStream?.write(`${JSON.stringify(logObj)}
627
+ `);
628
+ });
629
+ }
630
+ return logger;
631
+ }
632
+ var LEVEL_NAME_TO_ID, defaultLogger;
633
+ var init_logger = __esm({
634
+ "src/logging/logger.ts"() {
635
+ "use strict";
636
+ LEVEL_NAME_TO_ID = {
637
+ silly: 0,
638
+ trace: 1,
639
+ debug: 2,
640
+ info: 3,
641
+ warn: 4,
642
+ error: 5,
643
+ fatal: 6
644
+ };
645
+ defaultLogger = createLogger();
646
+ }
647
+ });
648
+
649
+ // src/agent/conversation-manager.ts
650
+ var ConversationManager;
651
+ var init_conversation_manager = __esm({
652
+ "src/agent/conversation-manager.ts"() {
653
+ "use strict";
654
+ init_messages();
655
+ ConversationManager = class {
656
+ baseMessages;
657
+ initialMessages;
658
+ historyBuilder;
659
+ parameterFormat;
660
+ constructor(baseMessages, initialMessages, parameterFormat = "json") {
661
+ this.baseMessages = baseMessages;
662
+ this.initialMessages = initialMessages;
663
+ this.parameterFormat = parameterFormat;
664
+ this.historyBuilder = new LLMMessageBuilder();
665
+ }
666
+ addUserMessage(content) {
667
+ this.historyBuilder.addUser(content);
668
+ }
669
+ addAssistantMessage(content) {
670
+ this.historyBuilder.addAssistant(content);
671
+ }
672
+ addGadgetCall(gadgetName, parameters, result) {
673
+ this.historyBuilder.addGadgetCall(gadgetName, parameters, result, this.parameterFormat);
674
+ }
675
+ getMessages() {
676
+ return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
677
+ }
678
+ };
679
+ }
680
+ });
681
+
682
+ // src/agent/event-handlers.ts
683
+ async function runWithHandlers(agentGenerator, handlers) {
684
+ for await (const event of agentGenerator) {
685
+ switch (event.type) {
686
+ case "text":
687
+ if (handlers.onText) {
688
+ await handlers.onText(event.content);
689
+ }
690
+ break;
691
+ case "gadget_call":
692
+ if (handlers.onGadgetCall) {
693
+ await handlers.onGadgetCall({
694
+ gadgetName: event.call.gadgetName,
695
+ parameters: event.call.parameters,
696
+ parametersYaml: event.call.parametersYaml
697
+ });
698
+ }
699
+ break;
700
+ case "gadget_result":
701
+ if (handlers.onGadgetResult) {
702
+ await handlers.onGadgetResult(event.result);
703
+ }
704
+ break;
705
+ case "human_input_required":
706
+ if (handlers.onHumanInputRequired) {
707
+ await handlers.onHumanInputRequired({
708
+ question: event.question,
709
+ gadgetName: event.gadgetName
710
+ });
711
+ }
712
+ break;
713
+ default:
714
+ if (handlers.onOther) {
715
+ await handlers.onOther(event);
716
+ }
717
+ break;
718
+ }
719
+ }
720
+ }
721
+ async function collectEvents(agentGenerator, collect) {
722
+ const result = {
723
+ text: [],
724
+ gadgetCalls: [],
725
+ gadgetResults: []
726
+ };
727
+ for await (const event of agentGenerator) {
728
+ switch (event.type) {
729
+ case "text":
730
+ if (collect.text) {
731
+ result.text.push(event.content);
732
+ }
733
+ break;
734
+ case "gadget_call":
735
+ if (collect.gadgetCalls && event.call.parameters) {
736
+ result.gadgetCalls.push({
737
+ gadgetName: event.call.gadgetName,
738
+ parameters: event.call.parameters
739
+ });
740
+ }
741
+ break;
742
+ case "gadget_result":
743
+ if (collect.gadgetResults) {
744
+ result.gadgetResults.push(event.result);
745
+ }
746
+ break;
747
+ }
748
+ }
749
+ return result;
750
+ }
751
+ async function collectText(agentGenerator) {
752
+ const chunks = [];
753
+ for await (const event of agentGenerator) {
754
+ if (event.type === "text") {
755
+ chunks.push(event.content);
756
+ }
757
+ }
758
+ return chunks.join("");
759
+ }
760
+ var init_event_handlers = __esm({
761
+ "src/agent/event-handlers.ts"() {
762
+ "use strict";
763
+ }
764
+ });
765
+
766
+ // src/gadgets/exceptions.ts
767
+ var BreakLoopException, HumanInputException, TimeoutException;
768
+ var init_exceptions = __esm({
769
+ "src/gadgets/exceptions.ts"() {
770
+ "use strict";
771
+ BreakLoopException = class extends Error {
772
+ constructor(message) {
773
+ super(message ?? "Agent loop terminated by gadget");
774
+ this.name = "BreakLoopException";
775
+ }
776
+ };
777
+ HumanInputException = class extends Error {
778
+ question;
779
+ constructor(question) {
780
+ super(`Human input required: ${question}`);
781
+ this.name = "HumanInputException";
782
+ this.question = question;
783
+ }
784
+ };
785
+ TimeoutException = class extends Error {
786
+ timeoutMs;
787
+ gadgetName;
788
+ constructor(gadgetName, timeoutMs) {
789
+ super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
790
+ this.name = "TimeoutException";
791
+ this.gadgetName = gadgetName;
792
+ this.timeoutMs = timeoutMs;
793
+ }
794
+ };
795
+ }
796
+ });
797
+
798
+ // src/gadgets/executor.ts
799
+ var GadgetExecutor;
800
+ var init_executor = __esm({
801
+ "src/gadgets/executor.ts"() {
802
+ "use strict";
803
+ init_logger();
804
+ init_exceptions();
805
+ GadgetExecutor = class {
806
+ constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs) {
807
+ this.registry = registry;
808
+ this.onHumanInputRequired = onHumanInputRequired;
809
+ this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
810
+ this.logger = logger ?? createLogger({ name: "llmist:executor" });
811
+ }
812
+ logger;
813
+ /**
814
+ * Creates a promise that rejects with a TimeoutException after the specified timeout.
815
+ */
816
+ createTimeoutPromise(gadgetName, timeoutMs) {
817
+ return new Promise((_, reject) => {
818
+ setTimeout(() => {
819
+ reject(new TimeoutException(gadgetName, timeoutMs));
820
+ }, timeoutMs);
821
+ });
822
+ }
823
+ // Execute a gadget call asynchronously
824
+ async execute(call) {
825
+ const startTime = Date.now();
826
+ this.logger.debug("Executing gadget", {
827
+ gadgetName: call.gadgetName,
828
+ invocationId: call.invocationId,
829
+ parameters: call.parameters
830
+ });
831
+ const rawParameters = call.parameters ?? {};
832
+ let validatedParameters = rawParameters;
833
+ try {
834
+ const gadget = this.registry.get(call.gadgetName);
835
+ if (!gadget) {
836
+ this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
837
+ return {
838
+ gadgetName: call.gadgetName,
839
+ invocationId: call.invocationId,
840
+ parameters: call.parameters ?? {},
841
+ error: `Gadget '${call.gadgetName}' not found in registry`,
842
+ executionTimeMs: Date.now() - startTime
843
+ };
844
+ }
845
+ if (call.parseError || !call.parameters) {
846
+ this.logger.error("Gadget parameter parse error", {
847
+ gadgetName: call.gadgetName,
848
+ parseError: call.parseError
849
+ });
850
+ return {
851
+ gadgetName: call.gadgetName,
852
+ invocationId: call.invocationId,
853
+ parameters: {},
854
+ error: call.parseError ?? "Failed to parse parameters",
855
+ executionTimeMs: Date.now() - startTime
856
+ };
857
+ }
858
+ if (gadget.parameterSchema) {
859
+ const validationResult = gadget.parameterSchema.safeParse(rawParameters);
860
+ if (!validationResult.success) {
861
+ const formattedIssues = validationResult.error.issues.map((issue) => {
862
+ const path = issue.path.join(".") || "root";
863
+ return `${path}: ${issue.message}`;
864
+ }).join("; ");
865
+ const validationError = `Invalid parameters: ${formattedIssues}`;
866
+ this.logger.error("Gadget parameter validation failed", {
867
+ gadgetName: call.gadgetName,
868
+ error: validationError
869
+ });
870
+ return {
871
+ gadgetName: call.gadgetName,
872
+ invocationId: call.invocationId,
873
+ parameters: rawParameters,
874
+ error: validationError,
875
+ executionTimeMs: Date.now() - startTime
876
+ };
877
+ }
878
+ validatedParameters = validationResult.data;
879
+ }
880
+ const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
881
+ let result;
882
+ if (timeoutMs && timeoutMs > 0) {
883
+ this.logger.debug("Executing gadget with timeout", {
884
+ gadgetName: call.gadgetName,
885
+ timeoutMs
886
+ });
887
+ result = await Promise.race([
888
+ Promise.resolve(gadget.execute(validatedParameters)),
889
+ this.createTimeoutPromise(call.gadgetName, timeoutMs)
890
+ ]);
891
+ } else {
892
+ result = await Promise.resolve(gadget.execute(validatedParameters));
893
+ }
894
+ const executionTimeMs = Date.now() - startTime;
895
+ this.logger.info("Gadget executed successfully", {
896
+ gadgetName: call.gadgetName,
897
+ invocationId: call.invocationId,
898
+ executionTimeMs
899
+ });
900
+ this.logger.debug("Gadget result", {
901
+ gadgetName: call.gadgetName,
902
+ invocationId: call.invocationId,
903
+ parameters: validatedParameters,
904
+ result,
905
+ executionTimeMs
906
+ });
907
+ return {
908
+ gadgetName: call.gadgetName,
909
+ invocationId: call.invocationId,
910
+ parameters: validatedParameters,
911
+ result,
912
+ executionTimeMs
913
+ };
914
+ } catch (error) {
915
+ if (error instanceof BreakLoopException) {
916
+ this.logger.info("Gadget requested loop termination", {
917
+ gadgetName: call.gadgetName,
918
+ message: error.message
919
+ });
920
+ return {
921
+ gadgetName: call.gadgetName,
922
+ invocationId: call.invocationId,
923
+ parameters: validatedParameters,
924
+ result: error.message,
925
+ breaksLoop: true,
926
+ executionTimeMs: Date.now() - startTime
927
+ };
928
+ }
929
+ if (error instanceof TimeoutException) {
930
+ this.logger.error("Gadget execution timed out", {
931
+ gadgetName: call.gadgetName,
932
+ timeoutMs: error.timeoutMs,
933
+ executionTimeMs: Date.now() - startTime
934
+ });
935
+ return {
936
+ gadgetName: call.gadgetName,
937
+ invocationId: call.invocationId,
938
+ parameters: validatedParameters,
939
+ error: error.message,
940
+ executionTimeMs: Date.now() - startTime
941
+ };
942
+ }
943
+ if (error instanceof HumanInputException) {
944
+ this.logger.info("Gadget requested human input", {
945
+ gadgetName: call.gadgetName,
946
+ question: error.question
947
+ });
948
+ if (this.onHumanInputRequired) {
949
+ try {
950
+ const answer = await this.onHumanInputRequired(error.question);
951
+ this.logger.debug("Human input received", {
952
+ gadgetName: call.gadgetName,
953
+ answerLength: answer.length
954
+ });
955
+ return {
956
+ gadgetName: call.gadgetName,
957
+ invocationId: call.invocationId,
958
+ parameters: validatedParameters,
959
+ result: answer,
960
+ executionTimeMs: Date.now() - startTime
961
+ };
962
+ } catch (inputError) {
963
+ this.logger.error("Human input callback error", {
964
+ gadgetName: call.gadgetName,
965
+ error: inputError instanceof Error ? inputError.message : String(inputError)
966
+ });
967
+ return {
968
+ gadgetName: call.gadgetName,
969
+ invocationId: call.invocationId,
970
+ parameters: validatedParameters,
971
+ error: inputError instanceof Error ? inputError.message : String(inputError),
972
+ executionTimeMs: Date.now() - startTime
973
+ };
974
+ }
975
+ }
976
+ this.logger.warn("Human input required but no callback provided", {
977
+ gadgetName: call.gadgetName
978
+ });
979
+ return {
980
+ gadgetName: call.gadgetName,
981
+ invocationId: call.invocationId,
982
+ parameters: validatedParameters,
983
+ error: "Human input required but not available (stdin is not interactive)",
984
+ executionTimeMs: Date.now() - startTime
985
+ };
986
+ }
987
+ const executionTimeMs = Date.now() - startTime;
988
+ this.logger.error("Gadget execution failed", {
989
+ gadgetName: call.gadgetName,
990
+ error: error instanceof Error ? error.message : String(error),
991
+ executionTimeMs
992
+ });
993
+ return {
994
+ gadgetName: call.gadgetName,
995
+ invocationId: call.invocationId,
996
+ parameters: validatedParameters,
997
+ error: error instanceof Error ? error.message : String(error),
998
+ executionTimeMs
999
+ };
1000
+ }
1001
+ }
1002
+ // Execute multiple gadget calls in parallel
1003
+ async executeAll(calls) {
1004
+ return Promise.all(calls.map((call) => this.execute(call)));
1005
+ }
1006
+ };
1007
+ }
1008
+ });
1009
+
1010
+ // src/gadgets/parser.ts
1011
+ import * as yaml from "js-yaml";
1012
+ var StreamParser;
1013
+ var init_parser = __esm({
1014
+ "src/gadgets/parser.ts"() {
1015
+ "use strict";
1016
+ init_constants();
1017
+ StreamParser = class {
1018
+ buffer = "";
1019
+ lastReportedTextLength = 0;
1020
+ startPrefix;
1021
+ endPrefix;
1022
+ parameterFormat;
1023
+ invocationCounter = 0;
1024
+ constructor(options = {}) {
1025
+ this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
1026
+ this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
1027
+ this.parameterFormat = options.parameterFormat ?? "json";
1028
+ }
1029
+ takeTextUntil(index) {
1030
+ if (index <= this.lastReportedTextLength) {
1031
+ return void 0;
1032
+ }
1033
+ const segment = this.buffer.slice(this.lastReportedTextLength, index);
1034
+ this.lastReportedTextLength = index;
1035
+ return segment.trim().length > 0 ? segment : void 0;
1036
+ }
1037
+ /**
1038
+ * Parse parameter string according to configured format
1039
+ */
1040
+ parseParameters(raw) {
1041
+ if (this.parameterFormat === "json") {
1042
+ try {
1043
+ return { parameters: JSON.parse(raw) };
1044
+ } catch (error) {
1045
+ return { parseError: error instanceof Error ? error.message : "Failed to parse JSON" };
1046
+ }
1047
+ }
1048
+ if (this.parameterFormat === "yaml") {
1049
+ try {
1050
+ return { parameters: yaml.load(raw) };
1051
+ } catch (error) {
1052
+ return { parseError: error instanceof Error ? error.message : "Failed to parse YAML" };
1053
+ }
1054
+ }
1055
+ try {
1056
+ return { parameters: JSON.parse(raw) };
1057
+ } catch {
1058
+ try {
1059
+ return { parameters: yaml.load(raw) };
1060
+ } catch (error) {
1061
+ return {
1062
+ parseError: error instanceof Error ? error.message : "Failed to parse as JSON or YAML"
1063
+ };
1064
+ }
1065
+ }
1066
+ }
1067
+ // Feed a chunk of text and get parsed events
1068
+ *feed(chunk) {
1069
+ this.buffer += chunk;
1070
+ let startIndex = 0;
1071
+ while (true) {
1072
+ const partStartIndex = this.buffer.indexOf(this.startPrefix, startIndex);
1073
+ if (partStartIndex === -1) break;
1074
+ const textBefore = this.takeTextUntil(partStartIndex);
1075
+ if (textBefore !== void 0) {
1076
+ yield { type: "text", content: textBefore };
1077
+ }
1078
+ const metadataStartIndex = partStartIndex + this.startPrefix.length;
1079
+ const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
1080
+ if (metadataEndIndex === -1) break;
1081
+ const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
1082
+ let invocationId;
1083
+ let actualGadgetName;
1084
+ if (gadgetName.includes(":")) {
1085
+ const parts = gadgetName.split(":");
1086
+ actualGadgetName = parts[0];
1087
+ invocationId = parts[1];
1088
+ } else {
1089
+ actualGadgetName = gadgetName;
1090
+ invocationId = `auto_${++this.invocationCounter}`;
1091
+ }
1092
+ const contentStartIndex = metadataEndIndex + 1;
1093
+ let partEndIndex;
1094
+ let endMarkerLength = 0;
1095
+ if (gadgetName.includes(":")) {
1096
+ const oldEndMarker = `${this.endPrefix + actualGadgetName}:${invocationId}`;
1097
+ partEndIndex = this.buffer.indexOf(oldEndMarker, contentStartIndex);
1098
+ if (partEndIndex === -1) break;
1099
+ endMarkerLength = oldEndMarker.length;
1100
+ } else {
1101
+ partEndIndex = contentStartIndex;
1102
+ while (true) {
1103
+ const endPos = this.buffer.indexOf(this.endPrefix, partEndIndex);
1104
+ if (endPos === -1) {
1105
+ partEndIndex = -1;
1106
+ break;
1107
+ }
1108
+ const afterEnd = this.buffer.substring(endPos + this.endPrefix.length);
1109
+ if (afterEnd.startsWith("\n") || afterEnd.startsWith("\r") || afterEnd.startsWith(this.startPrefix) || afterEnd.length === 0) {
1110
+ partEndIndex = endPos;
1111
+ endMarkerLength = this.endPrefix.length;
1112
+ break;
1113
+ } else {
1114
+ partEndIndex = endPos + this.endPrefix.length;
1115
+ }
1116
+ }
1117
+ if (partEndIndex === -1) break;
1118
+ }
1119
+ const parametersRaw = this.buffer.substring(contentStartIndex, partEndIndex).trim();
1120
+ const { parameters, parseError } = this.parseParameters(parametersRaw);
1121
+ yield {
1122
+ type: "gadget_call",
1123
+ call: {
1124
+ gadgetName: actualGadgetName,
1125
+ invocationId,
1126
+ parametersYaml: parametersRaw,
1127
+ // Keep property name for backward compatibility
1128
+ parameters,
1129
+ parseError
1130
+ }
1131
+ };
1132
+ startIndex = partEndIndex + endMarkerLength;
1133
+ this.lastReportedTextLength = startIndex;
1134
+ }
1135
+ if (startIndex > 0) {
1136
+ this.buffer = this.buffer.substring(startIndex);
1137
+ this.lastReportedTextLength = 0;
1138
+ }
1139
+ }
1140
+ // Finalize parsing and return remaining text
1141
+ *finalize() {
1142
+ const remainingText = this.takeTextUntil(this.buffer.length);
1143
+ if (remainingText !== void 0) {
1144
+ yield { type: "text", content: remainingText };
1145
+ }
1146
+ }
1147
+ // Reset parser state
1148
+ reset() {
1149
+ this.buffer = "";
1150
+ this.lastReportedTextLength = 0;
1151
+ this.invocationCounter = 0;
1152
+ }
1153
+ };
1154
+ }
1155
+ });
1156
+
1157
+ // src/agent/hook-validators.ts
1158
+ function validateBeforeLLMCallAction(action) {
1159
+ if (!action || typeof action !== "object" || !("action" in action)) {
1160
+ throw new HookValidationError(
1161
+ "beforeLLMCall",
1162
+ "Must return an action object with an 'action' field"
1163
+ );
1164
+ }
1165
+ const actionType = action.action;
1166
+ if (actionType !== "proceed" && actionType !== "skip") {
1167
+ throw new HookValidationError(
1168
+ "beforeLLMCall",
1169
+ `Invalid action type: ${actionType}. Must be 'proceed' or 'skip'`
1170
+ );
1171
+ }
1172
+ if (actionType === "skip" && !action.syntheticResponse) {
1173
+ throw new HookValidationError(
1174
+ "beforeLLMCall",
1175
+ "When action is 'skip', syntheticResponse is required"
1176
+ );
1177
+ }
1178
+ }
1179
+ function validateAfterLLMCallAction(action) {
1180
+ if (!action || typeof action !== "object" || !("action" in action)) {
1181
+ throw new HookValidationError(
1182
+ "afterLLMCall",
1183
+ "Must return an action object with an 'action' field"
1184
+ );
1185
+ }
1186
+ const actionType = action.action;
1187
+ const validActions = ["continue", "append_messages", "modify_and_continue", "append_and_modify"];
1188
+ if (!validActions.includes(actionType)) {
1189
+ throw new HookValidationError(
1190
+ "afterLLMCall",
1191
+ `Invalid action type: ${actionType}. Must be one of: ${validActions.join(", ")}`
1192
+ );
1193
+ }
1194
+ if (actionType === "append_messages" || actionType === "append_and_modify") {
1195
+ if (!("messages" in action) || !action.messages || !Array.isArray(action.messages)) {
1196
+ throw new HookValidationError(
1197
+ "afterLLMCall",
1198
+ `When action is '${actionType}', messages array is required`
1199
+ );
1200
+ }
1201
+ if (action.messages.length === 0) {
1202
+ throw new HookValidationError(
1203
+ "afterLLMCall",
1204
+ `When action is '${actionType}', messages array must not be empty`
1205
+ );
1206
+ }
1207
+ for (let i = 0; i < action.messages.length; i++) {
1208
+ const msg = action.messages[i];
1209
+ if (!msg || typeof msg !== "object") {
1210
+ throw new HookValidationError("afterLLMCall", `Message at index ${i} must be an object`);
1211
+ }
1212
+ if (!msg.role || !msg.content) {
1213
+ throw new HookValidationError(
1214
+ "afterLLMCall",
1215
+ `Message at index ${i} must have 'role' and 'content' fields`
1216
+ );
1217
+ }
1218
+ if (!["system", "user", "assistant"].includes(msg.role)) {
1219
+ throw new HookValidationError(
1220
+ "afterLLMCall",
1221
+ `Message at index ${i} has invalid role: ${msg.role}`
1222
+ );
1223
+ }
1224
+ }
1225
+ }
1226
+ if (actionType === "modify_and_continue" || actionType === "append_and_modify") {
1227
+ if (!("modifiedMessage" in action) || !action.modifiedMessage) {
1228
+ throw new HookValidationError(
1229
+ "afterLLMCall",
1230
+ `When action is '${actionType}', modifiedMessage is required`
1231
+ );
1232
+ }
1233
+ }
1234
+ }
1235
+ function validateAfterLLMErrorAction(action) {
1236
+ if (!action || typeof action !== "object" || !("action" in action)) {
1237
+ throw new HookValidationError(
1238
+ "afterLLMError",
1239
+ "Must return an action object with an 'action' field"
1240
+ );
1241
+ }
1242
+ const actionType = action.action;
1243
+ if (actionType !== "rethrow" && actionType !== "recover") {
1244
+ throw new HookValidationError(
1245
+ "afterLLMError",
1246
+ `Invalid action type: ${actionType}. Must be 'rethrow' or 'recover'`
1247
+ );
1248
+ }
1249
+ if (actionType === "recover" && !action.fallbackResponse) {
1250
+ throw new HookValidationError(
1251
+ "afterLLMError",
1252
+ "When action is 'recover', fallbackResponse is required"
1253
+ );
1254
+ }
1255
+ }
1256
+ function validateBeforeGadgetExecutionAction(action) {
1257
+ if (!action || typeof action !== "object" || !("action" in action)) {
1258
+ throw new HookValidationError(
1259
+ "beforeGadgetExecution",
1260
+ "Must return an action object with an 'action' field"
1261
+ );
1262
+ }
1263
+ const actionType = action.action;
1264
+ if (actionType !== "proceed" && actionType !== "skip") {
1265
+ throw new HookValidationError(
1266
+ "beforeGadgetExecution",
1267
+ `Invalid action type: ${actionType}. Must be 'proceed' or 'skip'`
1268
+ );
1269
+ }
1270
+ if (actionType === "skip" && !action.syntheticResult) {
1271
+ throw new HookValidationError(
1272
+ "beforeGadgetExecution",
1273
+ "When action is 'skip', syntheticResult is required"
1274
+ );
1275
+ }
1276
+ }
1277
+ function validateAfterGadgetExecutionAction(action) {
1278
+ if (!action || typeof action !== "object" || !("action" in action)) {
1279
+ throw new HookValidationError(
1280
+ "afterGadgetExecution",
1281
+ "Must return an action object with an 'action' field"
1282
+ );
1283
+ }
1284
+ const actionType = action.action;
1285
+ if (actionType !== "continue" && actionType !== "recover") {
1286
+ throw new HookValidationError(
1287
+ "afterGadgetExecution",
1288
+ `Invalid action type: ${actionType}. Must be 'continue' or 'recover'`
1289
+ );
1290
+ }
1291
+ if (actionType === "recover" && !action.fallbackResult) {
1292
+ throw new HookValidationError(
1293
+ "afterGadgetExecution",
1294
+ "When action is 'recover', fallbackResult is required"
1295
+ );
1296
+ }
1297
+ }
1298
+ var HookValidationError;
1299
+ var init_hook_validators = __esm({
1300
+ "src/agent/hook-validators.ts"() {
1301
+ "use strict";
1302
+ HookValidationError = class extends Error {
1303
+ constructor(hookName, message) {
1304
+ super(`Invalid action from ${hookName}: ${message}`);
1305
+ this.name = "HookValidationError";
1306
+ }
1307
+ };
1308
+ }
1309
+ });
1310
+
1311
+ // src/agent/stream-processor.ts
1312
+ var StreamProcessor;
1313
+ var init_stream_processor = __esm({
1314
+ "src/agent/stream-processor.ts"() {
1315
+ "use strict";
1316
+ init_executor();
1317
+ init_parser();
1318
+ init_logger();
1319
+ init_hook_validators();
1320
+ StreamProcessor = class {
1321
+ iteration;
1322
+ registry;
1323
+ hooks;
1324
+ logger;
1325
+ parser;
1326
+ executor;
1327
+ stopOnGadgetError;
1328
+ shouldContinueAfterError;
1329
+ accumulatedText = "";
1330
+ shouldStopExecution = false;
1331
+ observerFailureCount = 0;
1332
+ constructor(options) {
1333
+ this.iteration = options.iteration;
1334
+ this.registry = options.registry;
1335
+ this.hooks = options.hooks ?? {};
1336
+ this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
1337
+ this.stopOnGadgetError = options.stopOnGadgetError ?? true;
1338
+ this.shouldContinueAfterError = options.shouldContinueAfterError;
1339
+ this.parser = new StreamParser({
1340
+ parameterFormat: options.parameterFormat,
1341
+ startPrefix: options.gadgetStartPrefix,
1342
+ endPrefix: options.gadgetEndPrefix
1343
+ });
1344
+ this.executor = new GadgetExecutor(
1345
+ options.registry,
1346
+ options.onHumanInputRequired,
1347
+ this.logger.getSubLogger({ name: "executor" }),
1348
+ options.defaultGadgetTimeoutMs
1349
+ );
1350
+ }
1351
+ /**
1352
+ * Process an LLM stream and return structured results.
1353
+ */
1354
+ async process(stream2) {
1355
+ const outputs = [];
1356
+ let finishReason = null;
1357
+ let usage;
1358
+ let didExecuteGadgets = false;
1359
+ let shouldBreakLoop = false;
1360
+ for await (const chunk of stream2) {
1361
+ if (chunk.finishReason) finishReason = chunk.finishReason;
1362
+ if (chunk.usage) usage = chunk.usage;
1363
+ let processedChunk = "";
1364
+ if (chunk.text) {
1365
+ processedChunk = chunk.text;
1366
+ if (this.hooks.interceptors?.interceptRawChunk) {
1367
+ const context = {
1368
+ iteration: this.iteration,
1369
+ accumulatedText: this.accumulatedText,
1370
+ logger: this.logger
1371
+ };
1372
+ const intercepted = this.hooks.interceptors.interceptRawChunk(processedChunk, context);
1373
+ if (intercepted === null) {
1374
+ processedChunk = "";
1375
+ } else {
1376
+ processedChunk = intercepted;
1377
+ }
1378
+ }
1379
+ if (processedChunk) {
1380
+ this.accumulatedText += processedChunk;
1381
+ }
1382
+ }
1383
+ if (this.hooks.observers?.onStreamChunk && (processedChunk || chunk.usage)) {
1384
+ const chunkObservers = [];
1385
+ chunkObservers.push(async () => {
1386
+ const context = {
1387
+ iteration: this.iteration,
1388
+ rawChunk: processedChunk,
1389
+ accumulatedText: this.accumulatedText,
1390
+ usage,
1391
+ logger: this.logger
1392
+ };
1393
+ await this.hooks.observers.onStreamChunk(context);
1394
+ });
1395
+ await this.runObserversInParallel(chunkObservers);
1396
+ }
1397
+ if (!processedChunk) {
1398
+ continue;
1399
+ }
1400
+ for (const event of this.parser.feed(processedChunk)) {
1401
+ const processedEvents = await this.processEvent(event);
1402
+ outputs.push(...processedEvents);
1403
+ if (processedEvents.some((e) => e.type === "gadget_result")) {
1404
+ didExecuteGadgets = true;
1405
+ }
1406
+ for (const evt of processedEvents) {
1407
+ if (evt.type === "gadget_result" && evt.result.breaksLoop) {
1408
+ shouldBreakLoop = true;
1409
+ }
1410
+ }
1411
+ }
1412
+ if (this.shouldStopExecution) {
1413
+ this.logger.info("Breaking from LLM stream due to gadget error");
1414
+ break;
1415
+ }
1416
+ }
1417
+ if (!this.shouldStopExecution) {
1418
+ for (const event of this.parser.finalize()) {
1419
+ const processedEvents = await this.processEvent(event);
1420
+ outputs.push(...processedEvents);
1421
+ if (processedEvents.some((e) => e.type === "gadget_result")) {
1422
+ didExecuteGadgets = true;
1423
+ }
1424
+ for (const evt of processedEvents) {
1425
+ if (evt.type === "gadget_result" && evt.result.breaksLoop) {
1426
+ shouldBreakLoop = true;
1427
+ }
1428
+ }
1429
+ }
1430
+ }
1431
+ let finalMessage = this.accumulatedText;
1432
+ if (this.hooks.interceptors?.interceptAssistantMessage) {
1433
+ const context = {
1434
+ iteration: this.iteration,
1435
+ rawResponse: this.accumulatedText,
1436
+ logger: this.logger
1437
+ };
1438
+ finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
1439
+ }
1440
+ return {
1441
+ outputs,
1442
+ shouldBreakLoop,
1443
+ didExecuteGadgets,
1444
+ finishReason,
1445
+ usage,
1446
+ rawResponse: this.accumulatedText,
1447
+ finalMessage
1448
+ };
1449
+ }
1450
+ /**
1451
+ * Process a single parsed event (text or gadget call).
1452
+ */
1453
+ async processEvent(event) {
1454
+ if (event.type === "text") {
1455
+ return this.processTextEvent(event);
1456
+ } else if (event.type === "gadget_call") {
1457
+ return this.processGadgetCall(event.call);
1458
+ }
1459
+ return [event];
1460
+ }
1461
+ /**
1462
+ * Process a text event through interceptors.
1463
+ */
1464
+ async processTextEvent(event) {
1465
+ let content = event.content;
1466
+ if (this.hooks.interceptors?.interceptTextChunk) {
1467
+ const context = {
1468
+ iteration: this.iteration,
1469
+ accumulatedText: this.accumulatedText,
1470
+ logger: this.logger
1471
+ };
1472
+ const intercepted = this.hooks.interceptors.interceptTextChunk(content, context);
1473
+ if (intercepted === null) {
1474
+ return [];
1475
+ }
1476
+ content = intercepted;
1477
+ }
1478
+ return [{ type: "text", content }];
1479
+ }
1480
+ /**
1481
+ * Process a gadget call through the full lifecycle.
1482
+ */
1483
+ async processGadgetCall(call) {
1484
+ if (this.shouldStopExecution) {
1485
+ this.logger.debug("Skipping gadget execution due to previous error", {
1486
+ gadgetName: call.gadgetName
1487
+ });
1488
+ return [];
1489
+ }
1490
+ const events = [];
1491
+ events.push({ type: "gadget_call", call });
1492
+ if (call.parseError) {
1493
+ this.logger.warn("Gadget has parse error", {
1494
+ gadgetName: call.gadgetName,
1495
+ error: call.parseError
1496
+ });
1497
+ const shouldContinue = await this.checkContinueAfterError(
1498
+ call.parseError,
1499
+ call.gadgetName,
1500
+ "parse",
1501
+ call.parameters
1502
+ );
1503
+ if (!shouldContinue) {
1504
+ this.shouldStopExecution = true;
1505
+ }
1506
+ }
1507
+ let parameters = call.parameters ?? {};
1508
+ if (this.hooks.interceptors?.interceptGadgetParameters) {
1509
+ const context = {
1510
+ iteration: this.iteration,
1511
+ gadgetName: call.gadgetName,
1512
+ invocationId: call.invocationId,
1513
+ logger: this.logger
1514
+ };
1515
+ parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
1516
+ }
1517
+ call.parameters = parameters;
1518
+ let shouldSkip = false;
1519
+ let syntheticResult;
1520
+ if (this.hooks.controllers?.beforeGadgetExecution) {
1521
+ const context = {
1522
+ iteration: this.iteration,
1523
+ gadgetName: call.gadgetName,
1524
+ invocationId: call.invocationId,
1525
+ parameters,
1526
+ logger: this.logger
1527
+ };
1528
+ const action = await this.hooks.controllers.beforeGadgetExecution(context);
1529
+ validateBeforeGadgetExecutionAction(action);
1530
+ if (action.action === "skip") {
1531
+ shouldSkip = true;
1532
+ syntheticResult = action.syntheticResult;
1533
+ this.logger.info("Controller skipped gadget execution", {
1534
+ gadgetName: call.gadgetName
1535
+ });
1536
+ }
1537
+ }
1538
+ const startObservers = [];
1539
+ if (this.hooks.observers?.onGadgetExecutionStart) {
1540
+ startObservers.push(async () => {
1541
+ const context = {
1542
+ iteration: this.iteration,
1543
+ gadgetName: call.gadgetName,
1544
+ invocationId: call.invocationId,
1545
+ parameters,
1546
+ logger: this.logger
1547
+ };
1548
+ await this.hooks.observers.onGadgetExecutionStart(context);
1549
+ });
1550
+ }
1551
+ await this.runObserversInParallel(startObservers);
1552
+ let result;
1553
+ if (shouldSkip) {
1554
+ result = {
1555
+ gadgetName: call.gadgetName,
1556
+ invocationId: call.invocationId,
1557
+ parameters,
1558
+ result: syntheticResult ?? "Execution skipped",
1559
+ executionTimeMs: 0
1560
+ };
1561
+ } else {
1562
+ result = await this.executor.execute(call);
1563
+ }
1564
+ const originalResult = result.result;
1565
+ if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
1566
+ const context = {
1567
+ iteration: this.iteration,
1568
+ gadgetName: result.gadgetName,
1569
+ invocationId: result.invocationId,
1570
+ parameters,
1571
+ executionTimeMs: result.executionTimeMs,
1572
+ logger: this.logger
1573
+ };
1574
+ result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
1575
+ }
1576
+ if (this.hooks.controllers?.afterGadgetExecution) {
1577
+ const context = {
1578
+ iteration: this.iteration,
1579
+ gadgetName: result.gadgetName,
1580
+ invocationId: result.invocationId,
1581
+ parameters,
1582
+ result: result.result,
1583
+ error: result.error,
1584
+ executionTimeMs: result.executionTimeMs,
1585
+ logger: this.logger
1586
+ };
1587
+ const action = await this.hooks.controllers.afterGadgetExecution(context);
1588
+ validateAfterGadgetExecutionAction(action);
1589
+ if (action.action === "recover" && result.error) {
1590
+ this.logger.info("Controller recovered from gadget error", {
1591
+ gadgetName: result.gadgetName,
1592
+ originalError: result.error
1593
+ });
1594
+ result = {
1595
+ ...result,
1596
+ error: void 0,
1597
+ result: action.fallbackResult
1598
+ };
1599
+ }
1600
+ }
1601
+ const completeObservers = [];
1602
+ if (this.hooks.observers?.onGadgetExecutionComplete) {
1603
+ completeObservers.push(async () => {
1604
+ const context = {
1605
+ iteration: this.iteration,
1606
+ gadgetName: result.gadgetName,
1607
+ invocationId: result.invocationId,
1608
+ parameters,
1609
+ originalResult,
1610
+ finalResult: result.result,
1611
+ error: result.error,
1612
+ executionTimeMs: result.executionTimeMs,
1613
+ breaksLoop: result.breaksLoop,
1614
+ logger: this.logger
1615
+ };
1616
+ await this.hooks.observers.onGadgetExecutionComplete(context);
1617
+ });
1618
+ }
1619
+ await this.runObserversInParallel(completeObservers);
1620
+ events.push({ type: "gadget_result", result });
1621
+ if (result.error) {
1622
+ const errorType = this.determineErrorType(call, result);
1623
+ const shouldContinue = await this.checkContinueAfterError(
1624
+ result.error,
1625
+ result.gadgetName,
1626
+ errorType,
1627
+ result.parameters
1628
+ );
1629
+ if (!shouldContinue) {
1630
+ this.shouldStopExecution = true;
1631
+ }
1632
+ }
1633
+ return events;
1634
+ }
1635
+ /**
1636
+ * Safely execute an observer, catching and logging any errors.
1637
+ * Observers are non-critical, so errors are logged but don't crash the system.
1638
+ */
1639
+ async safeObserve(fn) {
1640
+ try {
1641
+ await fn();
1642
+ } catch (error) {
1643
+ this.observerFailureCount++;
1644
+ this.logger.error("Observer threw error (ignoring)", {
1645
+ error: error instanceof Error ? error.message : String(error),
1646
+ failureCount: this.observerFailureCount
1647
+ });
1648
+ }
1649
+ }
1650
+ /**
1651
+ * Execute multiple observers in parallel.
1652
+ * All observers run concurrently and failures are tracked but don't crash.
1653
+ */
1654
+ async runObserversInParallel(observers) {
1655
+ if (observers.length === 0) return;
1656
+ const results = await Promise.allSettled(
1657
+ observers.map((observer) => this.safeObserve(observer))
1658
+ );
1659
+ }
1660
+ /**
1661
+ * Check if execution should continue after an error.
1662
+ *
1663
+ * Returns true if we should continue processing subsequent gadgets, false if we should stop.
1664
+ *
1665
+ * Logic:
1666
+ * - If custom shouldContinueAfterError is provided, use it
1667
+ * - Otherwise, use stopOnGadgetError config:
1668
+ * - stopOnGadgetError=true → return false (stop execution)
1669
+ * - stopOnGadgetError=false → return true (continue execution)
1670
+ */
1671
+ async checkContinueAfterError(error, gadgetName, errorType, parameters) {
1672
+ if (this.shouldContinueAfterError) {
1673
+ return await this.shouldContinueAfterError({
1674
+ error,
1675
+ gadgetName,
1676
+ errorType,
1677
+ parameters
1678
+ });
1679
+ }
1680
+ const shouldContinue = !this.stopOnGadgetError;
1681
+ this.logger.debug("Checking if should continue after error", {
1682
+ error,
1683
+ gadgetName,
1684
+ errorType,
1685
+ stopOnGadgetError: this.stopOnGadgetError,
1686
+ shouldContinue
1687
+ });
1688
+ return shouldContinue;
1689
+ }
1690
+ /**
1691
+ * Determine the type of error from a gadget execution.
1692
+ */
1693
+ determineErrorType(call, result) {
1694
+ if (call.parseError) {
1695
+ return "parse";
1696
+ }
1697
+ if (result.error?.includes("Invalid parameters:")) {
1698
+ return "validation";
1699
+ }
1700
+ return "execution";
1701
+ }
1702
+ };
1703
+ }
1704
+ });
1705
+
1706
+ // src/providers/anthropic-models.ts
1707
+ var ANTHROPIC_MODELS;
1708
+ var init_anthropic_models = __esm({
1709
+ "src/providers/anthropic-models.ts"() {
1710
+ "use strict";
1711
+ ANTHROPIC_MODELS = [
1712
+ {
1713
+ provider: "anthropic",
1714
+ modelId: "claude-opus-4-5-20251124",
1715
+ displayName: "Claude Opus 4.5",
1716
+ contextWindow: 2e5,
1717
+ maxOutputTokens: 64e3,
1718
+ pricing: {
1719
+ input: 5,
1720
+ output: 25,
1721
+ cachedInput: 0.5
1722
+ },
1723
+ knowledgeCutoff: "2025-03",
1724
+ features: {
1725
+ streaming: true,
1726
+ functionCalling: true,
1727
+ vision: true,
1728
+ reasoning: true
1729
+ },
1730
+ metadata: {
1731
+ family: "Claude 4",
1732
+ releaseDate: "2025-11-24",
1733
+ notes: "Most powerful model. 80.9% SWE-bench Verified, 66.3% OSWorld. Best for coding and computer use."
1734
+ }
1735
+ },
1736
+ {
1737
+ provider: "anthropic",
1738
+ modelId: "claude-sonnet-4-5-20250929",
1739
+ displayName: "Claude Sonnet 4.5",
1740
+ contextWindow: 2e5,
1741
+ maxOutputTokens: 64e3,
1742
+ pricing: {
1743
+ input: 3,
1744
+ output: 15,
1745
+ cachedInput: 0.3
1746
+ },
1747
+ knowledgeCutoff: "2025-01",
1748
+ features: {
1749
+ streaming: true,
1750
+ functionCalling: true,
1751
+ vision: true,
1752
+ reasoning: true
1753
+ },
1754
+ metadata: {
1755
+ family: "Claude 4",
1756
+ releaseDate: "2025-09-29",
1757
+ notes: "Smartest model for complex agents and coding. Extended thinking. 1M context in beta."
1758
+ }
1759
+ },
1760
+ {
1761
+ provider: "anthropic",
1762
+ modelId: "claude-haiku-4-5-20251001",
1763
+ displayName: "Claude Haiku 4.5",
1764
+ contextWindow: 2e5,
1765
+ maxOutputTokens: 64e3,
1766
+ pricing: {
1767
+ input: 1,
1768
+ output: 5,
1769
+ cachedInput: 0.1
1770
+ },
1771
+ knowledgeCutoff: "2025-02",
1772
+ features: {
1773
+ streaming: true,
1774
+ functionCalling: true,
1775
+ vision: true,
1776
+ reasoning: true
1777
+ },
1778
+ metadata: {
1779
+ family: "Claude 4",
1780
+ releaseDate: "2025-10-01",
1781
+ notes: "Fastest model with near-frontier intelligence. Excellent for coding (73.3% SWE-bench)."
1782
+ }
1783
+ },
1784
+ {
1785
+ provider: "anthropic",
1786
+ modelId: "claude-sonnet-4-20250514",
1787
+ displayName: "Claude Sonnet 4",
1788
+ contextWindow: 2e5,
1789
+ maxOutputTokens: 64e3,
1790
+ pricing: {
1791
+ input: 3,
1792
+ output: 15,
1793
+ cachedInput: 0.3
1794
+ },
1795
+ knowledgeCutoff: "2025-03",
1796
+ features: {
1797
+ streaming: true,
1798
+ functionCalling: true,
1799
+ vision: true,
1800
+ reasoning: true
1801
+ },
1802
+ metadata: {
1803
+ family: "Claude 4",
1804
+ releaseDate: "2025-05-14",
1805
+ notes: "High performance with vision and extended thinking"
1806
+ }
1807
+ },
1808
+ {
1809
+ provider: "anthropic",
1810
+ modelId: "claude-3-7-sonnet-20250219",
1811
+ displayName: "Claude Sonnet 3.7",
1812
+ contextWindow: 2e5,
1813
+ maxOutputTokens: 64e3,
1814
+ pricing: {
1815
+ input: 3,
1816
+ output: 15,
1817
+ cachedInput: 0.3
1818
+ },
1819
+ knowledgeCutoff: "2024-11",
1820
+ features: {
1821
+ streaming: true,
1822
+ functionCalling: true,
1823
+ vision: true,
1824
+ reasoning: true
1825
+ },
1826
+ metadata: {
1827
+ family: "Claude 3",
1828
+ releaseDate: "2025-02-19",
1829
+ notes: "Legacy model - consider upgrading to Claude 4 family"
1830
+ }
1831
+ },
1832
+ {
1833
+ provider: "anthropic",
1834
+ modelId: "claude-opus-4-1-20250805",
1835
+ displayName: "Claude Opus 4.1",
1836
+ contextWindow: 2e5,
1837
+ maxOutputTokens: 32e3,
1838
+ pricing: {
1839
+ input: 15,
1840
+ output: 75,
1841
+ cachedInput: 1.5
1842
+ },
1843
+ knowledgeCutoff: "2025-01",
1844
+ features: {
1845
+ streaming: true,
1846
+ functionCalling: true,
1847
+ vision: true,
1848
+ reasoning: true
1849
+ },
1850
+ metadata: {
1851
+ family: "Claude 4",
1852
+ releaseDate: "2025-08-05",
1853
+ notes: "Exceptional for specialized reasoning tasks. Extended thinking support."
1854
+ }
1855
+ },
1856
+ {
1857
+ provider: "anthropic",
1858
+ modelId: "claude-opus-4-20250514",
1859
+ displayName: "Claude Opus 4",
1860
+ contextWindow: 2e5,
1861
+ maxOutputTokens: 32e3,
1862
+ pricing: {
1863
+ input: 15,
1864
+ output: 75,
1865
+ cachedInput: 1.5
1866
+ },
1867
+ knowledgeCutoff: "2025-03",
1868
+ features: {
1869
+ streaming: true,
1870
+ functionCalling: true,
1871
+ vision: true
1872
+ },
1873
+ metadata: {
1874
+ family: "Claude 4",
1875
+ releaseDate: "2025-05-14",
1876
+ notes: "Legacy Opus model - consider Opus 4.1 for improved reasoning"
1877
+ }
1878
+ },
1879
+ {
1880
+ provider: "anthropic",
1881
+ modelId: "claude-3-5-haiku-20241022",
1882
+ displayName: "Claude Haiku 3.5",
1883
+ contextWindow: 2e5,
1884
+ maxOutputTokens: 8192,
1885
+ pricing: {
1886
+ input: 0.8,
1887
+ output: 4,
1888
+ cachedInput: 0.08
1889
+ },
1890
+ knowledgeCutoff: "2024-07",
1891
+ features: {
1892
+ streaming: true,
1893
+ functionCalling: true,
1894
+ vision: true
1895
+ },
1896
+ metadata: {
1897
+ family: "Claude 3",
1898
+ releaseDate: "2024-10-22",
1899
+ notes: "Legacy model - upgrade to Haiku 4.5 for better performance"
1900
+ }
1901
+ },
1902
+ {
1903
+ provider: "anthropic",
1904
+ modelId: "claude-3-haiku-20240307",
1905
+ displayName: "Claude Haiku 3",
1906
+ contextWindow: 2e5,
1907
+ maxOutputTokens: 4096,
1908
+ pricing: {
1909
+ input: 0.25,
1910
+ output: 1.25,
1911
+ cachedInput: 0.025
1912
+ },
1913
+ knowledgeCutoff: "2023-08",
1914
+ features: {
1915
+ streaming: true,
1916
+ functionCalling: true,
1917
+ vision: true
1918
+ },
1919
+ metadata: {
1920
+ family: "Claude 3",
1921
+ releaseDate: "2024-03-07",
1922
+ notes: "Legacy model - upgrade to Haiku 4.5 for better performance"
1923
+ }
1924
+ }
1925
+ ];
1926
+ }
1927
+ });
1928
+
1929
+ // src/providers/base-provider.ts
1930
+ var BaseProviderAdapter;
1931
+ var init_base_provider = __esm({
1932
+ "src/providers/base-provider.ts"() {
1933
+ "use strict";
1934
+ BaseProviderAdapter = class {
1935
+ constructor(client) {
1936
+ this.client = client;
1937
+ }
1938
+ /**
1939
+ * Template method that defines the skeleton of the streaming algorithm.
1940
+ * This orchestrates the four-step process without dictating provider-specific details.
1941
+ */
1942
+ async *stream(options, descriptor, spec) {
1943
+ const preparedMessages = this.prepareMessages(options.messages);
1944
+ const payload = this.buildRequestPayload(options, descriptor, spec, preparedMessages);
1945
+ const rawStream = await this.executeStreamRequest(payload);
1946
+ yield* this.wrapStream(rawStream);
1947
+ }
1948
+ /**
1949
+ * Prepare messages for the request.
1950
+ * Default implementation returns messages unchanged.
1951
+ * Override this to implement provider-specific message transformations
1952
+ * (e.g., Gemini's consecutive message merging, Anthropic's system message extraction).
1953
+ *
1954
+ * @param messages - The input messages
1955
+ * @returns Prepared messages
1956
+ */
1957
+ prepareMessages(messages) {
1958
+ return messages;
1959
+ }
1960
+ };
1961
+ }
1962
+ });
1963
+
1964
+ // src/providers/constants.ts
1965
+ var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS, FALLBACK_CHARS_PER_TOKEN, OPENAI_MESSAGE_OVERHEAD_TOKENS, OPENAI_REPLY_PRIMING_TOKENS, OPENAI_NAME_FIELD_OVERHEAD_TOKENS;
1966
+ var init_constants2 = __esm({
1967
+ "src/providers/constants.ts"() {
1968
+ "use strict";
1969
+ ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
1970
+ FALLBACK_CHARS_PER_TOKEN = 4;
1971
+ OPENAI_MESSAGE_OVERHEAD_TOKENS = 4;
1972
+ OPENAI_REPLY_PRIMING_TOKENS = 2;
1973
+ OPENAI_NAME_FIELD_OVERHEAD_TOKENS = 1;
1974
+ }
1975
+ });
1976
+
1977
+ // src/providers/utils.ts
1978
+ function readEnvVar(key) {
1979
+ if (typeof process === "undefined" || typeof process.env === "undefined") {
1980
+ return void 0;
1981
+ }
1982
+ const value = process.env[key];
1983
+ return typeof value === "string" ? value : void 0;
1984
+ }
1985
+ function isNonEmpty(value) {
1986
+ return typeof value === "string" && value.trim().length > 0;
1987
+ }
1988
+ function createProviderFromEnv(envVarName, ClientClass, ProviderClass, clientOptions) {
1989
+ const apiKey = readEnvVar(envVarName);
1990
+ if (!isNonEmpty(apiKey)) {
1991
+ return null;
1992
+ }
1993
+ const client = new ClientClass({ apiKey: apiKey.trim(), ...clientOptions });
1994
+ return new ProviderClass(client);
1995
+ }
1996
+ var init_utils = __esm({
1997
+ "src/providers/utils.ts"() {
1998
+ "use strict";
1999
+ }
2000
+ });
2001
+
2002
+ // src/providers/anthropic.ts
2003
+ import Anthropic from "@anthropic-ai/sdk";
2004
+ function createAnthropicProviderFromEnv() {
2005
+ return createProviderFromEnv("ANTHROPIC_API_KEY", Anthropic, AnthropicMessagesProvider);
2006
+ }
2007
+ var AnthropicMessagesProvider;
2008
+ var init_anthropic = __esm({
2009
+ "src/providers/anthropic.ts"() {
2010
+ "use strict";
2011
+ init_anthropic_models();
2012
+ init_base_provider();
2013
+ init_constants2();
2014
+ init_utils();
2015
+ AnthropicMessagesProvider = class extends BaseProviderAdapter {
2016
+ providerId = "anthropic";
2017
+ supports(descriptor) {
2018
+ return descriptor.provider === this.providerId;
2019
+ }
2020
+ getModelSpecs() {
2021
+ return ANTHROPIC_MODELS;
2022
+ }
2023
+ buildRequestPayload(options, descriptor, spec, messages) {
2024
+ const systemMessages = messages.filter((message) => message.role === "system");
2025
+ const system = systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n\n") : void 0;
2026
+ const conversation = messages.filter(
2027
+ (message) => message.role !== "system"
2028
+ ).map((message) => ({
2029
+ role: message.role,
2030
+ content: [
2031
+ {
2032
+ type: "text",
2033
+ text: message.content
2034
+ }
2035
+ ]
2036
+ }));
2037
+ const defaultMaxTokens = spec?.maxOutputTokens ?? ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS;
2038
+ const payload = {
2039
+ model: descriptor.name,
2040
+ system,
2041
+ messages: conversation,
2042
+ max_tokens: options.maxTokens ?? defaultMaxTokens,
2043
+ temperature: options.temperature,
2044
+ top_p: options.topP,
2045
+ stop_sequences: options.stopSequences,
2046
+ stream: true,
2047
+ ...options.extra
2048
+ };
2049
+ return payload;
2050
+ }
2051
+ async executeStreamRequest(payload) {
2052
+ const client = this.client;
2053
+ const stream2 = await client.messages.create(payload);
2054
+ return stream2;
2055
+ }
2056
+ async *wrapStream(iterable) {
2057
+ const stream2 = iterable;
2058
+ let inputTokens = 0;
2059
+ for await (const event of stream2) {
2060
+ if (event.type === "message_start") {
2061
+ inputTokens = event.message.usage.input_tokens;
2062
+ yield {
2063
+ text: "",
2064
+ usage: {
2065
+ inputTokens,
2066
+ outputTokens: 0,
2067
+ totalTokens: inputTokens
2068
+ },
2069
+ rawEvent: event
2070
+ };
2071
+ continue;
2072
+ }
2073
+ if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
2074
+ yield { text: event.delta.text ?? "", rawEvent: event };
2075
+ continue;
2076
+ }
2077
+ if (event.type === "message_delta") {
2078
+ const usage = event.usage ? {
2079
+ inputTokens,
2080
+ outputTokens: event.usage.output_tokens,
2081
+ totalTokens: inputTokens + event.usage.output_tokens
2082
+ } : void 0;
2083
+ if (event.delta.stop_reason || usage) {
2084
+ yield {
2085
+ text: "",
2086
+ finishReason: event.delta.stop_reason ?? void 0,
2087
+ usage,
2088
+ rawEvent: event
2089
+ };
2090
+ }
2091
+ continue;
2092
+ }
2093
+ if (event.type === "message_stop") {
2094
+ yield { text: "", finishReason: "stop", rawEvent: event };
2095
+ }
2096
+ }
2097
+ }
2098
+ /**
2099
+ * Count tokens in messages using Anthropic's native token counting API.
2100
+ *
2101
+ * This method provides accurate token estimation for Anthropic models by:
2102
+ * - Using the native messages.countTokens() API
2103
+ * - Properly handling system messages and conversation structure
2104
+ * - Transforming messages to Anthropic's expected format
2105
+ *
2106
+ * @param messages - The messages to count tokens for
2107
+ * @param descriptor - Model descriptor containing the model name
2108
+ * @param _spec - Optional model specification (currently unused)
2109
+ * @returns Promise resolving to the estimated input token count
2110
+ *
2111
+ * @throws Never throws - falls back to character-based estimation (4 chars/token) on error
2112
+ *
2113
+ * @example
2114
+ * ```typescript
2115
+ * const count = await provider.countTokens(
2116
+ * [{ role: "user", content: "Hello!" }],
2117
+ * { provider: "anthropic", name: "claude-3-5-sonnet-20241022" }
2118
+ * );
2119
+ * ```
2120
+ */
2121
+ async countTokens(messages, descriptor, _spec) {
2122
+ const client = this.client;
2123
+ const systemMessages = messages.filter((message) => message.role === "system");
2124
+ const system = systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n\n") : void 0;
2125
+ const conversation = messages.filter(
2126
+ (message) => message.role !== "system"
2127
+ ).map((message) => ({
2128
+ role: message.role,
2129
+ content: [
2130
+ {
2131
+ type: "text",
2132
+ text: message.content
2133
+ }
2134
+ ]
2135
+ }));
2136
+ try {
2137
+ const response = await client.messages.countTokens({
2138
+ model: descriptor.name,
2139
+ messages: conversation,
2140
+ ...system ? { system } : {}
2141
+ });
2142
+ return response.input_tokens;
2143
+ } catch (error) {
2144
+ console.warn(
2145
+ `Token counting failed for ${descriptor.name}, using fallback estimation:`,
2146
+ error
2147
+ );
2148
+ const totalChars = messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0);
2149
+ return Math.ceil(totalChars / FALLBACK_CHARS_PER_TOKEN);
2150
+ }
2151
+ }
2152
+ };
2153
+ }
2154
+ });
2155
+
2156
+ // src/providers/gemini-models.ts
2157
+ var GEMINI_MODELS;
2158
+ var init_gemini_models = __esm({
2159
+ "src/providers/gemini-models.ts"() {
2160
+ "use strict";
2161
+ GEMINI_MODELS = [
2162
+ {
2163
+ provider: "gemini",
2164
+ modelId: "gemini-3-pro-preview",
2165
+ displayName: "Gemini 3 Pro (Preview)",
2166
+ contextWindow: 1048576,
2167
+ maxOutputTokens: 65536,
2168
+ pricing: {
2169
+ input: 2,
2170
+ output: 12,
2171
+ cachedInput: 0.2
2172
+ },
2173
+ knowledgeCutoff: "2025-01",
2174
+ features: {
2175
+ streaming: true,
2176
+ functionCalling: true,
2177
+ vision: true,
2178
+ reasoning: true,
2179
+ structuredOutputs: true
2180
+ },
2181
+ metadata: {
2182
+ family: "Gemini 3",
2183
+ releaseDate: "2025-11-18",
2184
+ notes: "Most advanced model. 1501 Elo LMArena, 91.9% GPQA Diamond, 76.2% SWE-bench. Deep Think mode available."
2185
+ }
2186
+ },
2187
+ {
2188
+ provider: "gemini",
2189
+ modelId: "gemini-2.5-pro",
2190
+ displayName: "Gemini 2.5 Pro",
2191
+ contextWindow: 1048576,
2192
+ maxOutputTokens: 65536,
2193
+ pricing: {
2194
+ input: 1.25,
2195
+ output: 10,
2196
+ cachedInput: 0.125
2197
+ },
2198
+ knowledgeCutoff: "2025-01",
2199
+ features: {
2200
+ streaming: true,
2201
+ functionCalling: true,
2202
+ vision: true,
2203
+ reasoning: true,
2204
+ structuredOutputs: true
2205
+ },
2206
+ metadata: {
2207
+ family: "Gemini 2.5",
2208
+ releaseDate: "2025-06",
2209
+ notes: "Balanced multimodal model with 1M context. Best for complex agents and reasoning."
2210
+ }
2211
+ },
2212
+ {
2213
+ provider: "gemini",
2214
+ modelId: "gemini-2.5-flash",
2215
+ displayName: "Gemini 2.5 Flash",
2216
+ contextWindow: 1048576,
2217
+ maxOutputTokens: 65536,
2218
+ pricing: {
2219
+ input: 0.3,
2220
+ output: 2.5,
2221
+ cachedInput: 0.03
2222
+ },
2223
+ knowledgeCutoff: "2025-01",
2224
+ features: {
2225
+ streaming: true,
2226
+ functionCalling: true,
2227
+ vision: true,
2228
+ reasoning: true,
2229
+ structuredOutputs: true
2230
+ },
2231
+ metadata: {
2232
+ family: "Gemini 2.5",
2233
+ releaseDate: "2025-06",
2234
+ notes: "Best price-performance ratio with thinking enabled by default"
2235
+ }
2236
+ },
2237
+ {
2238
+ provider: "gemini",
2239
+ modelId: "gemini-2.5-flash-lite",
2240
+ displayName: "Gemini 2.5 Flash-Lite",
2241
+ contextWindow: 1048576,
2242
+ maxOutputTokens: 65536,
2243
+ pricing: {
2244
+ input: 0.1,
2245
+ output: 0.4,
2246
+ cachedInput: 0.01
2247
+ },
2248
+ knowledgeCutoff: "2025-01",
2249
+ features: {
2250
+ streaming: true,
2251
+ functionCalling: true,
2252
+ vision: true,
2253
+ structuredOutputs: true
2254
+ },
2255
+ metadata: {
2256
+ family: "Gemini 2.5",
2257
+ releaseDate: "2025-06",
2258
+ notes: "Fastest and most cost-efficient model for high-volume, low-latency tasks"
2259
+ }
2260
+ },
2261
+ {
2262
+ provider: "gemini",
2263
+ modelId: "gemini-2.0-flash",
2264
+ displayName: "Gemini 2.0 Flash",
2265
+ contextWindow: 1048576,
2266
+ maxOutputTokens: 8192,
2267
+ pricing: {
2268
+ input: 0.1,
2269
+ output: 0.4,
2270
+ cachedInput: 0.01
2271
+ },
2272
+ knowledgeCutoff: "2024-08",
2273
+ features: {
2274
+ streaming: true,
2275
+ functionCalling: true,
2276
+ vision: true,
2277
+ structuredOutputs: true
2278
+ },
2279
+ metadata: {
2280
+ family: "Gemini 2.0",
2281
+ notes: "Previous generation with 1M context and multimodal capabilities"
2282
+ }
2283
+ },
2284
+ {
2285
+ provider: "gemini",
2286
+ modelId: "gemini-2.0-flash-lite",
2287
+ displayName: "Gemini 2.0 Flash-Lite",
2288
+ contextWindow: 1048576,
2289
+ maxOutputTokens: 8192,
2290
+ pricing: {
2291
+ input: 0.075,
2292
+ output: 0.3,
2293
+ cachedInput: 75e-4
2294
+ },
2295
+ knowledgeCutoff: "2024-08",
2296
+ features: {
2297
+ streaming: true,
2298
+ functionCalling: true,
2299
+ vision: true,
2300
+ structuredOutputs: true
2301
+ },
2302
+ metadata: {
2303
+ family: "Gemini 2.0",
2304
+ notes: "Lightweight previous generation model for cost-sensitive applications"
2305
+ }
2306
+ }
2307
+ ];
2308
+ }
2309
+ });
2310
+
2311
+ // src/providers/gemini.ts
2312
+ import { FunctionCallingConfigMode, GoogleGenAI } from "@google/genai";
2313
+ function createGeminiProviderFromEnv() {
2314
+ return createProviderFromEnv("GEMINI_API_KEY", GoogleGenAI, GeminiGenerativeProvider);
2315
+ }
2316
+ var GEMINI_ROLE_MAP, GeminiGenerativeProvider;
2317
+ var init_gemini = __esm({
2318
+ "src/providers/gemini.ts"() {
2319
+ "use strict";
2320
+ init_base_provider();
2321
+ init_constants2();
2322
+ init_gemini_models();
2323
+ init_utils();
2324
+ GEMINI_ROLE_MAP = {
2325
+ system: "user",
2326
+ user: "user",
2327
+ assistant: "model"
2328
+ };
2329
+ GeminiGenerativeProvider = class extends BaseProviderAdapter {
2330
+ providerId = "gemini";
2331
+ supports(descriptor) {
2332
+ return descriptor.provider === this.providerId;
2333
+ }
2334
+ getModelSpecs() {
2335
+ return GEMINI_MODELS;
2336
+ }
2337
+ buildRequestPayload(options, descriptor, _spec, messages) {
2338
+ const { systemInstruction, contents } = this.extractSystemAndContents(messages);
2339
+ const generationConfig = this.buildGenerationConfig(options);
2340
+ const config = {
2341
+ ...systemInstruction ? { systemInstruction: systemInstruction.parts.map((p) => p.text).join("\n") } : {},
2342
+ ...generationConfig ? { ...generationConfig } : {},
2343
+ // Explicitly disable function calling to prevent UNEXPECTED_TOOL_CALL errors
2344
+ toolConfig: {
2345
+ functionCallingConfig: {
2346
+ mode: FunctionCallingConfigMode.NONE
2347
+ }
2348
+ },
2349
+ ...options.extra
2350
+ };
2351
+ return {
2352
+ model: descriptor.name,
2353
+ contents: this.convertContentsForNewSDK(contents),
2354
+ config
2355
+ };
2356
+ }
2357
+ async executeStreamRequest(payload) {
2358
+ const client = this.client;
2359
+ const streamResponse = await client.models.generateContentStream(payload);
2360
+ return streamResponse;
2361
+ }
2362
+ extractSystemAndContents(messages) {
2363
+ const firstSystemIndex = messages.findIndex((message) => message.role === "system");
2364
+ if (firstSystemIndex === -1) {
2365
+ return {
2366
+ systemInstruction: null,
2367
+ contents: this.mergeConsecutiveMessages(messages)
2368
+ };
2369
+ }
2370
+ let systemBlockEnd = firstSystemIndex;
2371
+ while (systemBlockEnd < messages.length && messages[systemBlockEnd].role === "system") {
2372
+ systemBlockEnd++;
2373
+ }
2374
+ const systemMessages = messages.slice(firstSystemIndex, systemBlockEnd);
2375
+ const nonSystemMessages = [
2376
+ ...messages.slice(0, firstSystemIndex),
2377
+ ...messages.slice(systemBlockEnd)
2378
+ ];
2379
+ const systemInstruction = {
2380
+ role: "system",
2381
+ parts: systemMessages.map((message) => ({ text: message.content }))
2382
+ };
2383
+ return {
2384
+ systemInstruction,
2385
+ contents: this.mergeConsecutiveMessages(nonSystemMessages)
2386
+ };
2387
+ }
2388
+ mergeConsecutiveMessages(messages) {
2389
+ if (messages.length === 0) {
2390
+ return [];
2391
+ }
2392
+ const result = [];
2393
+ let currentGroup = null;
2394
+ for (const message of messages) {
2395
+ const geminiRole = GEMINI_ROLE_MAP[message.role];
2396
+ if (currentGroup && currentGroup.role === geminiRole) {
2397
+ currentGroup.parts.push({ text: message.content });
2398
+ } else {
2399
+ if (currentGroup) {
2400
+ result.push(currentGroup);
2401
+ }
2402
+ currentGroup = {
2403
+ role: geminiRole,
2404
+ parts: [{ text: message.content }]
2405
+ };
2406
+ }
2407
+ }
2408
+ if (currentGroup) {
2409
+ result.push(currentGroup);
2410
+ }
2411
+ return result;
2412
+ }
2413
+ convertContentsForNewSDK(contents) {
2414
+ return contents.map((content) => ({
2415
+ role: content.role,
2416
+ parts: content.parts.map((part) => ({ text: part.text }))
2417
+ }));
2418
+ }
2419
+ buildGenerationConfig(options) {
2420
+ const config = {};
2421
+ if (typeof options.maxTokens === "number") {
2422
+ config.maxOutputTokens = options.maxTokens;
2423
+ }
2424
+ if (typeof options.temperature === "number") {
2425
+ config.temperature = options.temperature;
2426
+ }
2427
+ if (typeof options.topP === "number") {
2428
+ config.topP = options.topP;
2429
+ }
2430
+ if (options.stopSequences?.length) {
2431
+ config.stopSequences = options.stopSequences;
2432
+ }
2433
+ return Object.keys(config).length > 0 ? config : null;
2434
+ }
2435
+ async *wrapStream(iterable) {
2436
+ const stream2 = iterable;
2437
+ for await (const chunk of stream2) {
2438
+ const text = this.extractText(chunk);
2439
+ if (text) {
2440
+ yield { text, rawEvent: chunk };
2441
+ }
2442
+ const finishReason = this.extractFinishReason(chunk);
2443
+ const usage = this.extractUsage(chunk);
2444
+ if (finishReason || usage) {
2445
+ yield { text: "", finishReason, usage, rawEvent: chunk };
2446
+ }
2447
+ }
2448
+ }
2449
+ extractText(chunk) {
2450
+ if (!chunk?.candidates) {
2451
+ return "";
2452
+ }
2453
+ return chunk.candidates.flatMap((candidate) => candidate.content?.parts ?? []).map((part) => part.text ?? "").join("");
2454
+ }
2455
+ extractFinishReason(chunk) {
2456
+ const candidate = chunk?.candidates?.find((item) => item.finishReason);
2457
+ return candidate?.finishReason ?? null;
2458
+ }
2459
+ extractUsage(chunk) {
2460
+ const usageMetadata = chunk?.usageMetadata;
2461
+ if (!usageMetadata) {
2462
+ return void 0;
2463
+ }
2464
+ return {
2465
+ inputTokens: usageMetadata.promptTokenCount ?? 0,
2466
+ outputTokens: usageMetadata.candidatesTokenCount ?? 0,
2467
+ totalTokens: usageMetadata.totalTokenCount ?? 0
2468
+ };
2469
+ }
2470
+ /**
2471
+ * Count tokens in messages using Gemini's native token counting API.
2472
+ *
2473
+ * This method provides accurate token estimation for Gemini models by:
2474
+ * - Using the SDK's countTokens() method
2475
+ * - Properly extracting and handling system instructions
2476
+ * - Transforming messages to Gemini's expected format
2477
+ *
2478
+ * @param messages - The messages to count tokens for
2479
+ * @param descriptor - Model descriptor containing the model name
2480
+ * @param _spec - Optional model specification (currently unused)
2481
+ * @returns Promise resolving to the estimated input token count
2482
+ *
2483
+ * @throws Never throws - falls back to character-based estimation (4 chars/token) on error
2484
+ *
2485
+ * @example
2486
+ * ```typescript
2487
+ * const count = await provider.countTokens(
2488
+ * [{ role: "user", content: "Hello!" }],
2489
+ * { provider: "gemini", name: "gemini-1.5-pro" }
2490
+ * );
2491
+ * ```
2492
+ */
2493
+ async countTokens(messages, descriptor, _spec) {
2494
+ const client = this.client;
2495
+ const { systemInstruction, contents } = this.extractSystemAndContents(messages);
2496
+ const request = {
2497
+ model: descriptor.name,
2498
+ contents: this.convertContentsForNewSDK(contents)
2499
+ };
2500
+ if (systemInstruction) {
2501
+ request.systemInstruction = systemInstruction.parts.map((p) => p.text).join("\n");
2502
+ }
2503
+ try {
2504
+ const response = await client.models.countTokens(request);
2505
+ return response.totalTokens ?? 0;
2506
+ } catch (error) {
2507
+ console.warn(
2508
+ `Token counting failed for ${descriptor.name}, using fallback estimation:`,
2509
+ error
2510
+ );
2511
+ const totalChars = messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0);
2512
+ return Math.ceil(totalChars / FALLBACK_CHARS_PER_TOKEN);
2513
+ }
2514
+ }
2515
+ };
2516
+ }
2517
+ });
2518
+
2519
+ // src/providers/openai-models.ts
2520
+ var OPENAI_MODELS;
2521
+ var init_openai_models = __esm({
2522
+ "src/providers/openai-models.ts"() {
2523
+ "use strict";
2524
+ OPENAI_MODELS = [
2525
+ {
2526
+ provider: "openai",
2527
+ modelId: "gpt-5.1",
2528
+ displayName: "GPT-5.1 Instant",
2529
+ contextWindow: 128e3,
2530
+ maxOutputTokens: 32768,
2531
+ pricing: {
2532
+ input: 1.25,
2533
+ output: 10,
2534
+ cachedInput: 0.125
2535
+ },
2536
+ knowledgeCutoff: "2024-09-30",
2537
+ features: {
2538
+ streaming: true,
2539
+ functionCalling: true,
2540
+ vision: true,
2541
+ reasoning: true,
2542
+ structuredOutputs: true,
2543
+ fineTuning: true
2544
+ },
2545
+ metadata: {
2546
+ family: "GPT-5",
2547
+ releaseDate: "2025-11-12",
2548
+ notes: "Warmer, more intelligent, better instruction following. 2-3x faster than GPT-5.",
2549
+ supportsTemperature: false
2550
+ }
2551
+ },
2552
+ {
2553
+ provider: "openai",
2554
+ modelId: "gpt-5.1-thinking",
2555
+ displayName: "GPT-5.1 Thinking",
2556
+ contextWindow: 196e3,
2557
+ maxOutputTokens: 32768,
2558
+ pricing: {
2559
+ input: 1.25,
2560
+ output: 10,
2561
+ cachedInput: 0.125
2562
+ },
2563
+ knowledgeCutoff: "2024-09-30",
2564
+ features: {
2565
+ streaming: true,
2566
+ functionCalling: true,
2567
+ vision: true,
2568
+ reasoning: true,
2569
+ structuredOutputs: true,
2570
+ fineTuning: true
2571
+ },
2572
+ metadata: {
2573
+ family: "GPT-5",
2574
+ releaseDate: "2025-11-12",
2575
+ notes: "Advanced reasoning with thinking levels: Light, Standard, Extended, Heavy. Best for complex tasks.",
2576
+ supportsTemperature: false
2577
+ }
2578
+ },
2579
+ {
2580
+ provider: "openai",
2581
+ modelId: "gpt-5",
2582
+ displayName: "GPT-5",
2583
+ contextWindow: 272e3,
2584
+ maxOutputTokens: 128e3,
2585
+ pricing: {
2586
+ input: 1.25,
2587
+ output: 10,
2588
+ cachedInput: 0.125
2589
+ },
2590
+ knowledgeCutoff: "2024-09-30",
2591
+ features: {
2592
+ streaming: true,
2593
+ functionCalling: true,
2594
+ vision: true,
2595
+ reasoning: true,
2596
+ structuredOutputs: true,
2597
+ fineTuning: true
2598
+ },
2599
+ metadata: {
2600
+ family: "GPT-5",
2601
+ releaseDate: "2025-08-07",
2602
+ notes: "Best model for coding and agentic tasks. Adaptive reasoning with 90% caching discount.",
2603
+ supportsTemperature: false
2604
+ }
2605
+ },
2606
+ {
2607
+ provider: "openai",
2608
+ modelId: "gpt-5-mini",
2609
+ displayName: "GPT-5 Mini",
2610
+ contextWindow: 272e3,
2611
+ maxOutputTokens: 32768,
2612
+ pricing: {
2613
+ input: 0.25,
2614
+ output: 2,
2615
+ cachedInput: 0.025
2616
+ },
2617
+ knowledgeCutoff: "2024-06-01",
2618
+ features: {
2619
+ streaming: true,
2620
+ functionCalling: true,
2621
+ vision: true,
2622
+ structuredOutputs: true,
2623
+ fineTuning: true
2624
+ },
2625
+ metadata: {
2626
+ family: "GPT-5",
2627
+ notes: "Fast and cost-efficient with adaptive reasoning",
2628
+ supportsTemperature: false
2629
+ }
2630
+ },
2631
+ {
2632
+ provider: "openai",
2633
+ modelId: "gpt-5-nano",
2634
+ displayName: "GPT-5 Nano",
2635
+ contextWindow: 272e3,
2636
+ maxOutputTokens: 32768,
2637
+ pricing: {
2638
+ input: 0.05,
2639
+ output: 0.4,
2640
+ cachedInput: 5e-3
2641
+ },
2642
+ knowledgeCutoff: "2024-05-31",
2643
+ features: {
2644
+ streaming: true,
2645
+ functionCalling: true,
2646
+ vision: true,
2647
+ structuredOutputs: true,
2648
+ fineTuning: true
2649
+ },
2650
+ metadata: {
2651
+ family: "GPT-5",
2652
+ notes: "Fastest, most cost-efficient version for well-defined tasks",
2653
+ supportsTemperature: false
2654
+ }
2655
+ }
2656
+ ];
2657
+ }
2658
+ });
2659
+
2660
+ // src/providers/openai.ts
2661
+ import OpenAI from "openai";
2662
+ import { encoding_for_model } from "tiktoken";
2663
+ function sanitizeExtra(extra, allowTemperature) {
2664
+ if (!extra) {
2665
+ return void 0;
2666
+ }
2667
+ if (allowTemperature || !Object.hasOwn(extra, "temperature")) {
2668
+ return extra;
2669
+ }
2670
+ return Object.fromEntries(Object.entries(extra).filter(([key]) => key !== "temperature"));
2671
+ }
2672
+ function createOpenAIProviderFromEnv() {
2673
+ return createProviderFromEnv("OPENAI_API_KEY", OpenAI, OpenAIChatProvider);
2674
+ }
2675
+ var ROLE_MAP, OpenAIChatProvider;
2676
+ var init_openai = __esm({
2677
+ "src/providers/openai.ts"() {
2678
+ "use strict";
2679
+ init_base_provider();
2680
+ init_constants2();
2681
+ init_openai_models();
2682
+ init_utils();
2683
+ ROLE_MAP = {
2684
+ system: "system",
2685
+ user: "user",
2686
+ assistant: "assistant"
2687
+ };
2688
+ OpenAIChatProvider = class extends BaseProviderAdapter {
2689
+ providerId = "openai";
2690
+ supports(descriptor) {
2691
+ return descriptor.provider === this.providerId;
2692
+ }
2693
+ getModelSpecs() {
2694
+ return OPENAI_MODELS;
2695
+ }
2696
+ buildRequestPayload(options, descriptor, spec, messages) {
2697
+ const { maxTokens, temperature, topP, stopSequences, extra } = options;
2698
+ const supportsTemperature = spec?.metadata?.supportsTemperature !== false;
2699
+ const shouldIncludeTemperature = typeof temperature === "number" && supportsTemperature;
2700
+ const sanitizedExtra = sanitizeExtra(extra, shouldIncludeTemperature);
2701
+ return {
2702
+ model: descriptor.name,
2703
+ messages: messages.map((message) => ({
2704
+ role: ROLE_MAP[message.role],
2705
+ content: message.content,
2706
+ name: message.name
2707
+ })),
2708
+ // Only set max_completion_tokens if explicitly provided
2709
+ // Otherwise let the API use "as much as fits" in the context window
2710
+ ...maxTokens !== void 0 ? { max_completion_tokens: maxTokens } : {},
2711
+ top_p: topP,
2712
+ stop: stopSequences,
2713
+ stream: true,
2714
+ stream_options: { include_usage: true },
2715
+ ...sanitizedExtra ?? {},
2716
+ ...shouldIncludeTemperature ? { temperature } : {}
2717
+ };
2718
+ }
2719
+ async executeStreamRequest(payload) {
2720
+ const client = this.client;
2721
+ const stream2 = await client.chat.completions.create(payload);
2722
+ return stream2;
2723
+ }
2724
+ async *wrapStream(iterable) {
2725
+ const stream2 = iterable;
2726
+ for await (const chunk of stream2) {
2727
+ const text = chunk.choices.map((choice) => choice.delta?.content ?? "").join("");
2728
+ if (text) {
2729
+ yield { text, rawEvent: chunk };
2730
+ }
2731
+ const finishReason = chunk.choices.find((choice) => choice.finish_reason)?.finish_reason;
2732
+ const usage = chunk.usage ? {
2733
+ inputTokens: chunk.usage.prompt_tokens,
2734
+ outputTokens: chunk.usage.completion_tokens,
2735
+ totalTokens: chunk.usage.total_tokens
2736
+ } : void 0;
2737
+ if (finishReason || usage) {
2738
+ yield { text: "", finishReason, usage, rawEvent: chunk };
2739
+ }
2740
+ }
2741
+ }
2742
+ /**
2743
+ * Count tokens in messages using OpenAI's tiktoken library.
2744
+ *
2745
+ * This method provides accurate token estimation for OpenAI models by:
2746
+ * - Using the model-specific tokenizer encoding
2747
+ * - Accounting for message formatting overhead
2748
+ * - Falling back to gpt-4o encoding for unknown models
2749
+ *
2750
+ * @param messages - The messages to count tokens for
2751
+ * @param descriptor - Model descriptor containing the model name
2752
+ * @param _spec - Optional model specification (currently unused)
2753
+ * @returns Promise resolving to the estimated input token count
2754
+ *
2755
+ * @throws Never throws - falls back to character-based estimation (4 chars/token) on error
2756
+ *
2757
+ * @example
2758
+ * ```typescript
2759
+ * const count = await provider.countTokens(
2760
+ * [{ role: "user", content: "Hello!" }],
2761
+ * { provider: "openai", name: "gpt-4" }
2762
+ * );
2763
+ * ```
2764
+ */
2765
+ async countTokens(messages, descriptor, _spec) {
2766
+ try {
2767
+ const modelName = descriptor.name;
2768
+ let encoding;
2769
+ try {
2770
+ encoding = encoding_for_model(modelName);
2771
+ } catch {
2772
+ encoding = encoding_for_model("gpt-4o");
2773
+ }
2774
+ try {
2775
+ let tokenCount = 0;
2776
+ for (const message of messages) {
2777
+ tokenCount += OPENAI_MESSAGE_OVERHEAD_TOKENS;
2778
+ const roleText = ROLE_MAP[message.role];
2779
+ tokenCount += encoding.encode(roleText).length;
2780
+ tokenCount += encoding.encode(message.content ?? "").length;
2781
+ if (message.name) {
2782
+ tokenCount += encoding.encode(message.name).length;
2783
+ tokenCount += OPENAI_NAME_FIELD_OVERHEAD_TOKENS;
2784
+ }
2785
+ }
2786
+ tokenCount += OPENAI_REPLY_PRIMING_TOKENS;
2787
+ return tokenCount;
2788
+ } finally {
2789
+ encoding.free();
2790
+ }
2791
+ } catch (error) {
2792
+ console.warn(
2793
+ `Token counting failed for ${descriptor.name}, using fallback estimation:`,
2794
+ error
2795
+ );
2796
+ const totalChars = messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0);
2797
+ return Math.ceil(totalChars / FALLBACK_CHARS_PER_TOKEN);
2798
+ }
2799
+ }
2800
+ };
2801
+ }
2802
+ });
2803
+
2804
+ // src/providers/discovery.ts
2805
+ function discoverProviderAdapters() {
2806
+ const adapters = [];
2807
+ for (const discover of DISCOVERERS) {
2808
+ const adapter = discover();
2809
+ if (adapter) {
2810
+ adapters.push(adapter);
2811
+ }
2812
+ }
2813
+ return adapters;
2814
+ }
2815
+ var DISCOVERERS;
2816
+ var init_discovery = __esm({
2817
+ "src/providers/discovery.ts"() {
2818
+ "use strict";
2819
+ init_anthropic();
2820
+ init_gemini();
2821
+ init_openai();
2822
+ DISCOVERERS = [
2823
+ createOpenAIProviderFromEnv,
2824
+ createAnthropicProviderFromEnv,
2825
+ createGeminiProviderFromEnv
2826
+ ];
2827
+ }
2828
+ });
2829
+
2830
+ // src/core/model-registry.ts
2831
+ var ModelRegistry;
2832
+ var init_model_registry = __esm({
2833
+ "src/core/model-registry.ts"() {
2834
+ "use strict";
2835
+ ModelRegistry = class {
2836
+ modelSpecs = [];
2837
+ providerMap = /* @__PURE__ */ new Map();
2838
+ /**
2839
+ * Register a provider and collect its model specifications
2840
+ */
2841
+ registerProvider(provider) {
2842
+ const specs = provider.getModelSpecs?.() ?? [];
2843
+ if (specs.length > 0) {
2844
+ this.modelSpecs.push(...specs);
2845
+ this.providerMap.set(provider.providerId, specs);
2846
+ }
2847
+ }
2848
+ /**
2849
+ * Register a custom model specification at runtime
2850
+ *
2851
+ * Use this to add models that aren't in the built-in catalog, such as:
2852
+ * - Fine-tuned models with custom pricing
2853
+ * - New models not yet supported by llmist
2854
+ * - Custom deployments with different configurations
2855
+ *
2856
+ * @param spec - Complete model specification
2857
+ * @throws {Error} If spec is missing required fields
2858
+ *
2859
+ * @example
2860
+ * ```ts
2861
+ * client.modelRegistry.registerModel({
2862
+ * provider: "openai",
2863
+ * modelId: "ft:gpt-4o-2024-08-06:my-org:custom:abc123",
2864
+ * displayName: "My Fine-tuned GPT-4o",
2865
+ * contextWindow: 128_000,
2866
+ * maxOutputTokens: 16_384,
2867
+ * pricing: { input: 7.5, output: 30.0 },
2868
+ * knowledgeCutoff: "2024-08",
2869
+ * features: { streaming: true, functionCalling: true, vision: true }
2870
+ * });
2871
+ * ```
2872
+ */
2873
+ registerModel(spec) {
2874
+ if (!spec.modelId || !spec.provider) {
2875
+ throw new Error("ModelSpec must have modelId and provider");
2876
+ }
2877
+ const existing = this.getModelSpec(spec.modelId);
2878
+ if (existing) {
2879
+ console.warn(
2880
+ `[llmist] Overwriting existing model spec for "${spec.modelId}". Previous: ${existing.displayName}, New: ${spec.displayName}`
2881
+ );
2882
+ const index = this.modelSpecs.findIndex((m) => m.modelId === spec.modelId);
2883
+ if (index !== -1) {
2884
+ this.modelSpecs.splice(index, 1);
2885
+ }
2886
+ const providerSpecs2 = this.providerMap.get(spec.provider);
2887
+ if (providerSpecs2) {
2888
+ const providerIndex = providerSpecs2.findIndex((m) => m.modelId === spec.modelId);
2889
+ if (providerIndex !== -1) {
2890
+ providerSpecs2.splice(providerIndex, 1);
2891
+ }
2892
+ }
2893
+ }
2894
+ this.modelSpecs.push(spec);
2895
+ const providerSpecs = this.providerMap.get(spec.provider) ?? [];
2896
+ providerSpecs.push(spec);
2897
+ this.providerMap.set(spec.provider, providerSpecs);
2898
+ }
2899
+ /**
2900
+ * Register multiple custom model specifications at once
2901
+ *
2902
+ * @param specs - Array of complete model specifications
2903
+ *
2904
+ * @example
2905
+ * ```ts
2906
+ * client.modelRegistry.registerModels([
2907
+ * { provider: "openai", modelId: "gpt-5", ... },
2908
+ * { provider: "openai", modelId: "gpt-5-mini", ... }
2909
+ * ]);
2910
+ * ```
2911
+ */
2912
+ registerModels(specs) {
2913
+ for (const spec of specs) {
2914
+ this.registerModel(spec);
2915
+ }
2916
+ }
2917
+ /**
2918
+ * Get model specification by model ID
2919
+ * @param modelId - Full model identifier (e.g., 'gpt-5', 'claude-sonnet-4-5-20250929')
2920
+ * @returns ModelSpec if found, undefined otherwise
2921
+ */
2922
+ getModelSpec(modelId) {
2923
+ return this.modelSpecs.find((model) => model.modelId === modelId);
2924
+ }
2925
+ /**
2926
+ * List all models, optionally filtered by provider
2927
+ * @param providerId - Optional provider ID to filter by (e.g., 'openai', 'anthropic')
2928
+ * @returns Array of ModelSpec objects
2929
+ */
2930
+ listModels(providerId) {
2931
+ if (!providerId) {
2932
+ return [...this.modelSpecs];
2933
+ }
2934
+ return this.providerMap.get(providerId) ?? [];
2935
+ }
2936
+ /**
2937
+ * Get context window and output limits for a model
2938
+ * @param modelId - Full model identifier
2939
+ * @returns ModelLimits if model found, undefined otherwise
2940
+ */
2941
+ getModelLimits(modelId) {
2942
+ const spec = this.getModelSpec(modelId);
2943
+ if (!spec) return void 0;
2944
+ return {
2945
+ contextWindow: spec.contextWindow,
2946
+ maxOutputTokens: spec.maxOutputTokens
2947
+ };
2948
+ }
2949
+ /**
2950
+ * Estimate API cost for a given model and token usage
2951
+ * @param modelId - Full model identifier
2952
+ * @param inputTokens - Number of input tokens
2953
+ * @param outputTokens - Number of output tokens
2954
+ * @param useCachedInput - Whether to use cached input pricing (if supported by provider)
2955
+ * @returns CostEstimate if model found, undefined otherwise
2956
+ */
2957
+ estimateCost(modelId, inputTokens, outputTokens, useCachedInput = false) {
2958
+ const spec = this.getModelSpec(modelId);
2959
+ if (!spec) return void 0;
2960
+ const inputRate = useCachedInput && spec.pricing.cachedInput !== void 0 ? spec.pricing.cachedInput : spec.pricing.input;
2961
+ const inputCost = inputTokens / 1e6 * inputRate;
2962
+ const outputCost = outputTokens / 1e6 * spec.pricing.output;
2963
+ const totalCost = inputCost + outputCost;
2964
+ return {
2965
+ inputCost,
2966
+ outputCost,
2967
+ totalCost,
2968
+ currency: "USD"
2969
+ };
2970
+ }
2971
+ /**
2972
+ * Validate that requested token count fits within model limits
2973
+ * @param modelId - Full model identifier
2974
+ * @param requestedTokens - Total tokens requested (input + output)
2975
+ * @returns true if valid, false if model not found or exceeds limits
2976
+ */
2977
+ validateModelConfig(modelId, requestedTokens) {
2978
+ const limits = this.getModelLimits(modelId);
2979
+ if (!limits) return false;
2980
+ return requestedTokens <= limits.contextWindow;
2981
+ }
2982
+ /**
2983
+ * Check if a model supports a specific feature
2984
+ * @param modelId - Full model identifier
2985
+ * @param feature - Feature to check ('streaming', 'functionCalling', 'vision', etc.)
2986
+ * @returns true if model supports feature, false otherwise
2987
+ */
2988
+ supportsFeature(modelId, feature) {
2989
+ const spec = this.getModelSpec(modelId);
2990
+ if (!spec) return false;
2991
+ return spec.features[feature] === true;
2992
+ }
2993
+ /**
2994
+ * Get all models that support a specific feature
2995
+ * @param feature - Feature to filter by
2996
+ * @param providerId - Optional provider ID to filter by
2997
+ * @returns Array of ModelSpec objects that support the feature
2998
+ */
2999
+ getModelsByFeature(feature, providerId) {
3000
+ const models = this.listModels(providerId);
3001
+ return models.filter((model) => model.features[feature] === true);
3002
+ }
3003
+ /**
3004
+ * Get the most cost-effective model for a given provider and token budget
3005
+ * @param inputTokens - Expected input tokens
3006
+ * @param outputTokens - Expected output tokens
3007
+ * @param providerId - Optional provider ID to filter by
3008
+ * @returns ModelSpec with lowest total cost, or undefined if no models found
3009
+ */
3010
+ getCheapestModel(inputTokens, outputTokens, providerId) {
3011
+ const models = this.listModels(providerId);
3012
+ if (models.length === 0) return void 0;
3013
+ let cheapest;
3014
+ for (const model of models) {
3015
+ const estimate = this.estimateCost(model.modelId, inputTokens, outputTokens);
3016
+ if (!estimate) continue;
3017
+ if (!cheapest || estimate.totalCost < cheapest.cost) {
3018
+ cheapest = { model, cost: estimate.totalCost };
3019
+ }
3020
+ }
3021
+ return cheapest?.model;
3022
+ }
3023
+ };
3024
+ }
3025
+ });
3026
+
3027
+ // src/core/options.ts
3028
+ var ModelIdentifierParser;
3029
+ var init_options = __esm({
3030
+ "src/core/options.ts"() {
3031
+ "use strict";
3032
+ ModelIdentifierParser = class {
3033
+ constructor(defaultProvider = "openai") {
3034
+ this.defaultProvider = defaultProvider;
3035
+ }
3036
+ parse(identifier) {
3037
+ const trimmed = identifier.trim();
3038
+ if (!trimmed) {
3039
+ throw new Error("Model identifier cannot be empty");
3040
+ }
3041
+ const [maybeProvider, ...rest] = trimmed.split(":");
3042
+ if (rest.length === 0) {
3043
+ return { provider: this.defaultProvider, name: maybeProvider };
3044
+ }
3045
+ const provider = maybeProvider;
3046
+ const name = rest.join(":");
3047
+ if (!name) {
3048
+ throw new Error("Model name cannot be empty");
3049
+ }
3050
+ return { provider, name };
3051
+ }
3052
+ };
3053
+ }
3054
+ });
3055
+
3056
+ // src/core/quick-methods.ts
3057
+ async function complete(client, prompt, options = {}) {
3058
+ const model = resolveModel(options.model ?? "gpt-5-nano");
3059
+ const builder = new LLMMessageBuilder();
3060
+ if (options.systemPrompt) {
3061
+ builder.addSystem(options.systemPrompt);
3062
+ }
3063
+ builder.addUser(prompt);
3064
+ let fullResponse = "";
3065
+ for await (const chunk of client.stream({
3066
+ model,
3067
+ messages: builder.build(),
3068
+ temperature: options.temperature,
3069
+ maxTokens: options.maxTokens
3070
+ })) {
3071
+ fullResponse += chunk.text;
3072
+ }
3073
+ return fullResponse.trim();
3074
+ }
3075
+ async function* stream(client, prompt, options = {}) {
3076
+ const model = resolveModel(options.model ?? "gpt-5-nano");
3077
+ const builder = new LLMMessageBuilder();
3078
+ if (options.systemPrompt) {
3079
+ builder.addSystem(options.systemPrompt);
3080
+ }
3081
+ builder.addUser(prompt);
3082
+ for await (const chunk of client.stream({
3083
+ model,
3084
+ messages: builder.build(),
3085
+ temperature: options.temperature,
3086
+ maxTokens: options.maxTokens
3087
+ })) {
3088
+ yield chunk.text;
3089
+ }
3090
+ }
3091
+ var init_quick_methods = __esm({
3092
+ "src/core/quick-methods.ts"() {
3093
+ "use strict";
3094
+ init_messages();
3095
+ init_model_shortcuts();
3096
+ }
3097
+ });
3098
+
3099
+ // src/agent/agent-internal-key.ts
3100
+ function isValidAgentKey(key) {
3101
+ return key === AGENT_INTERNAL_KEY;
3102
+ }
3103
+ var AGENT_INTERNAL_KEY;
3104
+ var init_agent_internal_key = __esm({
3105
+ "src/agent/agent-internal-key.ts"() {
3106
+ "use strict";
3107
+ AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
3108
+ }
3109
+ });
3110
+
3111
+ // src/agent/agent.ts
3112
+ var Agent;
3113
+ var init_agent = __esm({
3114
+ "src/agent/agent.ts"() {
3115
+ "use strict";
3116
+ init_messages();
3117
+ init_model_shortcuts();
3118
+ init_logger();
3119
+ init_agent_internal_key();
3120
+ init_conversation_manager();
3121
+ init_event_handlers();
3122
+ init_hook_validators();
3123
+ init_stream_processor();
3124
+ Agent = class {
3125
+ client;
3126
+ model;
3127
+ maxIterations;
3128
+ temperature;
3129
+ logger;
3130
+ hooks;
3131
+ conversation;
3132
+ registry;
3133
+ parameterFormat;
3134
+ gadgetStartPrefix;
3135
+ gadgetEndPrefix;
3136
+ onHumanInputRequired;
3137
+ textOnlyHandler;
3138
+ stopOnGadgetError;
3139
+ shouldContinueAfterError;
3140
+ defaultGadgetTimeoutMs;
3141
+ defaultMaxTokens;
3142
+ userPromptProvided;
3143
+ /**
3144
+ * Creates a new Agent instance.
3145
+ * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
3146
+ */
3147
+ constructor(key, options) {
3148
+ if (!isValidAgentKey(key)) {
3149
+ throw new Error(
3150
+ "Agent cannot be instantiated directly. Use LLMist.createAgent() or new AgentBuilder() instead."
3151
+ );
3152
+ }
3153
+ this.client = options.client;
3154
+ this.model = resolveModel(options.model);
3155
+ this.maxIterations = options.maxIterations ?? 10;
3156
+ this.temperature = options.temperature;
3157
+ this.logger = options.logger ?? createLogger({ name: "llmist:agent" });
3158
+ this.hooks = options.hooks ?? {};
3159
+ this.registry = options.registry;
3160
+ this.parameterFormat = options.parameterFormat ?? "json";
3161
+ this.gadgetStartPrefix = options.gadgetStartPrefix;
3162
+ this.gadgetEndPrefix = options.gadgetEndPrefix;
3163
+ this.onHumanInputRequired = options.onHumanInputRequired;
3164
+ this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
3165
+ this.stopOnGadgetError = options.stopOnGadgetError ?? true;
3166
+ this.shouldContinueAfterError = options.shouldContinueAfterError;
3167
+ this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
3168
+ this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
3169
+ const baseBuilder = new LLMMessageBuilder(options.promptConfig);
3170
+ if (options.systemPrompt) {
3171
+ baseBuilder.addSystem(options.systemPrompt);
3172
+ }
3173
+ baseBuilder.addGadgets(this.registry.getAll(), this.parameterFormat, {
3174
+ startPrefix: options.gadgetStartPrefix,
3175
+ endPrefix: options.gadgetEndPrefix
3176
+ });
3177
+ const baseMessages = baseBuilder.build();
3178
+ const initialMessages = (options.initialMessages ?? []).map((message) => ({
3179
+ role: message.role,
3180
+ content: message.content
3181
+ }));
3182
+ this.conversation = new ConversationManager(
3183
+ baseMessages,
3184
+ initialMessages,
3185
+ this.parameterFormat
3186
+ );
3187
+ this.userPromptProvided = !!options.userPrompt;
3188
+ if (options.userPrompt) {
3189
+ this.conversation.addUserMessage(options.userPrompt);
3190
+ }
3191
+ }
3192
+ /**
3193
+ * Get the gadget registry for this agent.
3194
+ *
3195
+ * Useful for inspecting registered gadgets in tests or advanced use cases.
3196
+ *
3197
+ * @returns The GadgetRegistry instance
3198
+ *
3199
+ * @example
3200
+ * ```typescript
3201
+ * const agent = new AgentBuilder()
3202
+ * .withModel("sonnet")
3203
+ * .withGadgets(Calculator, Weather)
3204
+ * .build();
3205
+ *
3206
+ * // Inspect registered gadgets
3207
+ * console.log(agent.getRegistry().getNames()); // ['Calculator', 'Weather']
3208
+ * ```
3209
+ */
3210
+ getRegistry() {
3211
+ return this.registry;
3212
+ }
3213
+ /**
3214
+ * Run the agent loop.
3215
+ * Clean, simple orchestration - all complexity is in StreamProcessor.
3216
+ *
3217
+ * @throws {Error} If no user prompt was provided (when using build() without ask())
3218
+ */
3219
+ async *run() {
3220
+ if (!this.userPromptProvided) {
3221
+ throw new Error(
3222
+ "No user prompt provided. Use .ask(prompt) instead of .build(), or call agent.run() after providing a prompt."
3223
+ );
3224
+ }
3225
+ let currentIteration = 0;
3226
+ this.logger.info("Starting agent loop", {
3227
+ model: this.model,
3228
+ maxIterations: this.maxIterations
3229
+ });
3230
+ while (currentIteration < this.maxIterations) {
3231
+ this.logger.debug("Starting iteration", { iteration: currentIteration });
3232
+ try {
3233
+ let llmOptions = {
3234
+ model: this.model,
3235
+ messages: this.conversation.getMessages(),
3236
+ temperature: this.temperature,
3237
+ maxTokens: this.defaultMaxTokens
3238
+ };
3239
+ await this.safeObserve(async () => {
3240
+ if (this.hooks.observers?.onLLMCallStart) {
3241
+ const context = {
3242
+ iteration: currentIteration,
3243
+ options: llmOptions,
3244
+ logger: this.logger
3245
+ };
3246
+ await this.hooks.observers.onLLMCallStart(context);
3247
+ }
3248
+ });
3249
+ if (this.hooks.controllers?.beforeLLMCall) {
3250
+ const context = {
3251
+ iteration: currentIteration,
3252
+ options: llmOptions,
3253
+ logger: this.logger
3254
+ };
3255
+ const action = await this.hooks.controllers.beforeLLMCall(context);
3256
+ validateBeforeLLMCallAction(action);
3257
+ if (action.action === "skip") {
3258
+ this.logger.info("Controller skipped LLM call, using synthetic response");
3259
+ this.conversation.addAssistantMessage(action.syntheticResponse);
3260
+ yield { type: "text", content: action.syntheticResponse };
3261
+ break;
3262
+ } else if (action.action === "proceed" && action.modifiedOptions) {
3263
+ llmOptions = { ...llmOptions, ...action.modifiedOptions };
3264
+ }
3265
+ }
3266
+ this.logger.info("Calling LLM", { model: this.model });
3267
+ this.logger.silly("LLM request details", {
3268
+ model: llmOptions.model,
3269
+ temperature: llmOptions.temperature,
3270
+ maxTokens: llmOptions.maxTokens,
3271
+ messageCount: llmOptions.messages.length,
3272
+ messages: llmOptions.messages
3273
+ });
3274
+ const stream2 = this.client.stream(llmOptions);
3275
+ const processor = new StreamProcessor({
3276
+ iteration: currentIteration,
3277
+ registry: this.registry,
3278
+ parameterFormat: this.parameterFormat,
3279
+ gadgetStartPrefix: this.gadgetStartPrefix,
3280
+ gadgetEndPrefix: this.gadgetEndPrefix,
3281
+ hooks: this.hooks,
3282
+ logger: this.logger.getSubLogger({ name: "stream-processor" }),
3283
+ onHumanInputRequired: this.onHumanInputRequired,
3284
+ stopOnGadgetError: this.stopOnGadgetError,
3285
+ shouldContinueAfterError: this.shouldContinueAfterError,
3286
+ defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
3287
+ });
3288
+ const result = await processor.process(stream2);
3289
+ for (const output of result.outputs) {
3290
+ yield output;
3291
+ }
3292
+ this.logger.info("LLM response completed", {
3293
+ finishReason: result.finishReason,
3294
+ usage: result.usage,
3295
+ didExecuteGadgets: result.didExecuteGadgets
3296
+ });
3297
+ this.logger.silly("LLM response details", {
3298
+ rawResponse: result.rawResponse
3299
+ });
3300
+ await this.safeObserve(async () => {
3301
+ if (this.hooks.observers?.onLLMCallComplete) {
3302
+ const context = {
3303
+ iteration: currentIteration,
3304
+ options: llmOptions,
3305
+ finishReason: result.finishReason,
3306
+ usage: result.usage,
3307
+ rawResponse: result.rawResponse,
3308
+ finalMessage: result.finalMessage,
3309
+ logger: this.logger
3310
+ };
3311
+ await this.hooks.observers.onLLMCallComplete(context);
3312
+ }
3313
+ });
3314
+ let finalMessage = result.finalMessage;
3315
+ if (this.hooks.controllers?.afterLLMCall) {
3316
+ const context = {
3317
+ iteration: currentIteration,
3318
+ options: llmOptions,
3319
+ finishReason: result.finishReason,
3320
+ usage: result.usage,
3321
+ finalMessage: result.finalMessage,
3322
+ logger: this.logger
3323
+ };
3324
+ const action = await this.hooks.controllers.afterLLMCall(context);
3325
+ validateAfterLLMCallAction(action);
3326
+ if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
3327
+ finalMessage = action.modifiedMessage;
3328
+ }
3329
+ if (action.action === "append_messages" || action.action === "append_and_modify") {
3330
+ for (const msg of action.messages) {
3331
+ if (msg.role === "user") {
3332
+ this.conversation.addUserMessage(msg.content);
3333
+ } else if (msg.role === "assistant") {
3334
+ this.conversation.addAssistantMessage(msg.content);
3335
+ } else if (msg.role === "system") {
3336
+ this.conversation.addUserMessage(`[System] ${msg.content}`);
3337
+ }
3338
+ }
3339
+ }
3340
+ }
3341
+ if (result.didExecuteGadgets) {
3342
+ for (const output of result.outputs) {
3343
+ if (output.type === "gadget_result") {
3344
+ const gadgetResult = output.result;
3345
+ this.conversation.addGadgetCall(
3346
+ gadgetResult.gadgetName,
3347
+ gadgetResult.parameters,
3348
+ gadgetResult.error ?? gadgetResult.result ?? ""
3349
+ );
3350
+ }
3351
+ }
3352
+ } else {
3353
+ this.conversation.addAssistantMessage(finalMessage);
3354
+ const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
3355
+ if (shouldBreak) {
3356
+ break;
3357
+ }
3358
+ }
3359
+ if (result.shouldBreakLoop) {
3360
+ this.logger.info("Loop terminated by gadget or processor");
3361
+ break;
3362
+ }
3363
+ } catch (error) {
3364
+ const errorHandled = await this.handleLLMError(error, currentIteration);
3365
+ await this.safeObserve(async () => {
3366
+ if (this.hooks.observers?.onLLMCallError) {
3367
+ const context = {
3368
+ iteration: currentIteration,
3369
+ options: {
3370
+ model: this.model,
3371
+ messages: this.conversation.getMessages(),
3372
+ temperature: this.temperature,
3373
+ maxTokens: this.defaultMaxTokens
3374
+ },
3375
+ error,
3376
+ recovered: errorHandled,
3377
+ logger: this.logger
3378
+ };
3379
+ await this.hooks.observers.onLLMCallError(context);
3380
+ }
3381
+ });
3382
+ if (!errorHandled) {
3383
+ throw error;
3384
+ }
3385
+ }
3386
+ currentIteration++;
3387
+ }
3388
+ this.logger.info("Agent loop completed", {
3389
+ totalIterations: currentIteration,
3390
+ reason: currentIteration >= this.maxIterations ? "max_iterations" : "natural_completion"
3391
+ });
3392
+ }
3393
+ /**
3394
+ * Handle LLM error through controller.
3395
+ */
3396
+ async handleLLMError(error, iteration) {
3397
+ this.logger.error("LLM call failed", { error: error.message });
3398
+ if (this.hooks.controllers?.afterLLMError) {
3399
+ const context = {
3400
+ iteration,
3401
+ options: {
3402
+ model: this.model,
3403
+ messages: this.conversation.getMessages(),
3404
+ temperature: this.temperature,
3405
+ maxTokens: this.defaultMaxTokens
3406
+ },
3407
+ error,
3408
+ logger: this.logger
3409
+ };
3410
+ const action = await this.hooks.controllers.afterLLMError(context);
3411
+ validateAfterLLMErrorAction(action);
3412
+ if (action.action === "recover") {
3413
+ this.logger.info("Controller recovered from LLM error");
3414
+ this.conversation.addAssistantMessage(action.fallbackResponse);
3415
+ return true;
3416
+ }
3417
+ }
3418
+ return false;
3419
+ }
3420
+ /**
3421
+ * Handle text-only response (no gadgets called).
3422
+ */
3423
+ async handleTextOnlyResponse(_text) {
3424
+ const handler = this.textOnlyHandler;
3425
+ if (typeof handler === "string") {
3426
+ switch (handler) {
3427
+ case "terminate":
3428
+ this.logger.info("No gadgets called, ending loop");
3429
+ return true;
3430
+ case "acknowledge":
3431
+ this.logger.info("No gadgets called, continuing loop");
3432
+ return false;
3433
+ case "wait_for_input":
3434
+ this.logger.info("No gadgets called, waiting for input");
3435
+ return true;
3436
+ default:
3437
+ this.logger.warn(`Unknown text-only strategy: ${handler}, defaulting to terminate`);
3438
+ return true;
3439
+ }
3440
+ }
3441
+ return true;
3442
+ }
3443
+ /**
3444
+ * Safely execute an observer, catching and logging any errors.
3445
+ */
3446
+ async safeObserve(fn) {
3447
+ try {
3448
+ await fn();
3449
+ } catch (error) {
3450
+ this.logger.error("Observer threw error (ignoring)", {
3451
+ error: error instanceof Error ? error.message : String(error)
3452
+ });
3453
+ }
3454
+ }
3455
+ /**
3456
+ * Resolve max tokens from model catalog.
3457
+ */
3458
+ resolveMaxTokensFromCatalog(modelId) {
3459
+ const limits = this.client.modelRegistry.getModelLimits(modelId);
3460
+ if (limits?.maxOutputTokens !== void 0) {
3461
+ return limits.maxOutputTokens;
3462
+ }
3463
+ const separatorIndex = modelId.indexOf(":");
3464
+ if (separatorIndex === -1) {
3465
+ return void 0;
3466
+ }
3467
+ const unprefixedModelId = modelId.slice(separatorIndex + 1).trim();
3468
+ if (!unprefixedModelId) {
3469
+ return void 0;
3470
+ }
3471
+ return this.client.modelRegistry.getModelLimits(unprefixedModelId)?.maxOutputTokens;
3472
+ }
3473
+ /**
3474
+ * Run agent with named event handlers (syntactic sugar).
3475
+ *
3476
+ * Instead of verbose if/else chains, use named handlers for cleaner code.
3477
+ *
3478
+ * @param handlers - Named event handlers
3479
+ *
3480
+ * @example
3481
+ * ```typescript
3482
+ * await agent.runWith({
3483
+ * onText: (text) => console.log("LLM:", text),
3484
+ * onGadgetResult: (result) => console.log("Result:", result.result),
3485
+ * onGadgetCall: (call) => console.log("Calling:", call.gadgetName),
3486
+ * });
3487
+ * ```
3488
+ */
3489
+ async runWith(handlers) {
3490
+ return runWithHandlers(this.run(), handlers);
3491
+ }
3492
+ };
3493
+ }
3494
+ });
3495
+
3496
+ // src/agent/builder.ts
3497
+ var AgentBuilder;
3498
+ var init_builder = __esm({
3499
+ "src/agent/builder.ts"() {
3500
+ "use strict";
3501
+ init_model_shortcuts();
3502
+ init_registry();
3503
+ init_agent();
3504
+ init_agent_internal_key();
3505
+ init_event_handlers();
3506
+ AgentBuilder = class {
3507
+ client;
3508
+ model;
3509
+ systemPrompt;
3510
+ temperature;
3511
+ maxIterations;
3512
+ logger;
3513
+ hooks;
3514
+ promptConfig;
3515
+ gadgets = [];
3516
+ initialMessages = [];
3517
+ onHumanInputRequired;
3518
+ parameterFormat;
3519
+ gadgetStartPrefix;
3520
+ gadgetEndPrefix;
3521
+ textOnlyHandler;
3522
+ stopOnGadgetError;
3523
+ shouldContinueAfterError;
3524
+ defaultGadgetTimeoutMs;
3525
+ constructor(client) {
3526
+ this.client = client;
3527
+ }
3528
+ /**
3529
+ * Set the model to use.
3530
+ * Supports aliases like "gpt4", "sonnet", "flash".
3531
+ *
3532
+ * @param model - Model name or alias
3533
+ * @returns This builder for chaining
3534
+ *
3535
+ * @example
3536
+ * ```typescript
3537
+ * .withModel("sonnet") // Alias
3538
+ * .withModel("gpt-5-nano") // Auto-detects provider
3539
+ * .withModel("openai:gpt-5") // Explicit provider
3540
+ * ```
3541
+ */
3542
+ withModel(model) {
3543
+ this.model = resolveModel(model);
3544
+ return this;
3545
+ }
3546
+ /**
3547
+ * Set the system prompt.
3548
+ *
3549
+ * @param prompt - System prompt
3550
+ * @returns This builder for chaining
3551
+ */
3552
+ withSystem(prompt) {
3553
+ this.systemPrompt = prompt;
3554
+ return this;
3555
+ }
3556
+ /**
3557
+ * Set the temperature (0-1).
3558
+ *
3559
+ * @param temperature - Temperature value
3560
+ * @returns This builder for chaining
3561
+ */
3562
+ withTemperature(temperature) {
3563
+ this.temperature = temperature;
3564
+ return this;
3565
+ }
3566
+ /**
3567
+ * Set maximum iterations.
3568
+ *
3569
+ * @param max - Maximum number of iterations
3570
+ * @returns This builder for chaining
3571
+ */
3572
+ withMaxIterations(max) {
3573
+ this.maxIterations = max;
3574
+ return this;
3575
+ }
3576
+ /**
3577
+ * Set logger instance.
3578
+ *
3579
+ * @param logger - Logger instance
3580
+ * @returns This builder for chaining
3581
+ */
3582
+ withLogger(logger) {
3583
+ this.logger = logger;
3584
+ return this;
3585
+ }
3586
+ /**
3587
+ * Add hooks for agent lifecycle events.
3588
+ *
3589
+ * @param hooks - Agent hooks configuration
3590
+ * @returns This builder for chaining
3591
+ *
3592
+ * @example
3593
+ * ```typescript
3594
+ * import { HookPresets } from 'llmist/hooks';
3595
+ *
3596
+ * .withHooks(HookPresets.logging())
3597
+ * .withHooks(HookPresets.merge(
3598
+ * HookPresets.logging(),
3599
+ * HookPresets.timing()
3600
+ * ))
3601
+ * ```
3602
+ */
3603
+ withHooks(hooks) {
3604
+ this.hooks = hooks;
3605
+ return this;
3606
+ }
3607
+ /**
3608
+ * Configure custom prompts for gadget system messages.
3609
+ *
3610
+ * @param config - Prompt configuration object
3611
+ * @returns This builder for chaining
3612
+ *
3613
+ * @example
3614
+ * ```typescript
3615
+ * .withPromptConfig({
3616
+ * mainInstruction: "Use the gadget markers below:",
3617
+ * rules: ["Always use markers", "Never use function calling"]
3618
+ * })
3619
+ * ```
3620
+ */
3621
+ withPromptConfig(config) {
3622
+ this.promptConfig = config;
3623
+ return this;
3624
+ }
3625
+ /**
3626
+ * Add gadgets (classes or instances).
3627
+ * Can be called multiple times to add more gadgets.
3628
+ *
3629
+ * @param gadgets - Gadget classes or instances
3630
+ * @returns This builder for chaining
3631
+ *
3632
+ * @example
3633
+ * ```typescript
3634
+ * .withGadgets(Calculator, Weather, Email)
3635
+ * .withGadgets(new Calculator(), new Weather())
3636
+ * .withGadgets(createGadget({ ... }))
3637
+ * ```
3638
+ */
3639
+ withGadgets(...gadgets) {
3640
+ this.gadgets.push(...gadgets);
3641
+ return this;
3642
+ }
3643
+ /**
3644
+ * Add conversation history messages.
3645
+ * Useful for continuing previous conversations.
3646
+ *
3647
+ * @param messages - Array of history messages
3648
+ * @returns This builder for chaining
3649
+ *
3650
+ * @example
3651
+ * ```typescript
3652
+ * .withHistory([
3653
+ * { user: "Hello" },
3654
+ * { assistant: "Hi there!" },
3655
+ * { user: "How are you?" },
3656
+ * { assistant: "I'm doing well, thanks!" }
3657
+ * ])
3658
+ * ```
3659
+ */
3660
+ withHistory(messages) {
3661
+ for (const msg of messages) {
3662
+ if ("user" in msg) {
3663
+ this.initialMessages.push({ role: "user", content: msg.user });
3664
+ } else if ("assistant" in msg) {
3665
+ this.initialMessages.push({ role: "assistant", content: msg.assistant });
3666
+ } else if ("system" in msg) {
3667
+ this.initialMessages.push({ role: "system", content: msg.system });
3668
+ }
3669
+ }
3670
+ return this;
3671
+ }
3672
+ /**
3673
+ * Add a single message to the conversation history.
3674
+ *
3675
+ * @param message - Single history message
3676
+ * @returns This builder for chaining
3677
+ *
3678
+ * @example
3679
+ * ```typescript
3680
+ * .addMessage({ user: "Hello" })
3681
+ * .addMessage({ assistant: "Hi there!" })
3682
+ * ```
3683
+ */
3684
+ addMessage(message) {
3685
+ return this.withHistory([message]);
3686
+ }
3687
+ /**
3688
+ * Set the human input handler for interactive conversations.
3689
+ *
3690
+ * @param handler - Function to handle human input requests
3691
+ * @returns This builder for chaining
3692
+ *
3693
+ * @example
3694
+ * ```typescript
3695
+ * .onHumanInput(async (question) => {
3696
+ * return await promptUser(question);
3697
+ * })
3698
+ * ```
3699
+ */
3700
+ onHumanInput(handler) {
3701
+ this.onHumanInputRequired = handler;
3702
+ return this;
3703
+ }
3704
+ /**
3705
+ * Set the parameter format for gadget calls.
3706
+ *
3707
+ * @param format - Parameter format ("json" or "xml")
3708
+ * @returns This builder for chaining
3709
+ *
3710
+ * @example
3711
+ * ```typescript
3712
+ * .withParameterFormat("xml")
3713
+ * ```
3714
+ */
3715
+ withParameterFormat(format) {
3716
+ this.parameterFormat = format;
3717
+ return this;
3718
+ }
3719
+ /**
3720
+ * Set custom gadget marker prefix.
3721
+ *
3722
+ * @param prefix - Custom start prefix for gadget markers
3723
+ * @returns This builder for chaining
3724
+ *
3725
+ * @example
3726
+ * ```typescript
3727
+ * .withGadgetStartPrefix("<<GADGET_START>>")
3728
+ * ```
3729
+ */
3730
+ withGadgetStartPrefix(prefix) {
3731
+ this.gadgetStartPrefix = prefix;
3732
+ return this;
3733
+ }
3734
+ /**
3735
+ * Set custom gadget marker suffix.
3736
+ *
3737
+ * @param suffix - Custom end suffix for gadget markers
3738
+ * @returns This builder for chaining
3739
+ *
3740
+ * @example
3741
+ * ```typescript
3742
+ * .withGadgetEndPrefix("<<GADGET_END>>")
3743
+ * ```
3744
+ */
3745
+ withGadgetEndPrefix(suffix) {
3746
+ this.gadgetEndPrefix = suffix;
3747
+ return this;
3748
+ }
3749
+ /**
3750
+ * Set the text-only handler strategy.
3751
+ *
3752
+ * Controls what happens when the LLM returns text without calling any gadgets:
3753
+ * - "terminate": End the agent loop (default)
3754
+ * - "acknowledge": Continue the loop for another iteration
3755
+ * - "wait_for_input": Wait for human input
3756
+ * - Custom handler: Provide a function for dynamic behavior
3757
+ *
3758
+ * @param handler - Text-only handler strategy or custom handler
3759
+ * @returns This builder for chaining
3760
+ *
3761
+ * @example
3762
+ * ```typescript
3763
+ * // Simple strategy
3764
+ * .withTextOnlyHandler("acknowledge")
3765
+ *
3766
+ * // Custom handler
3767
+ * .withTextOnlyHandler({
3768
+ * type: "custom",
3769
+ * handler: async (context) => {
3770
+ * if (context.text.includes("?")) {
3771
+ * return { action: "wait_for_input", question: context.text };
3772
+ * }
3773
+ * return { action: "continue" };
3774
+ * }
3775
+ * })
3776
+ * ```
3777
+ */
3778
+ withTextOnlyHandler(handler) {
3779
+ this.textOnlyHandler = handler;
3780
+ return this;
3781
+ }
3782
+ /**
3783
+ * Set whether to stop gadget execution on first error.
3784
+ *
3785
+ * When true (default), if a gadget fails:
3786
+ * - Subsequent gadgets in the same response are skipped
3787
+ * - LLM stream is cancelled to save costs
3788
+ * - Agent loop continues with error in context
3789
+ *
3790
+ * When false:
3791
+ * - All gadgets in the response still execute
3792
+ * - LLM stream continues to completion
3793
+ *
3794
+ * @param stop - Whether to stop on gadget error
3795
+ * @returns This builder for chaining
3796
+ *
3797
+ * @example
3798
+ * ```typescript
3799
+ * .withStopOnGadgetError(false)
3800
+ * ```
3801
+ */
3802
+ withStopOnGadgetError(stop) {
3803
+ this.stopOnGadgetError = stop;
3804
+ return this;
3805
+ }
3806
+ /**
3807
+ * Set custom error handling logic.
3808
+ *
3809
+ * Provides fine-grained control over whether to continue after different types of errors.
3810
+ * Overrides `stopOnGadgetError` when provided.
3811
+ *
3812
+ * **Note:** This builder method configures the underlying `shouldContinueAfterError` option
3813
+ * in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
3814
+ * but maps to the `shouldContinueAfterError` property internally.
3815
+ *
3816
+ * @param handler - Function that decides whether to continue after an error.
3817
+ * Return `true` to continue execution, `false` to stop.
3818
+ * @returns This builder for chaining
3819
+ *
3820
+ * @example
3821
+ * ```typescript
3822
+ * .withErrorHandler((context) => {
3823
+ * // Stop on parse errors, continue on validation/execution errors
3824
+ * if (context.errorType === "parse") {
3825
+ * return false;
3826
+ * }
3827
+ * if (context.error.includes("CRITICAL")) {
3828
+ * return false;
3829
+ * }
3830
+ * return true;
3831
+ * })
3832
+ * ```
3833
+ */
3834
+ withErrorHandler(handler) {
3835
+ this.shouldContinueAfterError = handler;
3836
+ return this;
3837
+ }
3838
+ /**
3839
+ * Set default timeout for gadget execution.
3840
+ *
3841
+ * @param timeoutMs - Timeout in milliseconds (must be non-negative)
3842
+ * @returns This builder for chaining
3843
+ * @throws {Error} If timeout is negative
3844
+ *
3845
+ * @example
3846
+ * ```typescript
3847
+ * .withDefaultGadgetTimeout(5000) // 5 second timeout
3848
+ * ```
3849
+ */
3850
+ withDefaultGadgetTimeout(timeoutMs) {
3851
+ if (timeoutMs < 0) {
3852
+ throw new Error("Timeout must be a non-negative number");
3853
+ }
3854
+ this.defaultGadgetTimeoutMs = timeoutMs;
3855
+ return this;
3856
+ }
3857
+ /**
3858
+ * Build and create the agent with the given user prompt.
3859
+ * Returns the Agent instance ready to run.
3860
+ *
3861
+ * @param userPrompt - User's question or request
3862
+ * @returns Configured Agent instance
3863
+ *
3864
+ * @example
3865
+ * ```typescript
3866
+ * const agent = await LLMist.createAgent()
3867
+ * .withModel("sonnet")
3868
+ * .withGadgets(Calculator)
3869
+ * .ask("What is 2+2?");
3870
+ *
3871
+ * for await (const event of agent.run()) {
3872
+ * // handle events
3873
+ * }
3874
+ * ```
3875
+ */
3876
+ ask(userPrompt) {
3877
+ if (!this.client) {
3878
+ const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
3879
+ this.client = new LLMistClass();
3880
+ }
3881
+ const registry = GadgetRegistry.from(this.gadgets);
3882
+ const options = {
3883
+ client: this.client,
3884
+ model: this.model ?? "openai:gpt-5-nano",
3885
+ systemPrompt: this.systemPrompt,
3886
+ userPrompt,
3887
+ registry,
3888
+ maxIterations: this.maxIterations,
3889
+ temperature: this.temperature,
3890
+ logger: this.logger,
3891
+ hooks: this.hooks,
3892
+ promptConfig: this.promptConfig,
3893
+ initialMessages: this.initialMessages,
3894
+ onHumanInputRequired: this.onHumanInputRequired,
3895
+ parameterFormat: this.parameterFormat,
3896
+ gadgetStartPrefix: this.gadgetStartPrefix,
3897
+ gadgetEndPrefix: this.gadgetEndPrefix,
3898
+ textOnlyHandler: this.textOnlyHandler,
3899
+ stopOnGadgetError: this.stopOnGadgetError,
3900
+ shouldContinueAfterError: this.shouldContinueAfterError,
3901
+ defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
3902
+ };
3903
+ return new Agent(AGENT_INTERNAL_KEY, options);
3904
+ }
3905
+ /**
3906
+ * Build, run, and collect only the text response.
3907
+ * Convenient for simple queries where you just want the final answer.
3908
+ *
3909
+ * @param userPrompt - User's question or request
3910
+ * @returns Promise resolving to the complete text response
3911
+ *
3912
+ * @example
3913
+ * ```typescript
3914
+ * const answer = await LLMist.createAgent()
3915
+ * .withModel("gpt4-mini")
3916
+ * .withGadgets(Calculator)
3917
+ * .askAndCollect("What is 42 * 7?");
3918
+ *
3919
+ * console.log(answer); // "294"
3920
+ * ```
3921
+ */
3922
+ async askAndCollect(userPrompt) {
3923
+ const agent = this.ask(userPrompt);
3924
+ return collectText(agent.run());
3925
+ }
3926
+ /**
3927
+ * Build and run with event handlers.
3928
+ * Combines agent creation and event handling in one call.
3929
+ *
3930
+ * @param userPrompt - User's question or request
3931
+ * @param handlers - Event handlers
3932
+ *
3933
+ * @example
3934
+ * ```typescript
3935
+ * await LLMist.createAgent()
3936
+ * .withModel("sonnet")
3937
+ * .withGadgets(Calculator)
3938
+ * .askWith("What is 2+2?", {
3939
+ * onText: (text) => console.log("LLM:", text),
3940
+ * onGadgetResult: (result) => console.log("Result:", result.result),
3941
+ * });
3942
+ * ```
3943
+ */
3944
+ async askWith(userPrompt, handlers) {
3945
+ const agent = this.ask(userPrompt);
3946
+ await agent.runWith(handlers);
3947
+ }
3948
+ /**
3949
+ * Build the agent without a user prompt.
3950
+ *
3951
+ * Returns an Agent instance that can be inspected (e.g., check registered gadgets)
3952
+ * but cannot be run without first calling .ask(prompt).
3953
+ *
3954
+ * This is useful for:
3955
+ * - Testing: Inspect the registry, configuration, etc.
3956
+ * - Advanced use cases: Build agent configuration separately from execution
3957
+ *
3958
+ * @returns Configured Agent instance (without user prompt)
3959
+ *
3960
+ * @example
3961
+ * ```typescript
3962
+ * // Build agent for inspection
3963
+ * const agent = new AgentBuilder()
3964
+ * .withModel("sonnet")
3965
+ * .withGadgets(Calculator, Weather)
3966
+ * .build();
3967
+ *
3968
+ * // Inspect registered gadgets
3969
+ * console.log(agent.getRegistry().getNames()); // ['Calculator', 'Weather']
3970
+ *
3971
+ * // Note: Calling agent.run() will throw an error
3972
+ * // Use .ask(prompt) instead if you want to run the agent
3973
+ * ```
3974
+ */
3975
+ build() {
3976
+ if (!this.client) {
3977
+ const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
3978
+ this.client = new LLMistClass();
3979
+ }
3980
+ const registry = GadgetRegistry.from(this.gadgets);
3981
+ const options = {
3982
+ client: this.client,
3983
+ model: this.model ?? "openai:gpt-5-nano",
3984
+ systemPrompt: this.systemPrompt,
3985
+ // No userPrompt - agent.run() will throw if called directly
3986
+ registry,
3987
+ maxIterations: this.maxIterations,
3988
+ temperature: this.temperature,
3989
+ logger: this.logger,
3990
+ hooks: this.hooks,
3991
+ promptConfig: this.promptConfig,
3992
+ initialMessages: this.initialMessages,
3993
+ onHumanInputRequired: this.onHumanInputRequired,
3994
+ parameterFormat: this.parameterFormat,
3995
+ gadgetStartPrefix: this.gadgetStartPrefix,
3996
+ gadgetEndPrefix: this.gadgetEndPrefix,
3997
+ textOnlyHandler: this.textOnlyHandler,
3998
+ stopOnGadgetError: this.stopOnGadgetError,
3999
+ shouldContinueAfterError: this.shouldContinueAfterError,
4000
+ defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
4001
+ };
4002
+ return new Agent(AGENT_INTERNAL_KEY, options);
4003
+ }
4004
+ };
4005
+ }
4006
+ });
4007
+
4008
+ // src/core/client.ts
4009
+ var client_exports = {};
4010
+ __export(client_exports, {
4011
+ LLMist: () => LLMist
4012
+ });
4013
+ var LLMist;
4014
+ var init_client = __esm({
4015
+ "src/core/client.ts"() {
4016
+ "use strict";
4017
+ init_builder();
4018
+ init_discovery();
4019
+ init_model_registry();
4020
+ init_options();
4021
+ init_quick_methods();
4022
+ LLMist = class _LLMist {
4023
+ parser;
4024
+ modelRegistry;
4025
+ adapters;
4026
+ constructor(...args) {
4027
+ let adapters = [];
4028
+ let defaultProvider;
4029
+ let autoDiscoverProviders = true;
4030
+ let customModels = [];
4031
+ if (args.length === 0) {
4032
+ } else if (Array.isArray(args[0])) {
4033
+ adapters = args[0];
4034
+ if (args.length > 1) {
4035
+ defaultProvider = args[1];
4036
+ }
4037
+ } else if (typeof args[0] === "object" && args[0] !== null) {
4038
+ const options = args[0];
4039
+ adapters = options.adapters ?? [];
4040
+ defaultProvider = options.defaultProvider;
4041
+ customModels = options.customModels ?? [];
4042
+ if (typeof options.autoDiscoverProviders === "boolean") {
4043
+ autoDiscoverProviders = options.autoDiscoverProviders;
4044
+ }
4045
+ }
4046
+ const discoveredAdapters = autoDiscoverProviders ? discoverProviderAdapters() : [];
4047
+ const combinedAdapters = [...adapters];
4048
+ for (const adapter of discoveredAdapters) {
4049
+ if (!combinedAdapters.some((existing) => existing.providerId === adapter.providerId)) {
4050
+ combinedAdapters.push(adapter);
4051
+ }
4052
+ }
4053
+ if (combinedAdapters.length === 0) {
4054
+ throw new Error(
4055
+ "No LLM providers available. Provide adapters explicitly or set provider API keys in the environment."
4056
+ );
4057
+ }
4058
+ const resolvedDefaultProvider = defaultProvider ?? combinedAdapters[0]?.providerId ?? "openai";
4059
+ this.adapters = [...combinedAdapters].sort((a, b) => {
4060
+ const priorityA = a.priority ?? 0;
4061
+ const priorityB = b.priority ?? 0;
4062
+ return priorityB - priorityA;
4063
+ });
4064
+ this.parser = new ModelIdentifierParser(resolvedDefaultProvider);
4065
+ this.modelRegistry = new ModelRegistry();
4066
+ for (const adapter of this.adapters) {
4067
+ this.modelRegistry.registerProvider(adapter);
4068
+ }
4069
+ if (customModels.length > 0) {
4070
+ this.modelRegistry.registerModels(customModels);
4071
+ }
4072
+ }
4073
+ stream(options) {
4074
+ const descriptor = this.parser.parse(options.model);
4075
+ const spec = this.modelRegistry.getModelSpec(descriptor.name);
4076
+ const adapter = this.resolveAdapter(descriptor);
4077
+ return adapter.stream(options, descriptor, spec);
4078
+ }
4079
+ /**
4080
+ * Count tokens in messages for a given model.
4081
+ *
4082
+ * Uses provider-specific token counting methods for accurate estimation:
4083
+ * - OpenAI: tiktoken library with model-specific encodings
4084
+ * - Anthropic: Native messages.countTokens() API
4085
+ * - Gemini: SDK's countTokens() method
4086
+ *
4087
+ * Falls back to character-based estimation (4 chars/token) if the provider
4088
+ * doesn't support native token counting or if counting fails.
4089
+ *
4090
+ * This is useful for:
4091
+ * - Pre-request cost estimation
4092
+ * - Context window management
4093
+ * - Request batching optimization
4094
+ *
4095
+ * @param model - Model identifier (e.g., "openai:gpt-4", "anthropic:claude-3-5-sonnet-20241022")
4096
+ * @param messages - Array of messages to count tokens for
4097
+ * @returns Promise resolving to the estimated input token count
4098
+ *
4099
+ * @example
4100
+ * ```typescript
4101
+ * const client = new LLMist();
4102
+ * const messages = [
4103
+ * { role: 'system', content: 'You are a helpful assistant.' },
4104
+ * { role: 'user', content: 'Hello!' }
4105
+ * ];
4106
+ *
4107
+ * const tokenCount = await client.countTokens('openai:gpt-4', messages);
4108
+ * console.log(`Estimated tokens: ${tokenCount}`);
4109
+ * ```
4110
+ */
4111
+ async countTokens(model, messages) {
4112
+ const descriptor = this.parser.parse(model);
4113
+ const spec = this.modelRegistry.getModelSpec(descriptor.name);
4114
+ const adapter = this.resolveAdapter(descriptor);
4115
+ if (adapter.countTokens) {
4116
+ return adapter.countTokens(messages, descriptor, spec);
4117
+ }
4118
+ const totalChars = messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0);
4119
+ return Math.ceil(totalChars / 4);
4120
+ }
4121
+ resolveAdapter(descriptor) {
4122
+ const adapter = this.adapters.find((item) => item.supports(descriptor));
4123
+ if (!adapter) {
4124
+ throw new Error(`No adapter registered for provider ${descriptor.provider}`);
4125
+ }
4126
+ return adapter;
4127
+ }
4128
+ /**
4129
+ * Quick completion - returns final text response.
4130
+ * Convenient for simple queries without needing agent setup.
4131
+ *
4132
+ * @param prompt - User prompt
4133
+ * @param options - Optional configuration
4134
+ * @returns Complete text response
4135
+ *
4136
+ * @example
4137
+ * ```typescript
4138
+ * const answer = await LLMist.complete("What is 2+2?");
4139
+ * console.log(answer); // "4" or "2+2 equals 4"
4140
+ *
4141
+ * const answer = await LLMist.complete("Tell me a joke", {
4142
+ * model: "sonnet",
4143
+ * temperature: 0.9
4144
+ * });
4145
+ * ```
4146
+ */
4147
+ static async complete(prompt, options) {
4148
+ const client = new _LLMist();
4149
+ return complete(client, prompt, options);
4150
+ }
4151
+ /**
4152
+ * Quick streaming - returns async generator of text chunks.
4153
+ * Convenient for streaming responses without needing agent setup.
4154
+ *
4155
+ * @param prompt - User prompt
4156
+ * @param options - Optional configuration
4157
+ * @returns Async generator yielding text chunks
4158
+ *
4159
+ * @example
4160
+ * ```typescript
4161
+ * for await (const chunk of LLMist.stream("Tell me a story")) {
4162
+ * process.stdout.write(chunk);
4163
+ * }
4164
+ *
4165
+ * // With options
4166
+ * for await (const chunk of LLMist.stream("Generate code", {
4167
+ * model: "gpt4",
4168
+ * systemPrompt: "You are a coding assistant"
4169
+ * })) {
4170
+ * process.stdout.write(chunk);
4171
+ * }
4172
+ * ```
4173
+ */
4174
+ static stream(prompt, options) {
4175
+ const client = new _LLMist();
4176
+ return stream(client, prompt, options);
4177
+ }
4178
+ /**
4179
+ * Instance method: Quick completion using this client instance.
4180
+ *
4181
+ * @param prompt - User prompt
4182
+ * @param options - Optional configuration
4183
+ * @returns Complete text response
4184
+ */
4185
+ async complete(prompt, options) {
4186
+ return complete(this, prompt, options);
4187
+ }
4188
+ /**
4189
+ * Instance method: Quick streaming using this client instance.
4190
+ *
4191
+ * @param prompt - User prompt
4192
+ * @param options - Optional configuration
4193
+ * @returns Async generator yielding text chunks
4194
+ */
4195
+ streamText(prompt, options) {
4196
+ return stream(this, prompt, options);
4197
+ }
4198
+ /**
4199
+ * Create a fluent agent builder.
4200
+ * Provides a chainable API for configuring and creating agents.
4201
+ *
4202
+ * @returns AgentBuilder instance for chaining
4203
+ *
4204
+ * @example
4205
+ * ```typescript
4206
+ * const agent = LLMist.createAgent()
4207
+ * .withModel("sonnet")
4208
+ * .withSystem("You are a helpful assistant")
4209
+ * .withGadgets(Calculator, Weather)
4210
+ * .ask("What's the weather in Paris?");
4211
+ *
4212
+ * for await (const event of agent.run()) {
4213
+ * // handle events
4214
+ * }
4215
+ * ```
4216
+ *
4217
+ * @example
4218
+ * ```typescript
4219
+ * // Quick one-liner for simple queries
4220
+ * const answer = await LLMist.createAgent()
4221
+ * .withModel("gpt4-mini")
4222
+ * .askAndCollect("What is 2+2?");
4223
+ * ```
4224
+ */
4225
+ static createAgent() {
4226
+ return new AgentBuilder();
4227
+ }
4228
+ /**
4229
+ * Create agent builder with this client instance.
4230
+ * Useful when you want to reuse a configured client.
4231
+ *
4232
+ * @returns AgentBuilder instance using this client
4233
+ *
4234
+ * @example
4235
+ * ```typescript
4236
+ * const client = new LLMist({ ... });
4237
+ *
4238
+ * const agent = client.createAgent()
4239
+ * .withModel("sonnet")
4240
+ * .ask("Hello");
4241
+ * ```
4242
+ */
4243
+ createAgent() {
4244
+ return new AgentBuilder(this);
4245
+ }
4246
+ };
4247
+ }
4248
+ });
4249
+
4250
+ // src/gadgets/gadget.ts
4251
+ import * as yaml2 from "js-yaml";
4252
+
4253
+ // src/gadgets/schema-to-json.ts
4254
+ init_logger();
4255
+ import * as z2 from "zod";
4256
+ function schemaToJSONSchema(schema, options) {
4257
+ const jsonSchema = z2.toJSONSchema(schema, options ?? { target: "draft-7" });
4258
+ const mismatches = detectDescriptionMismatch(schema, jsonSchema);
4259
+ if (mismatches.length > 0) {
4260
+ defaultLogger.warn(
4261
+ `Zod instance mismatch detected: ${mismatches.length} description(s) lost. For best results, use: import { z } from "llmist"`
4262
+ );
4263
+ return mergeDescriptions(schema, jsonSchema);
4264
+ }
4265
+ return jsonSchema;
4266
+ }
4267
+ function detectDescriptionMismatch(schema, jsonSchema) {
4268
+ const mismatches = [];
4269
+ function checkSchema(zodSchema, json, path) {
4270
+ if (!zodSchema || typeof zodSchema !== "object") return;
4271
+ const def = zodSchema._def;
4272
+ const jsonObj = json;
4273
+ if (def?.description && !jsonObj?.description) {
4274
+ mismatches.push(path || "root");
4275
+ }
4276
+ if (def?.typeName === "ZodObject" && def?.shape) {
4277
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
4278
+ for (const [key, fieldSchema] of Object.entries(shape)) {
4279
+ const properties = jsonObj?.properties;
4280
+ const jsonProp = properties?.[key];
4281
+ checkSchema(fieldSchema, jsonProp, path ? `${path}.${key}` : key);
4282
+ }
4283
+ }
4284
+ if (def?.typeName === "ZodArray" && def?.type) {
4285
+ checkSchema(def.type, jsonObj?.items, path ? `${path}[]` : "[]");
4286
+ }
4287
+ if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
4288
+ checkSchema(def.innerType, json, path);
4289
+ }
4290
+ if (def?.typeName === "ZodDefault" && def?.innerType) {
4291
+ checkSchema(def.innerType, json, path);
4292
+ }
4293
+ }
4294
+ checkSchema(schema, jsonSchema, "");
4295
+ return mismatches;
4296
+ }
4297
+ function mergeDescriptions(schema, jsonSchema) {
4298
+ function merge(zodSchema, json) {
4299
+ if (!json || typeof json !== "object") return json;
4300
+ const def = zodSchema._def;
4301
+ const jsonObj = json;
4302
+ const merged = { ...jsonObj };
4303
+ if (def?.description && !jsonObj.description) {
4304
+ merged.description = def.description;
4305
+ }
4306
+ if (def?.typeName === "ZodObject" && def?.shape && jsonObj.properties) {
4307
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
4308
+ const properties = jsonObj.properties;
4309
+ merged.properties = { ...properties };
4310
+ for (const [key, fieldSchema] of Object.entries(shape)) {
4311
+ if (properties[key]) {
4312
+ merged.properties[key] = merge(fieldSchema, properties[key]);
4313
+ }
4314
+ }
4315
+ }
4316
+ if (def?.typeName === "ZodArray" && def?.type && jsonObj.items) {
4317
+ merged.items = merge(def.type, jsonObj.items);
4318
+ }
4319
+ if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
4320
+ return merge(def.innerType, json);
4321
+ }
4322
+ if (def?.typeName === "ZodDefault" && def?.innerType) {
4323
+ return merge(def.innerType, json);
4324
+ }
4325
+ return merged;
4326
+ }
4327
+ return merge(schema, jsonSchema);
4328
+ }
4329
+
4330
+ // src/gadgets/gadget.ts
4331
+ init_schema_validator();
4332
+ var BaseGadget = class {
4333
+ /**
4334
+ * The name of the gadget. Used for identification when LLM calls it.
4335
+ * If not provided, defaults to the class name.
4336
+ */
4337
+ name;
4338
+ /**
4339
+ * Optional Zod schema describing the expected input payload. When provided,
4340
+ * it will be validated before execution and transformed into a JSON Schema
4341
+ * representation that is surfaced to the LLM as part of the instructions.
4342
+ */
4343
+ parameterSchema;
4344
+ /**
4345
+ * Optional timeout in milliseconds for gadget execution.
4346
+ * If execution exceeds this timeout, a TimeoutException will be thrown.
4347
+ * If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
4348
+ * Set to 0 or undefined to disable timeout for this gadget.
4349
+ */
4350
+ timeoutMs;
4351
+ /**
4352
+ * Auto-generated instruction text for the LLM.
4353
+ * Combines name, description, and parameter schema into a formatted instruction.
4354
+ * @deprecated Use getInstruction(format) instead for format-specific schemas
4355
+ */
4356
+ get instruction() {
4357
+ return this.getInstruction("yaml");
4358
+ }
4359
+ /**
4360
+ * Generate instruction text for the LLM with format-specific schema.
4361
+ * Combines name, description, and parameter schema into a formatted instruction.
4362
+ *
4363
+ * @param format - Format for the schema representation ('json' | 'yaml' | 'auto')
4364
+ * @returns Formatted instruction string
4365
+ */
4366
+ getInstruction(format = "json") {
4367
+ const parts = [];
4368
+ parts.push(this.description);
4369
+ if (this.parameterSchema) {
4370
+ const gadgetName = this.name ?? this.constructor.name;
4371
+ validateGadgetSchema(this.parameterSchema, gadgetName);
4372
+ const jsonSchema = schemaToJSONSchema(this.parameterSchema, {
4373
+ target: "draft-7"
4374
+ });
4375
+ if (format === "json" || format === "auto") {
4376
+ parts.push("\n\nInput Schema (JSON):");
4377
+ parts.push(JSON.stringify(jsonSchema, null, 2));
4378
+ } else {
4379
+ const yamlSchema = yaml2.dump(jsonSchema).trimEnd();
4380
+ parts.push("\n\nInput Schema (YAML):");
4381
+ parts.push(yamlSchema);
4382
+ }
4383
+ }
4384
+ return parts.join("\n");
4385
+ }
4386
+ };
4387
+
4388
+ export {
4389
+ MODEL_ALIASES,
4390
+ resolveModel,
4391
+ hasProviderPrefix,
4392
+ getProvider,
4393
+ getModelId,
4394
+ init_model_shortcuts,
4395
+ GadgetRegistry,
4396
+ init_registry,
4397
+ GADGET_START_PREFIX,
4398
+ GADGET_END_PREFIX,
4399
+ init_constants,
4400
+ DEFAULT_PROMPTS,
4401
+ resolvePromptTemplate,
4402
+ resolveRulesTemplate,
4403
+ init_prompt_config,
4404
+ LLMMessageBuilder,
4405
+ init_messages,
4406
+ createLogger,
4407
+ defaultLogger,
4408
+ init_logger,
4409
+ ConversationManager,
4410
+ init_conversation_manager,
4411
+ runWithHandlers,
4412
+ collectEvents,
4413
+ collectText,
4414
+ init_event_handlers,
4415
+ BreakLoopException,
4416
+ HumanInputException,
4417
+ init_exceptions,
4418
+ GadgetExecutor,
4419
+ init_executor,
4420
+ StreamParser,
4421
+ init_parser,
4422
+ StreamProcessor,
4423
+ init_stream_processor,
4424
+ FALLBACK_CHARS_PER_TOKEN,
4425
+ init_constants2,
4426
+ AnthropicMessagesProvider,
4427
+ createAnthropicProviderFromEnv,
4428
+ init_anthropic,
4429
+ GeminiGenerativeProvider,
4430
+ createGeminiProviderFromEnv,
4431
+ init_gemini,
4432
+ OpenAIChatProvider,
4433
+ createOpenAIProviderFromEnv,
4434
+ init_openai,
4435
+ discoverProviderAdapters,
4436
+ init_discovery,
4437
+ ModelRegistry,
4438
+ init_model_registry,
4439
+ ModelIdentifierParser,
4440
+ init_options,
4441
+ complete,
4442
+ stream,
4443
+ init_quick_methods,
4444
+ LLMist,
4445
+ init_client,
4446
+ AgentBuilder,
4447
+ init_builder,
4448
+ BaseGadget
4449
+ };
4450
+ //# sourceMappingURL=chunk-TP7HE3MN.js.map