llm-exe 2.3.11 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -55,6 +55,11 @@ var ExecutorExecutionMetadataState = class {
55
55
  output: void 0,
56
56
  errorMessage: void 0,
57
57
  error: void 0,
58
+ errorCategory: void 0,
59
+ errorCode: void 0,
60
+ errorContext: void 0,
61
+ errorCause: void 0,
62
+ hookErrors: void 0,
58
63
  metadata: null
59
64
  });
60
65
  if (items) {
@@ -82,6 +87,11 @@ var ExecutorExecutionMetadataState = class {
82
87
  output: __privateGet(this, _state).output,
83
88
  errorMessage: __privateGet(this, _state).errorMessage,
84
89
  error: __privateGet(this, _state).error,
90
+ errorCategory: __privateGet(this, _state).errorCategory,
91
+ errorCode: __privateGet(this, _state).errorCode,
92
+ errorContext: __privateGet(this, _state).errorContext,
93
+ errorCause: __privateGet(this, _state).errorCause,
94
+ hookErrors: __privateGet(this, _state).hookErrors,
85
95
  metadata: __privateGet(this, _state).metadata
86
96
  });
87
97
  }
@@ -96,6 +106,653 @@ var hookOnComplete = `onComplete`;
96
106
  var hookOnError = `onError`;
97
107
  var hookOnSuccess = `onSuccess`;
98
108
 
109
+ // src/errors/knownCodes.ts
110
+ var ALL_CODES = [
111
+ "configuration.missing_provider",
112
+ "configuration.invalid_provider",
113
+ "configuration.missing_env",
114
+ "configuration.missing_option",
115
+ "configuration.invalid_headers",
116
+ "parser.invalid_type",
117
+ "parser.invalid_input",
118
+ "parser.parse_failed",
119
+ "parser.schema_validation_failed",
120
+ "prompt.missing_input",
121
+ "prompt.invalid_messages",
122
+ "prompt.missing_template_variable",
123
+ "llm.provider_http_error",
124
+ "llm.provider_rate_limited",
125
+ "llm.provider_auth_failed",
126
+ "llm.provider_invalid_request",
127
+ "llm.provider_unavailable",
128
+ "llm.invalid_response_shape",
129
+ "llm.invalid_jsonl_response",
130
+ "embedding.provider_http_error",
131
+ "embedding.provider_rate_limited",
132
+ "embedding.provider_auth_failed",
133
+ "embedding.provider_invalid_request",
134
+ "embedding.provider_unavailable",
135
+ "embedding.missing_provider",
136
+ "embedding.invalid_provider",
137
+ "embedding.unsupported_dimensions",
138
+ "embedding.invalid_response_shape",
139
+ "executor.missing_prompt",
140
+ "executor.hook_limit_reached",
141
+ "executor.hook_failed",
142
+ "callable.invalid_handler",
143
+ "callable.handler_not_found",
144
+ "callable.validation_failed",
145
+ "template.invalid_helper_arguments",
146
+ "state.invalid_arguments",
147
+ "auth.aws_signing_input_missing",
148
+ "request.invalid_url",
149
+ "request.http_error",
150
+ "internal.invariant_failed",
151
+ "unknown.unclassified"
152
+ ];
153
+ var KNOWN_ERROR_CODES = new Set(ALL_CODES);
154
+
155
+ // src/errors/serialize.ts
156
+ var MAX_VALUE_DEPTH = 5;
157
+ var MAX_CAUSE_DEPTH = 5;
158
+ var CIRCULAR = "[Circular]";
159
+ var SYMBOL = Symbol.for("llm-exe.error");
160
+ function isLlmExeErrorLike(value) {
161
+ if (!value || typeof value !== "object") return false;
162
+ return value[SYMBOL] === true || value.isLlmExeError === true;
163
+ }
164
+ function isErrorLike(value) {
165
+ if (value instanceof Error) return true;
166
+ if (!value || typeof value !== "object") return false;
167
+ const v = value;
168
+ return typeof v.message === "string" && typeof v.name === "string";
169
+ }
170
+ function isResponseLike(value) {
171
+ if (!value || typeof value !== "object") return false;
172
+ if (typeof Response !== "undefined" && value instanceof Response) return true;
173
+ return false;
174
+ }
175
+ function describeOpaque(value) {
176
+ const tag = Object.prototype.toString.call(value).slice(8, -1);
177
+ if (tag === "Object") return "[object Object]";
178
+ return `[object ${tag}]`;
179
+ }
180
+ function safeValue(value, seen, depth) {
181
+ if (value === null) return null;
182
+ if (value === void 0) return null;
183
+ const t = typeof value;
184
+ if (t === "string") return value;
185
+ if (t === "boolean") return value;
186
+ if (t === "number") {
187
+ const n = value;
188
+ return Number.isFinite(n) ? n : null;
189
+ }
190
+ if (t === "bigint") return value.toString();
191
+ if (t === "symbol") return String(value);
192
+ if (t === "function") {
193
+ const name = value.name;
194
+ return name ? `[Function ${name}]` : "[Function]";
195
+ }
196
+ if (depth >= MAX_VALUE_DEPTH) {
197
+ return describeOpaque(value);
198
+ }
199
+ if (seen.has(value)) return CIRCULAR;
200
+ seen.add(value);
201
+ try {
202
+ if (Array.isArray(value)) {
203
+ const arr = [];
204
+ for (let i = 0; i < value.length; i++) {
205
+ arr.push(safeValue(value[i], seen, depth + 1));
206
+ }
207
+ return arr;
208
+ }
209
+ if (value instanceof Date) {
210
+ const ts = value.getTime();
211
+ return Number.isFinite(ts) ? new Date(ts).toISOString() : null;
212
+ }
213
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value)) {
214
+ return `[Buffer length=${value.length}]`;
215
+ }
216
+ if (value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
217
+ return describeOpaque(value);
218
+ }
219
+ if (isResponseLike(value)) {
220
+ return {
221
+ name: "Response",
222
+ status: value.status,
223
+ statusText: value.statusText ?? "",
224
+ url: value.url ?? ""
225
+ };
226
+ }
227
+ const proto = Object.getPrototypeOf(value);
228
+ if (proto !== null && proto !== Object.prototype) {
229
+ const ctor = value.constructor;
230
+ if (ctor && ctor.name && ctor.name !== "Object") {
231
+ const out2 = {};
232
+ for (const key of Object.keys(value)) {
233
+ const v = value[key];
234
+ if (v === void 0) continue;
235
+ out2[key] = safeValue(v, seen, depth + 1);
236
+ }
237
+ return Object.keys(out2).length > 0 ? out2 : describeOpaque(value);
238
+ }
239
+ }
240
+ const out = {};
241
+ for (const key of Object.keys(value)) {
242
+ const v = value[key];
243
+ if (v === void 0) continue;
244
+ out[key] = safeValue(v, seen, depth + 1);
245
+ }
246
+ return out;
247
+ } finally {
248
+ seen.delete(value);
249
+ }
250
+ }
251
+ function freshSafeValue(value) {
252
+ return safeValue(value, /* @__PURE__ */ new WeakSet(), 0);
253
+ }
254
+ function serializeOne(error, errorSeen, causeDepth, options) {
255
+ if (error === null || error === void 0) return null;
256
+ if (typeof error !== "object") return freshSafeValue(error);
257
+ if (errorSeen.has(error)) return CIRCULAR;
258
+ errorSeen.add(error);
259
+ try {
260
+ if (isLlmExeErrorLike(error)) {
261
+ const e = error;
262
+ const out = {
263
+ name: "LlmExeError",
264
+ message: typeof e.message === "string" ? e.message : "",
265
+ category: typeof e.category === "string" ? e.category : "unknown",
266
+ code: typeof e.code === "string" ? e.code : "unknown.unclassified"
267
+ };
268
+ if (e.context !== void 0) {
269
+ out.context = freshSafeValue(e.context);
270
+ }
271
+ if (options.includeStack && typeof e.stack === "string") {
272
+ out.stack = e.stack;
273
+ }
274
+ if (e.cause !== void 0) {
275
+ if (causeDepth + 1 >= MAX_CAUSE_DEPTH) {
276
+ out.cause = { truncated: true };
277
+ } else {
278
+ out.cause = serializeOne(e.cause, errorSeen, causeDepth + 1, options);
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+ if (isResponseLike(error)) {
284
+ return {
285
+ name: "Response",
286
+ status: error.status,
287
+ statusText: error.statusText ?? "",
288
+ url: error.url ?? ""
289
+ };
290
+ }
291
+ if (isErrorLike(error)) {
292
+ const e = error;
293
+ const out = {
294
+ name: typeof e.name === "string" && e.name ? e.name : "Error",
295
+ message: typeof e.message === "string" ? e.message : ""
296
+ };
297
+ if (options.includeStack && typeof e.stack === "string") {
298
+ out.stack = e.stack;
299
+ }
300
+ if (e.cause !== void 0) {
301
+ if (causeDepth + 1 >= MAX_CAUSE_DEPTH) {
302
+ out.cause = { truncated: true };
303
+ } else {
304
+ out.cause = serializeOne(e.cause, errorSeen, causeDepth + 1, options);
305
+ }
306
+ }
307
+ return out;
308
+ }
309
+ return freshSafeValue(error);
310
+ } finally {
311
+ errorSeen.delete(error);
312
+ }
313
+ }
314
+ function serializeLlmExeError(error, options) {
315
+ return serializeOne(error, /* @__PURE__ */ new WeakSet(), 0, options ?? {});
316
+ }
317
+
318
+ // src/errors/LlmExeError.ts
319
+ var LLM_EXE_ERROR_SYMBOL = Symbol.for("llm-exe.error");
320
+ function deriveCategory(code) {
321
+ if (typeof code !== "string") return void 0;
322
+ if (!KNOWN_ERROR_CODES.has(code)) return void 0;
323
+ const dot = code.indexOf(".");
324
+ return code.slice(0, dot);
325
+ }
326
+ var LlmExeError = class _LlmExeError extends Error {
327
+ constructor(message, options) {
328
+ super(message);
329
+ __publicField(this, "category");
330
+ __publicField(this, "code");
331
+ __publicField(this, "context");
332
+ Object.defineProperties(this, {
333
+ name: {
334
+ value: "LlmExeError",
335
+ configurable: true,
336
+ writable: false,
337
+ enumerable: false
338
+ },
339
+ isLlmExeError: {
340
+ value: true,
341
+ configurable: false,
342
+ writable: false,
343
+ enumerable: false
344
+ },
345
+ [LLM_EXE_ERROR_SYMBOL]: {
346
+ value: true,
347
+ configurable: false,
348
+ writable: false,
349
+ enumerable: false
350
+ }
351
+ });
352
+ const providedCode = options ? options.code : void 0;
353
+ const category = deriveCategory(providedCode);
354
+ if (!category) {
355
+ this.code = "internal.invariant_failed";
356
+ this.category = "internal";
357
+ this.context = {
358
+ invariant: "invalid_error_code",
359
+ received: providedCode
360
+ };
361
+ } else {
362
+ this.code = providedCode;
363
+ this.category = category;
364
+ if (options.context !== void 0) {
365
+ this.context = options.context;
366
+ }
367
+ }
368
+ if (options && options.cause !== void 0) {
369
+ Object.defineProperty(this, "cause", {
370
+ value: options.cause,
371
+ configurable: true,
372
+ writable: true,
373
+ enumerable: false
374
+ });
375
+ }
376
+ const capture = Error.captureStackTrace;
377
+ if (typeof capture === "function") {
378
+ capture(this, _LlmExeError);
379
+ }
380
+ }
381
+ toJSON() {
382
+ return serializeLlmExeError(this);
383
+ }
384
+ };
385
+ Object.defineProperty(LlmExeError, "name", {
386
+ value: "LlmExeError",
387
+ configurable: true
388
+ });
389
+
390
+ // src/errors/isLlmExeError.ts
391
+ function isLlmExeError(error, code) {
392
+ if (!error || typeof error !== "object") return false;
393
+ const marked = error instanceof LlmExeError || error[LLM_EXE_ERROR_SYMBOL] === true || error.isLlmExeError === true;
394
+ if (!marked) return false;
395
+ if (code === void 0) return true;
396
+ const errorCode = error.code;
397
+ if (Array.isArray(code)) {
398
+ return code.includes(errorCode);
399
+ }
400
+ return errorCode === code;
401
+ }
402
+
403
+ // src/errors/format.ts
404
+ var DEFAULT_VALUE_MAX_LENGTH = 200;
405
+ var DEFAULT_LIST_MAX_ITEMS = 8;
406
+ function truncate(s, maxLength) {
407
+ if (s.length <= maxLength) return s;
408
+ return s.slice(0, Math.max(0, maxLength - 1)) + "\u2026";
409
+ }
410
+ function compactJson(value) {
411
+ try {
412
+ const seen = /* @__PURE__ */ new WeakSet();
413
+ const replacer = (_key, val) => {
414
+ if (typeof val === "bigint") return val.toString();
415
+ if (typeof val === "function") return "[Function]";
416
+ if (typeof val === "symbol") return String(val);
417
+ if (val && typeof val === "object") {
418
+ if (seen.has(val)) return "[Circular]";
419
+ seen.add(val);
420
+ }
421
+ return val;
422
+ };
423
+ return JSON.stringify(value, replacer);
424
+ } catch {
425
+ return void 0;
426
+ }
427
+ }
428
+ function formatErrorValue(value, options) {
429
+ const maxLength = options?.maxLength ?? DEFAULT_VALUE_MAX_LENGTH;
430
+ if (value === null) return "null";
431
+ if (value === void 0) return "undefined";
432
+ const t = typeof value;
433
+ if (t === "string") {
434
+ const s = value;
435
+ return `"${truncate(s, Math.max(2, maxLength - 2))}"`;
436
+ }
437
+ if (t === "number") {
438
+ const n = value;
439
+ return Number.isFinite(n) ? String(n) : "null";
440
+ }
441
+ if (t === "boolean") return value ? "true" : "false";
442
+ if (t === "bigint") return value.toString();
443
+ if (t === "symbol") return String(value);
444
+ if (t === "function") {
445
+ const name = value.name;
446
+ return name ? `[Function ${name}]` : "[Function]";
447
+ }
448
+ const json = compactJson(value);
449
+ if (json !== void 0) return truncate(json, maxLength);
450
+ const tag = Object.prototype.toString.call(value).slice(8, -1);
451
+ return `[object ${tag}]`;
452
+ }
453
+ function formatErrorList(values, options) {
454
+ const maxItems = options?.maxItems ?? DEFAULT_LIST_MAX_ITEMS;
455
+ const maxLength = options?.maxLength;
456
+ if (!values || values.length === 0) return "";
457
+ const limit = Math.min(values.length, maxItems);
458
+ const parts = [];
459
+ for (let i = 0; i < limit; i++) {
460
+ parts.push(formatErrorValue(values[i], maxLength ? { maxLength } : void 0));
461
+ }
462
+ if (values.length > limit) {
463
+ parts.push(`\u2026 (${values.length - limit} more)`);
464
+ }
465
+ return parts.join(", ");
466
+ }
467
+
468
+ // src/errors/createLlmExeError.ts
469
+ var DOCS_BASE_URL = "https://llm-exe.com";
470
+ function resolveDefinitionValue(value, context) {
471
+ if (value === void 0) return void 0;
472
+ return typeof value === "function" ? value(context) : value;
473
+ }
474
+ function resolveDocsUrl(docsPath) {
475
+ if (/^https?:\/\//i.test(docsPath)) return docsPath;
476
+ return `${DOCS_BASE_URL}${docsPath.startsWith("/") ? "" : "/"}${docsPath}`;
477
+ }
478
+ function createLlmExeError(definition, context, options) {
479
+ const message = definition.message(context);
480
+ const resolution = resolveDefinitionValue(definition.resolution, context);
481
+ const docsPath = resolveDefinitionValue(definition.docsPath, context);
482
+ const docsUrl = docsPath ? resolveDocsUrl(docsPath) : void 0;
483
+ const mergedContext = {
484
+ ...context,
485
+ ...resolution !== void 0 ? { resolution } : null,
486
+ ...docsPath !== void 0 ? { docsPath } : null,
487
+ ...docsUrl !== void 0 ? { docsUrl } : null
488
+ };
489
+ return new LlmExeError(message, {
490
+ code: definition.code,
491
+ context: mergedContext,
492
+ cause: options?.cause
493
+ });
494
+ }
495
+
496
+ // src/utils/modules/redactSecrets.ts
497
+ var TOKEN_MASK_REGEX = /\b(Bearer\s+[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|Bearer\s+[A-Za-z0-9_-]{20,}|sk-ant-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{30,}|AKIA[A-Z0-9]{16}|[A-Za-z0-9]{32,})\b/g;
498
+ var FULL_REDACTION_PATTERNS = [
499
+ // Alternate auth schemes.
500
+ [/\b(Basic|Digest|AWS4-HMAC-SHA256)\s+[A-Za-z0-9+/=._-]+/g, "$1 [redacted]"],
501
+ // Header- or query-style key/value pairs. Preserves the original separator
502
+ // ("Authorization: ..." stays colon, "?token=..." stays equals).
503
+ [
504
+ /(authorization|proxy-authorization|cookie|set-cookie|x-api-key|x-amz-security-token|x-amz-signature|api[-_]?key|access[-_]?token|token|secret|password)(\s*[:=]\s*)[^\r\n,;&]+/gi,
505
+ "$1$2[redacted]"
506
+ ],
507
+ // JSON-shaped fields.
508
+ [
509
+ /"(authorization|api[-_]?key|secret[-_]?key|access[-_]?key|secret|token|password|cookie|x-amz[a-z-]*)"\s*:\s*"[^"]*"/gi,
510
+ '"$1": "[redacted]"'
511
+ ]
512
+ ];
513
+ function maskToken(match) {
514
+ if (match.length <= 8) return match;
515
+ const prefix = match.substring(0, 4);
516
+ const suffix = match.substring(match.length - 4);
517
+ return `${prefix}${"*".repeat(match.length - 8)}${suffix}`;
518
+ }
519
+ function redactSecrets(input) {
520
+ let out = input.replace(TOKEN_MASK_REGEX, maskToken);
521
+ for (const [pattern, replacement] of FULL_REDACTION_PATTERNS) {
522
+ out = out.replace(pattern, replacement);
523
+ }
524
+ return out;
525
+ }
526
+ var maskApiKeys = redactSecrets;
527
+ var SECRET_URL_QUERY_KEYS = /* @__PURE__ */ new Set([
528
+ "key",
529
+ "api_key",
530
+ "apikey",
531
+ "token",
532
+ "access_token",
533
+ "refresh_token",
534
+ "id_token",
535
+ "secret",
536
+ "client_secret",
537
+ "password",
538
+ "x-amz-signature",
539
+ "x-amz-security-token",
540
+ "x-amz-credential",
541
+ "signature"
542
+ ]);
543
+ function safeRequestUrl(url) {
544
+ if (typeof url !== "string" || !url) return url;
545
+ let working = url;
546
+ try {
547
+ const parsed = new URL(url);
548
+ for (const key of Array.from(parsed.searchParams.keys())) {
549
+ if (SECRET_URL_QUERY_KEYS.has(key.toLowerCase())) {
550
+ parsed.searchParams.set(key, "[redacted]");
551
+ }
552
+ }
553
+ working = parsed.toString();
554
+ } catch {
555
+ }
556
+ return redactSecrets(working);
557
+ }
558
+
559
+ // src/errors/providerErrors.ts
560
+ var PROVIDER_MESSAGE_MAX_LENGTH = 240;
561
+ function safeProviderString(value, maxLength = PROVIDER_MESSAGE_MAX_LENGTH) {
562
+ if (typeof value !== "string" || value.length === 0) return void 0;
563
+ const scrubbed = redactSecrets(value);
564
+ return scrubbed.length > maxLength ? scrubbed.slice(0, maxLength) + "\u2026(truncated)" : scrubbed;
565
+ }
566
+ var SECRET_KEY_REGEX = /authorization|api[-_]?key|secret|token|password|cookie|set-cookie|x-amz/i;
567
+ var REDACTED = "[redacted]";
568
+ var DEFAULT_MAX_BODY_BYTES = 8192;
569
+ var DEFAULT_MAX_STRING_LENGTH = 2e3;
570
+ var MAX_SCRUB_DEPTH = 8;
571
+ var ALLOWED_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
572
+ // Generic diagnostic headers
573
+ "content-type",
574
+ "retry-after",
575
+ // Request/correlation IDs
576
+ "request-id",
577
+ "x-request-id",
578
+ "x-amzn-requestid",
579
+ "x-amz-request-id",
580
+ "x-goog-request-id",
581
+ // OpenAI-style rate-limit headers
582
+ "x-ratelimit-limit-requests",
583
+ "x-ratelimit-remaining-requests",
584
+ "x-ratelimit-reset-requests",
585
+ "x-ratelimit-limit-tokens",
586
+ "x-ratelimit-remaining-tokens",
587
+ "x-ratelimit-reset-tokens",
588
+ // Anthropic-style rate-limit headers
589
+ "anthropic-ratelimit-requests-limit",
590
+ "anthropic-ratelimit-requests-remaining",
591
+ "anthropic-ratelimit-requests-reset",
592
+ "anthropic-ratelimit-tokens-limit",
593
+ "anthropic-ratelimit-tokens-remaining",
594
+ "anthropic-ratelimit-tokens-reset"
595
+ ]);
596
+ function truncateString(value, max) {
597
+ if (value.length <= max) return value;
598
+ return value.slice(0, max) + "\u2026(truncated)";
599
+ }
600
+ function scrub(value, seen, depth, maxStringLength) {
601
+ if (value === null || value === void 0) return value;
602
+ const t = typeof value;
603
+ if (t === "string")
604
+ return redactSecrets(truncateString(value, maxStringLength));
605
+ if (t === "number" || t === "boolean") return value;
606
+ if (t === "bigint") return value.toString();
607
+ if (t === "symbol") return String(value);
608
+ if (t === "function") return "[Function]";
609
+ if (depth >= MAX_SCRUB_DEPTH) return "[deep]";
610
+ if (seen.has(value)) return "[Circular]";
611
+ seen.add(value);
612
+ try {
613
+ if (Array.isArray(value)) {
614
+ const arr = [];
615
+ for (let i = 0; i < value.length; i++) {
616
+ arr.push(scrub(value[i], seen, depth + 1, maxStringLength));
617
+ }
618
+ return arr;
619
+ }
620
+ const out = {};
621
+ for (const key of Object.keys(value)) {
622
+ if (SECRET_KEY_REGEX.test(key)) {
623
+ out[key] = REDACTED;
624
+ continue;
625
+ }
626
+ const v = value[key];
627
+ out[key] = scrub(v, seen, depth + 1, maxStringLength);
628
+ }
629
+ return out;
630
+ } finally {
631
+ seen.delete(value);
632
+ }
633
+ }
634
+ function safeProviderErrorBody(value, options) {
635
+ const maxStringLength = options?.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH;
636
+ const maxBodyBytes = options?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
637
+ if (typeof value === "string") {
638
+ return redactSecrets(truncateString(value, maxBodyBytes));
639
+ }
640
+ return scrub(value, /* @__PURE__ */ new WeakSet(), 0, maxStringLength);
641
+ }
642
+ function safeResponseHeaders(headers) {
643
+ const out = {};
644
+ if (!headers) return out;
645
+ if (typeof headers.forEach === "function" && typeof headers.get === "function") {
646
+ headers.forEach((value, key) => {
647
+ const lower = key.toLowerCase();
648
+ if (ALLOWED_RESPONSE_HEADERS.has(lower) && typeof value === "string") {
649
+ out[lower] = value;
650
+ }
651
+ });
652
+ return out;
653
+ }
654
+ for (const key of Object.keys(headers)) {
655
+ const value = headers[key];
656
+ const lower = key.toLowerCase();
657
+ if (ALLOWED_RESPONSE_HEADERS.has(lower) && typeof value === "string") {
658
+ out[lower] = value;
659
+ }
660
+ }
661
+ return out;
662
+ }
663
+ function parseRetryAfter(value) {
664
+ if (!value) return void 0;
665
+ const trimmed = value.trim();
666
+ if (!trimmed) return void 0;
667
+ if (/^\d+(\.\d+)?$/.test(trimmed)) {
668
+ const seconds = Number(trimmed);
669
+ if (!Number.isFinite(seconds)) return void 0;
670
+ return Math.max(0, Math.round(seconds * 1e3));
671
+ }
672
+ const dateMs = Date.parse(trimmed);
673
+ if (Number.isFinite(dateMs)) {
674
+ return Math.max(0, dateMs - Date.now());
675
+ }
676
+ return void 0;
677
+ }
678
+ function readStringField(obj, key) {
679
+ if (!obj || typeof obj !== "object") return void 0;
680
+ const v = obj[key];
681
+ return typeof v === "string" ? v : void 0;
682
+ }
683
+ function readStringOrNumberField(obj, key) {
684
+ if (!obj || typeof obj !== "object") return void 0;
685
+ const v = obj[key];
686
+ if (typeof v === "string") return v;
687
+ if (typeof v === "number" && Number.isFinite(v)) return String(v);
688
+ return void 0;
689
+ }
690
+ var parseProviderErrorGeneric = (input) => {
691
+ const status = input.status ?? input.response?.status;
692
+ const statusText = input.statusText ?? input.response?.statusText;
693
+ const headers = input.headers ?? {};
694
+ let message;
695
+ let providerType;
696
+ let providerCode;
697
+ const body = input.bodyJson;
698
+ if (body && typeof body === "object") {
699
+ const root = body;
700
+ const errVal = root.error;
701
+ const errObj = errVal && typeof errVal === "object" ? errVal : void 0;
702
+ message = readStringField(errObj, "message") ?? readStringField(root, "message") ?? (typeof errVal === "string" ? errVal : void 0);
703
+ providerType = readStringField(errObj, "type") ?? readStringField(root, "type") ?? // Bedrock-style: top-level "__type" carries the exception class.
704
+ readStringField(root, "__type") ?? readStringField(errObj, "status");
705
+ providerCode = // Accept numeric codes too (Gemini emits error.code: 400 as a number).
706
+ readStringOrNumberField(errObj, "code") ?? readStringOrNumberField(root, "code") ?? readStringField(errObj, "status");
707
+ }
708
+ if (!message && typeof input.bodyText === "string" && input.bodyText.length > 0) {
709
+ message = input.bodyText;
710
+ }
711
+ const retryAfterRaw = headers["retry-after"] ?? headers["Retry-After"];
712
+ const retryAfterMs = parseRetryAfter(retryAfterRaw);
713
+ let retryable;
714
+ if (typeof status === "number") {
715
+ retryable = status === 408 || status === 429 || status >= 500 && status < 600;
716
+ }
717
+ return {
718
+ message: safeProviderString(message),
719
+ providerType: safeProviderString(providerType),
720
+ providerCode: safeProviderString(providerCode),
721
+ status,
722
+ statusText,
723
+ retryable,
724
+ retryAfterMs
725
+ };
726
+ };
727
+ function statusToLlmProviderCode(status) {
728
+ if (status === 429) return "llm.provider_rate_limited";
729
+ if (status === 401 || status === 403) return "llm.provider_auth_failed";
730
+ if (status === 400 || status === 422) return "llm.provider_invalid_request";
731
+ if (status === 408 || status >= 500 && status < 600)
732
+ return "llm.provider_unavailable";
733
+ return "llm.provider_http_error";
734
+ }
735
+ function statusToEmbeddingProviderCode(status) {
736
+ if (status === 429) return "embedding.provider_rate_limited";
737
+ if (status === 401 || status === 403) return "embedding.provider_auth_failed";
738
+ if (status === 400 || status === 422)
739
+ return "embedding.provider_invalid_request";
740
+ if (status === 408 || status >= 500 && status < 600)
741
+ return "embedding.provider_unavailable";
742
+ return "embedding.provider_http_error";
743
+ }
744
+
745
+ // src/errors/getErrorMetadata.ts
746
+ function getErrorMetadata(error) {
747
+ if (!isLlmExeError(error)) return {};
748
+ return {
749
+ errorCategory: error.category,
750
+ errorCode: error.code,
751
+ errorContext: error.context,
752
+ errorCause: error.cause
753
+ };
754
+ }
755
+
99
756
  // src/executor/_base.ts
100
757
  var BaseExecutor = class {
101
758
  constructor(name, type, options) {
@@ -144,12 +801,41 @@ var BaseExecutor = class {
144
801
  }
145
802
  }
146
803
  /**
804
+ * Build a per-call execution context snapshot. Captures the current
805
+ * execution metadata so the snapshot reflects state at the time it was
806
+ * taken (e.g. `handlerInput` becomes available after `getHandlerInput`).
807
+ */
808
+ snapshotContext(execution) {
809
+ return {
810
+ traceId: this.getTraceId() ?? void 0,
811
+ executor: this.getMetadata(),
812
+ execution,
813
+ attributes: {}
814
+ };
815
+ }
816
+ /**
817
+ *
818
+ * Used to filter the input of the handler.
819
+ *
820
+ * Throws a `TypeError` if `_input` is `null` or `undefined`. The declared
821
+ * input type is `I extends PlainObject`; omitting input or passing an
822
+ * explicit `null` is a contract violation with no valid coercion, so we
823
+ * surface it loudly rather than silently wrapping it.
824
+ *
825
+ * Non-object inputs (strings, numbers, arrays) are intentionally coerced
826
+ * to `{ input: value }` via {@link ensureInputIsObject}. This preserves the
827
+ * convenience pattern used by tool/function callables, which forward raw
828
+ * string arguments into an executor.
147
829
  *
148
- * Used to filter the input of the handler
149
830
  * @param _input
150
831
  * @returns original input formatted for handler
151
832
  */
152
833
  async getHandlerInput(_input, _metadata, _options) {
834
+ if (_input === null || typeof _input === "undefined") {
835
+ throw new TypeError(
836
+ `[llm-exe] Executor "${this.name}" received null or undefined as input. execute() expects an object matching the prompt's input type.`
837
+ );
838
+ }
153
839
  return ensureInputIsObject(_input);
154
840
  }
155
841
  /**
@@ -158,7 +844,7 @@ var BaseExecutor = class {
158
844
  * @param _input
159
845
  * @returns output O
160
846
  */
161
- getHandlerOutput(out, _metadata, _options) {
847
+ getHandlerOutput(out, _metadata, _options, _context) {
162
848
  return out;
163
849
  }
164
850
  /**
@@ -180,25 +866,45 @@ var BaseExecutor = class {
180
866
  _options
181
867
  );
182
868
  _metadata.setItem({ handlerInput: input });
183
- let result = await this.handler(input, _options);
869
+ let result = await this.handler(
870
+ input,
871
+ _options,
872
+ this.snapshotContext(_metadata.asPlainObject())
873
+ );
184
874
  _metadata.setItem({ handlerOutput: result });
185
875
  const output = this.getHandlerOutput(
186
876
  result,
187
877
  _metadata.asPlainObject(),
188
- _options
878
+ _options,
879
+ this.snapshotContext(_metadata.asPlainObject())
189
880
  );
190
881
  _metadata.setItem({ output });
191
- this.runHook("onSuccess", _metadata.asPlainObject());
882
+ this.collectHookErrors(
883
+ this.runHook("onSuccess", _metadata.asPlainObject()),
884
+ _metadata
885
+ );
192
886
  return output;
193
887
  } catch (error) {
194
- _metadata.setItem({ error, errorMessage: error.message });
195
- this.runHook("onError", _metadata.asPlainObject());
888
+ _metadata.setItem({
889
+ error,
890
+ errorMessage: error.message,
891
+ ...getErrorMetadata(error)
892
+ });
893
+ this.collectHookErrors(
894
+ this.runHook("onError", _metadata.asPlainObject()),
895
+ _metadata
896
+ );
196
897
  throw error;
197
898
  } finally {
198
899
  _metadata.setItem({ end: (/* @__PURE__ */ new Date()).getTime() });
199
900
  this.runHook("onComplete", _metadata.asPlainObject());
200
901
  }
201
902
  }
903
+ collectHookErrors(fresh, state) {
904
+ if (fresh.length === 0) return;
905
+ const existing = state.asPlainObject().hookErrors ?? [];
906
+ state.setItem({ hookErrors: [...existing, ...fresh] });
907
+ }
202
908
  metadata() {
203
909
  return {};
204
910
  }
@@ -215,18 +921,23 @@ var BaseExecutor = class {
215
921
  }
216
922
  runHook(hook, _metadata) {
217
923
  const { [hook]: hooks = [] } = pick(this.hooks, this.allowedHooks);
924
+ const errors = [];
218
925
  for (const hookFn of [...hooks]) {
219
926
  if (typeof hookFn === "function") {
220
927
  try {
221
928
  hookFn(_metadata, this.getMetadata());
222
929
  } catch (error) {
223
- console.warn(
224
- `[llm-exe] Error in "${String(hook)}" hook:`,
225
- error
226
- );
930
+ const message = error instanceof Error ? error.message : String(error);
931
+ errors.push({
932
+ hook: String(hook),
933
+ error,
934
+ errorMessage: message,
935
+ ...getErrorMetadata(error)
936
+ });
227
937
  }
228
938
  }
229
939
  }
940
+ return errors;
230
941
  }
231
942
  setHooks(hooks = {}) {
232
943
  const hookKeys = Object.keys(hooks);
@@ -239,8 +950,19 @@ var BaseExecutor = class {
239
950
  for (const hook of _hooks) {
240
951
  if (hook && typeof hook === "function" && !this.hooks[hookKey].find((h) => h === hook)) {
241
952
  if (this.hooks[hookKey].length >= this.maxHooksPerEvent) {
242
- throw new Error(
243
- `Maximum number of hooks (${this.maxHooksPerEvent}) reached for event "${String(hookKey)}". Consider removing unused hooks or increasing the limit.`
953
+ throw new LlmExeError(
954
+ `Maximum number of hooks (${this.maxHooksPerEvent}) reached for event "${String(hookKey)}". Consider removing unused hooks or increasing the limit.`,
955
+ {
956
+ code: "executor.hook_limit_reached",
957
+ context: {
958
+ operation: "BaseExecutor.setHooks",
959
+ executorName: this.name,
960
+ executorType: this.type,
961
+ hook: String(hookKey),
962
+ hookCount: this.hooks[hookKey].length,
963
+ maxHooksPerEvent: this.maxHooksPerEvent
964
+ }
965
+ }
244
966
  );
245
967
  }
246
968
  this.hooks[hookKey].push(hook);
@@ -271,13 +993,27 @@ var BaseExecutor = class {
271
993
  once(eventName, fn) {
272
994
  if (typeof fn !== "function") return this;
273
995
  if (this.hooks[eventName] && this.hooks[eventName].length >= this.maxHooksPerEvent) {
274
- throw new Error(
275
- `Maximum number of hooks (${this.maxHooksPerEvent}) reached for event "${String(eventName)}". Consider removing unused hooks or increasing the limit.`
996
+ throw new LlmExeError(
997
+ `Maximum number of hooks (${this.maxHooksPerEvent}) reached for event "${String(eventName)}". Consider removing unused hooks or increasing the limit.`,
998
+ {
999
+ code: "executor.hook_limit_reached",
1000
+ context: {
1001
+ operation: "BaseExecutor.once",
1002
+ executorName: this.name,
1003
+ executorType: this.type,
1004
+ hook: String(eventName),
1005
+ hookCount: this.hooks[eventName].length,
1006
+ maxHooksPerEvent: this.maxHooksPerEvent
1007
+ }
1008
+ }
276
1009
  );
277
1010
  }
278
1011
  const onceWrapper = (...args) => {
279
- fn(...args);
280
- this.off(eventName, onceWrapper);
1012
+ try {
1013
+ fn(...args);
1014
+ } finally {
1015
+ this.off(eventName, onceWrapper);
1016
+ }
281
1017
  };
282
1018
  if (!this.hooks[eventName]) {
283
1019
  this.hooks[eventName] = [];
@@ -357,25 +1093,16 @@ var BaseParser = class {
357
1093
  /**
358
1094
  * Create a new BaseParser.
359
1095
  * @param name - The name of the parser.
360
- * @param options - options
1096
+ * @param target - Whether the parser consumes text or function-call output.
361
1097
  */
362
- constructor(name, options = {}, target = "text") {
1098
+ constructor(name, target = "text") {
363
1099
  __publicField(this, "name");
364
- __publicField(this, "options");
365
1100
  __publicField(this, "target", "text");
366
1101
  this.name = name;
367
1102
  this.target = target;
368
- if (options) {
369
- this.options = options;
370
- }
371
1103
  }
372
1104
  };
373
1105
  var BaseParserWithJson = class extends BaseParser {
374
- /**
375
- * Create a new BaseParser.
376
- * @param name - The name of the parser.
377
- * @param options - options
378
- */
379
1106
  constructor(name, options) {
380
1107
  super(name);
381
1108
  __publicField(this, "schema");
@@ -388,93 +1115,185 @@ var BaseParserWithJson = class extends BaseParser {
388
1115
  }
389
1116
  };
390
1117
 
391
- // src/utils/modules/assert.ts
392
- function assert(condition, message) {
393
- if (condition === void 0 || condition === null || condition === false) {
394
- if (typeof message === "string") {
395
- throw new Error(message);
396
- } else if (message instanceof Error) {
397
- throw message;
398
- } else {
399
- throw new Error(`Assertion error`);
1118
+ // src/parser/parsers/StringParser.ts
1119
+ var StringParser = class extends BaseParser {
1120
+ constructor() {
1121
+ super("string");
1122
+ }
1123
+ parse(text, _attributes) {
1124
+ if (typeof text !== "string") {
1125
+ throw new LlmExeError(
1126
+ `Invalid input. Expected string. Received ${text === null ? "null" : Array.isArray(text) ? "array" : typeof text}.`,
1127
+ {
1128
+ code: "parser.invalid_input",
1129
+ context: {
1130
+ operation: "StringParser.parse",
1131
+ parser: "string",
1132
+ reason: "invalid_input_type",
1133
+ expected: "string",
1134
+ received: text === null ? "null" : Array.isArray(text) ? "array" : typeof text
1135
+ }
1136
+ }
1137
+ );
400
1138
  }
1139
+ return text;
401
1140
  }
402
- }
1141
+ };
403
1142
 
404
- // src/utils/guards.ts
405
- var guards_exports = {};
406
- __export(guards_exports, {
407
- hasFunctionCall: () => hasFunctionCall,
408
- hasToolCall: () => hasToolCall,
409
- isAssistantMessage: () => isAssistantMessage,
410
- isFunctionCall: () => isFunctionCall,
411
- isOutputResult: () => isOutputResult,
412
- isOutputResultContentText: () => isOutputResultContentText,
413
- isSystemMessage: () => isSystemMessage,
414
- isToolCall: () => isToolCall,
415
- isUserMessage: () => isUserMessage
416
- });
417
- function isOutputResult(obj) {
418
- return !!(obj && typeof obj === "object" && "id" in obj && "stopReason" in obj && "content" in obj && Array.isArray(obj.content));
419
- }
420
- function isOutputResultContentText(obj) {
421
- return !!(obj && typeof obj === "object" && "text" in obj && "type" in obj && obj.type === "text" && typeof obj.text === "string");
422
- }
423
- function isFunctionCall(result) {
424
- return !!(result && typeof result === "object" && "functionId" in result && "type" in result && result.type === "function_use");
425
- }
426
- function isToolCall(result) {
427
- return isFunctionCall(result);
428
- }
429
- function hasFunctionCall(results) {
430
- return !!(results && Array.isArray(results) && results.some(isToolCall));
431
- }
432
- function hasToolCall(results) {
433
- return hasFunctionCall(results);
434
- }
435
- function isUserMessage(message) {
436
- return message.role === "user";
437
- }
438
- function isAssistantMessage(message) {
439
- return message.role === "assistant" || message.role === "model";
440
- }
441
- function isSystemMessage(message) {
442
- return message.role === "system";
1143
+ // src/utils/modules/getEnvironmentVariable.ts
1144
+ function getEnvironmentVariable(name) {
1145
+ if (typeof process === "object" && process?.env) {
1146
+ return process.env[name];
1147
+ } else {
1148
+ return void 0;
1149
+ }
443
1150
  }
444
1151
 
445
- // src/parser/parsers/StringParser.ts
446
- var StringParser = class extends BaseParser {
447
- constructor(options) {
448
- super("string", options);
1152
+ // src/utils/modules/debug.ts
1153
+ function isDebugEnabled() {
1154
+ const debugValue = getEnvironmentVariable("LLM_EXE_DEBUG");
1155
+ return typeof debugValue === "string" && debugValue !== "" && debugValue.toLowerCase() !== "undefined" && debugValue.toLowerCase() !== "null";
1156
+ }
1157
+ function debug(...args) {
1158
+ if (!isDebugEnabled()) {
1159
+ return;
449
1160
  }
450
- parse(text, _options) {
451
- if (isOutputResult(text)) {
452
- return text.content?.[0]?.text ?? "";
1161
+ const logs = [];
1162
+ for (const arg of args) {
1163
+ if (arg && typeof arg === "object") {
1164
+ if (arg instanceof Error) {
1165
+ } else if (arg instanceof Array) {
1166
+ logs.push(arg.map((item) => JSON.stringify(item, null, 2)));
1167
+ } else if (arg instanceof Map) {
1168
+ logs.push(
1169
+ Array.from(arg.entries()).map(
1170
+ ([key, value]) => `${key}: ${JSON.stringify(value, null, 2)}`
1171
+ )
1172
+ );
1173
+ } else if (arg instanceof Set) {
1174
+ logs.push(Array.from(arg).map((item) => JSON.stringify(item, null, 2)));
1175
+ } else if (arg instanceof Date) {
1176
+ logs.push(arg.toISOString());
1177
+ } else if (arg instanceof RegExp) {
1178
+ logs.push(arg.toString());
1179
+ } else {
1180
+ try {
1181
+ const str = maskApiKeys(JSON.stringify(arg, null, 2));
1182
+ logs.push(str);
1183
+ } catch (error) {
1184
+ console.error("Error parsing object:", error);
1185
+ }
1186
+ }
1187
+ } else if (typeof arg === "string") {
1188
+ logs.push(safeRequestUrl(arg));
1189
+ } else {
1190
+ logs.push(arg);
453
1191
  }
454
- assert(
455
- typeof text === "string",
456
- `Invalid input. Expected string. Received ${typeof text}.`
457
- );
458
- const parsed = text.toString();
459
- return parsed;
460
1192
  }
461
- };
1193
+ if (isDebugEnabled()) {
1194
+ console.debug(...logs);
1195
+ }
1196
+ }
462
1197
 
463
1198
  // src/parser/parsers/BooleanParser.ts
1199
+ var BOOLEAN_VALUES = ["true", "false", "yes", "no", "y", "n", "1", "0"];
1200
+ var TRUTHY_VALUES = /* @__PURE__ */ new Set(["true", "yes", "y", "1"]);
1201
+ var FALSY_VALUES = /* @__PURE__ */ new Set(["false", "no", "n", "0"]);
1202
+ var MAX_ERROR_INPUT_EXCERPT_LENGTH = 500;
1203
+ var BOOLEAN_TOKEN_PATTERN = /(?<![\p{L}\p{N}])(?:true|false|yes|no|y|n|1|0)(?![\p{L}\p{N}])/giu;
464
1204
  var BooleanParser = class extends BaseParser {
465
1205
  constructor(options) {
466
- super("boolean", options);
1206
+ super("boolean");
1207
+ __publicField(this, "match");
1208
+ this.match = options?.match ?? "exact";
467
1209
  }
468
- parse(text) {
469
- assert(
470
- typeof text === "string",
471
- `Invalid input. Expected string. Received ${typeof text}.`
472
- );
1210
+ getInputErrorContext(text) {
1211
+ const context = {
1212
+ inputLength: text.length
1213
+ };
1214
+ if (isDebugEnabled()) {
1215
+ const truncated = text.length > MAX_ERROR_INPUT_EXCERPT_LENGTH;
1216
+ context.inputExcerpt = truncated ? text.slice(0, MAX_ERROR_INPUT_EXCERPT_LENGTH) : text;
1217
+ context.inputExcerptTruncated = truncated;
1218
+ }
1219
+ return context;
1220
+ }
1221
+ parse(text, _attributes) {
1222
+ if (typeof text !== "string") {
1223
+ throw new LlmExeError(
1224
+ `Invalid input. Expected string. Received ${text === null ? "null" : typeof text}.`,
1225
+ {
1226
+ code: "parser.invalid_input",
1227
+ context: {
1228
+ operation: "BooleanParser.parse",
1229
+ parser: "boolean",
1230
+ reason: "invalid_input_type",
1231
+ expected: "string",
1232
+ received: text === null ? "null" : typeof text
1233
+ }
1234
+ }
1235
+ );
1236
+ }
473
1237
  const clean = text.toLowerCase().trim();
474
- if (clean === "true" || clean === "yes" || clean === "y" || clean === "1") {
1238
+ if (!clean) {
1239
+ throw new LlmExeError(`No boolean value found in input.`, {
1240
+ code: "parser.parse_failed",
1241
+ context: {
1242
+ operation: "BooleanParser.parse",
1243
+ parser: "boolean",
1244
+ reason: "empty_input",
1245
+ expected: BOOLEAN_VALUES,
1246
+ ...this.getInputErrorContext(text)
1247
+ }
1248
+ });
1249
+ }
1250
+ if (TRUTHY_VALUES.has(clean)) {
475
1251
  return true;
476
1252
  }
477
- return false;
1253
+ if (FALSY_VALUES.has(clean)) {
1254
+ return false;
1255
+ }
1256
+ if (this.match === "extract") {
1257
+ const matches = Array.from(
1258
+ text.toLowerCase().matchAll(BOOLEAN_TOKEN_PATTERN)
1259
+ ).map((match) => match[0]);
1260
+ const values = Array.from(
1261
+ new Set(
1262
+ matches.map((match) => {
1263
+ if (TRUTHY_VALUES.has(match)) return true;
1264
+ return false;
1265
+ })
1266
+ )
1267
+ );
1268
+ if (values.length === 1) {
1269
+ return values[0];
1270
+ }
1271
+ if (values.length > 1) {
1272
+ throw new LlmExeError(`Multiple boolean values found in input.`, {
1273
+ code: "parser.parse_failed",
1274
+ context: {
1275
+ operation: "BooleanParser.parse",
1276
+ parser: "boolean",
1277
+ reason: "ambiguous_boolean",
1278
+ expected: "one boolean value",
1279
+ match: this.match,
1280
+ matchCount: values.length,
1281
+ ...this.getInputErrorContext(text)
1282
+ }
1283
+ });
1284
+ }
1285
+ }
1286
+ throw new LlmExeError(`No boolean value found in input.`, {
1287
+ code: "parser.parse_failed",
1288
+ context: {
1289
+ operation: "BooleanParser.parse",
1290
+ parser: "boolean",
1291
+ reason: "unrecognized_boolean",
1292
+ expected: BOOLEAN_VALUES,
1293
+ match: this.match,
1294
+ ...this.getInputErrorContext(text)
1295
+ }
1296
+ });
478
1297
  }
479
1298
  };
480
1299
 
@@ -494,106 +1313,121 @@ function toNumber(value) {
494
1313
  return NaN;
495
1314
  }
496
1315
 
497
- // src/utils/modules/errors.ts
498
- var LlmExeError = class extends Error {
499
- constructor(message, code, context) {
500
- super(message ?? "");
501
- __publicField(this, "code");
502
- __publicField(this, "context");
503
- this.name = this.constructor.name;
504
- this.code = code ?? "unknown";
505
- this.context = context;
506
- if (Error.captureStackTrace) {
507
- Error.captureStackTrace(this, this.constructor);
508
- }
509
- }
510
- };
511
-
512
1316
  // src/parser/parsers/NumberParser.ts
1317
+ var NUMERIC_TOKEN_PATTERN = /(^|[^\w.,])([+-]?(?:(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?)(?![\w,])/g;
1318
+ var EXACT_NUMERIC_PATTERN = /^[+-]?(?:(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?$/;
513
1319
  var NumberParser = class extends BaseParser {
514
1320
  constructor(options) {
515
- super("number", options);
1321
+ super("number");
1322
+ __publicField(this, "match");
1323
+ this.match = options?.match ?? "extract";
516
1324
  }
517
- parse(text) {
518
- const match = text.match(/-?\d+(\.\d+)?/);
519
- if (match && isFinite(toNumber(match[0]))) {
520
- return toNumber(match[0]);
1325
+ parse(text, _attributes) {
1326
+ if (typeof text !== "string") {
1327
+ throw new LlmExeError(
1328
+ `Invalid input. Expected string. Received ${text === null ? "null" : Array.isArray(text) ? "array" : typeof text}.`,
1329
+ {
1330
+ code: "parser.invalid_input",
1331
+ context: {
1332
+ operation: "NumberParser.parse",
1333
+ parser: "number",
1334
+ reason: "invalid_input_type",
1335
+ expected: "string",
1336
+ received: text === null ? "null" : Array.isArray(text) ? "array" : typeof text
1337
+ }
1338
+ }
1339
+ );
521
1340
  }
522
- throw new LlmExeError(
523
- `No numeric value found in input.`,
524
- "parser",
525
- {
526
- parser: "number",
527
- output: text,
528
- error: `No numeric value found in input.`
1341
+ if (text.trim() === "") {
1342
+ throw new LlmExeError(`No numeric value found in input.`, {
1343
+ code: "parser.parse_failed",
1344
+ context: {
1345
+ operation: "NumberParser.parse",
1346
+ parser: "number",
1347
+ reason: "empty_input",
1348
+ expected: "number",
1349
+ inputLength: text.length
1350
+ }
1351
+ });
1352
+ }
1353
+ if (this.match === "exact") {
1354
+ const trimmed = text.trim();
1355
+ if (!EXACT_NUMERIC_PATTERN.test(trimmed)) {
1356
+ throw new LlmExeError(`Input is not an exact numeric value.`, {
1357
+ code: "parser.parse_failed",
1358
+ context: {
1359
+ operation: "NumberParser.parse",
1360
+ parser: "number",
1361
+ reason: "no_numeric_value",
1362
+ expected: "exact number",
1363
+ match: this.match,
1364
+ inputLength: text.length
1365
+ }
1366
+ });
529
1367
  }
530
- );
531
- }
532
- };
533
-
534
- // src/utils/index.ts
535
- var utils_exports = {};
536
- __export(utils_exports, {
537
- assert: () => assert,
538
- asyncCallWithTimeout: () => asyncCallWithTimeout,
539
- defineSchema: () => defineSchema,
540
- filterObjectOnSchema: () => filterObjectOnSchema,
541
- guessProviderFromModel: () => guessProviderFromModel,
542
- importHelpers: () => importHelpers,
543
- importPartials: () => importPartials,
544
- isObjectStringified: () => isObjectStringified,
545
- maybeParseJSON: () => maybeParseJSON,
546
- maybeStringifyJSON: () => maybeStringifyJSON,
547
- registerHelpers: () => registerHelpers,
548
- registerPartials: () => registerPartials,
549
- replaceTemplateString: () => replaceTemplateString,
550
- replaceTemplateStringAsync: () => replaceTemplateStringAsync
551
- });
552
-
553
- // src/utils/modules/defineSchema.ts
554
- import { asConst } from "json-schema-to-ts";
555
- function defineSchema(obj) {
556
- obj.additionalProperties = false;
557
- return asConst(obj);
558
- }
559
-
560
- // src/utils/modules/handlebars/utils/importPartials.ts
561
- function importPartials(_partials) {
562
- let partials2 = [];
563
- if (_partials) {
564
- const externalPartialKeys = Object.keys(
565
- _partials
566
- );
567
- for (const externalPartialKey of externalPartialKeys) {
568
- if (typeof externalPartialKey === "string") {
569
- partials2.push({
570
- name: externalPartialKey,
571
- template: _partials[externalPartialKey]
1368
+ const value2 = toNumber(trimmed.replace(/,/g, ""));
1369
+ if (!isFinite(value2)) {
1370
+ throw new LlmExeError(`Invalid numeric value found in input.`, {
1371
+ code: "parser.parse_failed",
1372
+ context: {
1373
+ operation: "NumberParser.parse",
1374
+ parser: "number",
1375
+ reason: "invalid_number",
1376
+ expected: "finite number",
1377
+ match: this.match,
1378
+ inputLength: text.length
1379
+ }
572
1380
  });
573
1381
  }
1382
+ return value2;
574
1383
  }
575
- }
576
- return partials2;
577
- }
578
-
579
- // src/utils/modules/handlebars/utils/importHelpers.ts
580
- function importHelpers(_helpers) {
581
- let helpers = [];
582
- if (_helpers) {
583
- const externalHelperKeys = Object.keys(
584
- _helpers
1384
+ const matches = Array.from(text.matchAll(NUMERIC_TOKEN_PATTERN)).map(
1385
+ (match) => match[2]
585
1386
  );
586
- for (const externalHelperKey of externalHelperKeys) {
587
- if (typeof externalHelperKey === "string") {
588
- helpers.push({
589
- name: externalHelperKey,
590
- handler: _helpers[externalHelperKey]
591
- });
592
- }
1387
+ if (matches.length === 0) {
1388
+ throw new LlmExeError(`No numeric value found in input.`, {
1389
+ code: "parser.parse_failed",
1390
+ context: {
1391
+ operation: "NumberParser.parse",
1392
+ parser: "number",
1393
+ reason: "no_numeric_value",
1394
+ expected: "number",
1395
+ match: this.match,
1396
+ inputLength: text.length
1397
+ }
1398
+ });
1399
+ }
1400
+ if (matches.length > 1) {
1401
+ throw new LlmExeError(`Multiple numeric values found in input.`, {
1402
+ code: "parser.parse_failed",
1403
+ context: {
1404
+ operation: "NumberParser.parse",
1405
+ parser: "number",
1406
+ reason: "ambiguous_number",
1407
+ expected: "one numeric value",
1408
+ match: this.match,
1409
+ inputLength: text.length,
1410
+ matchCount: matches.length
1411
+ }
1412
+ });
593
1413
  }
1414
+ const value = toNumber(matches[0].replace(/,/g, ""));
1415
+ if (!isFinite(value)) {
1416
+ throw new LlmExeError(`Invalid numeric value found in input.`, {
1417
+ code: "parser.parse_failed",
1418
+ context: {
1419
+ operation: "NumberParser.parse",
1420
+ parser: "number",
1421
+ reason: "invalid_number",
1422
+ expected: "finite number",
1423
+ match: this.match,
1424
+ inputLength: text.length
1425
+ }
1426
+ });
1427
+ }
1428
+ return value;
594
1429
  }
595
- return helpers;
596
- }
1430
+ };
597
1431
 
598
1432
  // src/utils/modules/get.ts
599
1433
  function get(obj, path, defaultValue) {
@@ -647,10 +1481,22 @@ function filterObjectOnSchema(schema, doc, detach, property) {
647
1481
  return;
648
1482
  }
649
1483
  } else {
650
- if (sp.type === "integer" || sp.type === "number") {
1484
+ var childType = getType(sp.type);
1485
+ if (childType === "integer" || childType === "number") {
651
1486
  result[key] = toNumber(filteredChild);
652
- } else if (sp.type === "boolean") {
653
- result[key] = !!filteredChild;
1487
+ } else if (childType === "boolean") {
1488
+ if (typeof filteredChild === "string") {
1489
+ const normalized = filteredChild.trim().toLowerCase();
1490
+ if (normalized === "true") {
1491
+ result[key] = true;
1492
+ } else if (normalized === "false") {
1493
+ result[key] = false;
1494
+ } else {
1495
+ result[key] = filteredChild;
1496
+ }
1497
+ } else {
1498
+ result[key] = filteredChild;
1499
+ }
654
1500
  } else {
655
1501
  result[key] = filteredChild;
656
1502
  }
@@ -670,122 +1516,719 @@ function filterObjectOnSchema(schema, doc, detach, property) {
670
1516
  return result;
671
1517
  }
672
1518
 
673
- // src/utils/modules/handlebars/hbs.ts
674
- import Handlebars from "handlebars";
675
-
676
- // src/utils/modules/handlebars/utils/registerHelpers.ts
677
- function _registerHelpers(helpers, instance) {
678
- if (helpers && Array.isArray(helpers)) {
679
- for (const helper of helpers) {
680
- if (helper.name && typeof helper.name === "string" && typeof helper.handler === "function") {
681
- if (instance) {
682
- instance.registerHelper(helper.name, helper.handler);
683
- }
684
- }
685
- }
1519
+ // src/parser/_utils.ts
1520
+ import { validate as validateSchema } from "jsonschema";
1521
+ function isPlainObject(value) {
1522
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1523
+ return false;
686
1524
  }
1525
+ const proto = Object.getPrototypeOf(value);
1526
+ return proto === Object.prototype || proto === null;
687
1527
  }
688
-
689
- // src/utils/modules/handlebars/templates/index.ts
690
- var MarkdownCode = `{{#if code}}\`\`\`{{#if language}}{{language}}{{/if}}
691
- {{{code}}}
692
- \`\`\`{{/if}}`;
693
- var ThoughtActionResult = `
694
- {{#if title}}{{#if attributes.stepsTaken.length}}{{title}}
695
- {{/if}}{{/if}}
696
- {{#each attributes.stepsTaken as | step |}}
697
- {{#if step.thought}}Thought: {{step.result}}{{/if}}
698
- {{#if step.result}}Action: {{step.action}}{{/if}}
699
- {{#if step.result}}Result: {{step.result}}{{/if}}
700
- {{/each}}`;
701
- var ChatConversationHistory = `{{~#if title}}{{~#if chat_history.length}}{{title}}
702
- {{~/if}}{{~/if}}
703
- {{#each chat_history as | item |}}
704
- {{~#eq item.role 'user'}}{{#if @last}}{{../mostRecentRolePrefix}}{{../userName}}{{../mostRecentRoleSuffix}}{{else}}{{../userName}}{{/if}}: {{#if @last}}{{../mostRecentMessagePrefix}}{{/if}}{{{item.content}}}{{#if @last}}{{../mostRecentMessageSuffix}}{{/if}}
705
- {{/eq}}
706
- {{~#eq item.role 'assistant'}}{{#if @last}}{{../mostRecentRolePrefix}}{{../assistantName}}{{../mostRecentRoleSuffix}}{{else}}{{../assistantName}}{{/if}}: {{#if @last}}{{../mostRecentMessagePrefix}}{{/if}}{{{item.content}}}{{#if @last}}{{../mostRecentMessageSuffix}}{{/if}}
707
- {{/eq}}
708
- {{~#eq item.role 'system'}}{{../systemName}}: {{{item.content}}}
709
- {{/eq}}
710
- {{~/each}}`;
711
- var DialogueHistory = `{{>ChatConversationHistory title=title chat_history=(getKeyOr key []) assistantName=(getOr assistant 'Assistant') userName=(getOr user 'User') systemName=(getOr system 'System') mostRecentRolePrefix=mostRecentRolePrefix mostRecentRoleSuffix=mostRecentRoleSuffix mostRecentMessagePrefix=mostRecentMessagePrefix mostRecentMessageSuffix=mostRecentMessageSuffix}}`;
712
- var SingleChatMessage = `{{~#eq role 'user'}}{{getOr name 'User'}}: {{{content}}}{{~/eq}}
713
- {{~#eq role 'assistant'}}{{getOr assistant 'Assistant'}}: {{{content}}}{{~/eq}}
714
- {{~#eq role 'system'}}{{getOr system 'System'}}: {{{content}}}{{~/eq}}`;
715
- var ThoughtsAndObservations = `{{~#each thoughts as | step |}}
716
- {{~#if step.thought}}Thought: {{{step.thought}}}
717
- {{/if}}
718
- {{~#if step.observation}}Observation: {{{step.observation}}}
719
- {{/if}}
720
- {{~/each}}`;
721
- var JsonSchema = `{{#if (getKeyOr key false)}}
722
- \`\`\`json
723
- {{{indentJson (getKeyOr key) collapse}}}
724
- \`\`\`
725
- {{~/if}}`;
726
- var JsonSchemaExampleJson = `{{#if (getOr key false)}}
727
- \`\`\`json
728
- {{{jsonSchemaExample key (getOr property '') collapse}}}
729
- \`\`\`
730
- {{~/if}}`;
731
- var partials = {
732
- JsonSchema,
733
- JsonSchemaExampleJson,
734
- MarkdownCode,
735
- DialogueHistory,
736
- SingleChatMessage,
737
- ChatConversationHistory,
738
- ThoughtsAndObservations,
739
- ThoughtActionResult
740
- };
741
-
742
- // src/utils/modules/handlebars/helpers/index.ts
743
- var helpers_exports = {};
744
- __export(helpers_exports, {
745
- cut: () => cutFn,
746
- eq: () => eq,
747
- getKeyOr: () => getKeyOr,
748
- getOr: () => getOr,
749
- ifCond: () => ifCond,
750
- indentJson: () => indentJson,
751
- join: () => join,
752
- jsonSchemaExample: () => jsonSchemaExample,
753
- neq: () => neq,
754
- objectToList: () => objectToList,
755
- pluralize: () => pluralize,
756
- substring: () => substringFn,
757
- with: () => withFn
758
- });
759
-
760
- // src/utils/modules/json.ts
761
- var maybeStringifyJSON = (objOrMaybeString) => {
762
- if (!objOrMaybeString || typeof objOrMaybeString !== "object") {
763
- return objOrMaybeString;
1528
+ function getSchemaType(schemaType) {
1529
+ if (!Array.isArray(schemaType)) {
1530
+ return schemaType;
764
1531
  }
765
- try {
766
- const result = JSON.stringify(objOrMaybeString);
767
- return result;
768
- } catch (error) {
1532
+ return schemaType.find((type) => type !== "null");
1533
+ }
1534
+ function enforceParserSchema(schema, parsed) {
1535
+ if (!schema || !parsed || typeof parsed !== "object") {
1536
+ return parsed;
769
1537
  }
770
- return "";
771
- };
772
- var maybeParseJSON = (objOrMaybeJSON) => {
773
- if (!objOrMaybeJSON) return {};
774
- if (typeof objOrMaybeJSON === "string") {
775
- try {
776
- const cleanMarkdown = helpJsonMarkup(objOrMaybeJSON);
777
- const result = JSON.parse(cleanMarkdown);
778
- if (typeof result === "object" && result !== null) {
779
- return result;
1538
+ return filterObjectOnSchema(schema, parsed);
1539
+ }
1540
+ function validateParserSchema(schema, parsed) {
1541
+ if (!schema || !parsed || typeof parsed !== "object") {
1542
+ return null;
1543
+ }
1544
+ const validate = validateSchema(parsed, schema);
1545
+ if (validate.errors.length) {
1546
+ return validate.errors;
1547
+ }
1548
+ return null;
1549
+ }
1550
+ function coerceParserSchemaValues(schema, parsed) {
1551
+ if (!schema || parsed === null || typeof parsed === "undefined") {
1552
+ return parsed;
1553
+ }
1554
+ const type = getSchemaType(schema.type);
1555
+ if (type === "object" && isPlainObject(parsed)) {
1556
+ const properties = schema.properties || {};
1557
+ return Object.keys(parsed).reduce((output, key) => {
1558
+ const propertySchema = properties[key];
1559
+ output[key] = propertySchema ? coerceParserSchemaValues(propertySchema, parsed[key]) : parsed[key];
1560
+ return output;
1561
+ }, {});
1562
+ }
1563
+ if (type === "array" && Array.isArray(parsed) && schema.items) {
1564
+ return parsed.map(
1565
+ (item) => coerceParserSchemaValues(schema.items, item)
1566
+ );
1567
+ }
1568
+ if ((type === "number" || type === "integer") && typeof parsed === "string") {
1569
+ const trimmed = parsed.trim();
1570
+ if (trimmed !== "") {
1571
+ const value = Number(trimmed);
1572
+ if (Number.isFinite(value)) {
1573
+ return value;
780
1574
  }
781
- } catch (error) {
782
1575
  }
783
1576
  }
784
- if (typeof objOrMaybeJSON === "object" && objOrMaybeJSON !== null) {
785
- return objOrMaybeJSON;
1577
+ if (type === "boolean" && typeof parsed === "string") {
1578
+ const normalized = parsed.trim().toLowerCase();
1579
+ if (normalized === "true") {
1580
+ return true;
1581
+ }
1582
+ if (normalized === "false") {
1583
+ return false;
1584
+ }
786
1585
  }
787
- return {};
788
- };
1586
+ return parsed;
1587
+ }
1588
+ function applyParserSchemaDefaultsAndFilter(schema, parsed) {
1589
+ if (!schema || parsed === null || typeof parsed === "undefined") {
1590
+ return parsed;
1591
+ }
1592
+ const type = getSchemaType(schema.type);
1593
+ if (type === "object" && isPlainObject(parsed)) {
1594
+ const properties = schema.properties;
1595
+ if (!properties) {
1596
+ return parsed;
1597
+ }
1598
+ const keepAdditionalProperties = schema.additionalProperties !== false;
1599
+ const initialOutput = keepAdditionalProperties ? { ...parsed } : {};
1600
+ return Object.keys(properties).reduce((output, key) => {
1601
+ const propertySchema = properties[key];
1602
+ const value = parsed[key];
1603
+ if (typeof value === "undefined") {
1604
+ if (typeof propertySchema?.default !== "undefined") {
1605
+ output[key] = propertySchema.default;
1606
+ }
1607
+ return output;
1608
+ }
1609
+ output[key] = applyParserSchemaDefaultsAndFilter(propertySchema, value);
1610
+ return output;
1611
+ }, initialOutput);
1612
+ }
1613
+ if (type === "array" && Array.isArray(parsed) && schema.items) {
1614
+ return parsed.map(
1615
+ (item) => applyParserSchemaDefaultsAndFilter(schema.items, item)
1616
+ );
1617
+ }
1618
+ return parsed;
1619
+ }
1620
+
1621
+ // src/parser/parsers/JsonParser.ts
1622
+ function isPlainObject2(value) {
1623
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1624
+ return false;
1625
+ }
1626
+ const proto = Object.getPrototypeOf(value);
1627
+ return proto === Object.prototype || proto === null;
1628
+ }
1629
+ function normalizeExactResponseJsonText(input) {
1630
+ const trimmed = input.trim();
1631
+ const fenceMatch = trimmed.match(
1632
+ /^```([a-zA-Z]*)[^\S\r\n]*\r?\n([\s\S]*)```$/
1633
+ );
1634
+ if (!fenceMatch) {
1635
+ return trimmed;
1636
+ }
1637
+ const [, language, body] = fenceMatch;
1638
+ if (language && language.toLowerCase() !== "json") {
1639
+ return trimmed;
1640
+ }
1641
+ return body.trim();
1642
+ }
1643
+ function findBalancedJsonEnd(input, start) {
1644
+ const first = input[start];
1645
+ const stack = first === "{" ? ["}"] : first === "[" ? ["]"] : [];
1646
+ let inString = false;
1647
+ let escaping = false;
1648
+ for (let index = start + 1; index < input.length; index += 1) {
1649
+ const char = input[index];
1650
+ if (inString) {
1651
+ if (escaping) {
1652
+ escaping = false;
1653
+ } else if (char === "\\") {
1654
+ escaping = true;
1655
+ } else if (char === '"') {
1656
+ inString = false;
1657
+ }
1658
+ continue;
1659
+ }
1660
+ if (char === '"') {
1661
+ inString = true;
1662
+ continue;
1663
+ }
1664
+ if (char === "{" || char === "[") {
1665
+ stack.push(char === "{" ? "}" : "]");
1666
+ continue;
1667
+ }
1668
+ if (char !== "}" && char !== "]") {
1669
+ continue;
1670
+ }
1671
+ if (stack[stack.length - 1] !== char) {
1672
+ return void 0;
1673
+ }
1674
+ stack.pop();
1675
+ if (stack.length === 0) {
1676
+ return index;
1677
+ }
1678
+ }
1679
+ return void 0;
1680
+ }
1681
+ function extractJsonCandidates(input) {
1682
+ const candidates = [];
1683
+ for (let index = 0; index < input.length; index += 1) {
1684
+ const char = input[index];
1685
+ if (char !== "{" && char !== "[") {
1686
+ continue;
1687
+ }
1688
+ const end = findBalancedJsonEnd(input, index);
1689
+ if (end === void 0) {
1690
+ continue;
1691
+ }
1692
+ const candidate = input.slice(index, end + 1);
1693
+ try {
1694
+ candidates.push({
1695
+ parsed: JSON.parse(candidate)
1696
+ });
1697
+ index = end;
1698
+ } catch {
1699
+ }
1700
+ }
1701
+ return candidates;
1702
+ }
1703
+ var JsonParser = class extends BaseParserWithJson {
1704
+ constructor(options = {}) {
1705
+ super("json", options);
1706
+ __publicField(this, "shouldValidateSchema");
1707
+ __publicField(this, "match");
1708
+ this.match = options.match ?? "exact";
1709
+ this.shouldValidateSchema = !!options.schema && options.validateSchema !== false;
1710
+ }
1711
+ /**
1712
+ * v3 parser contract:
1713
+ * Category: strict
1714
+ * Mode: exact
1715
+ *
1716
+ * Parses strict JSON object/array output by default. Pass match: "extract"
1717
+ * to extract one JSON object or array from surrounding text. Invalid JSON,
1718
+ * empty input, JSON primitives, and non-plain runtime objects throw typed parser errors.
1719
+ * Schema validation is on by default when a schema is provided unless
1720
+ * validateSchema: false is explicitly set.
1721
+ *
1722
+ */
1723
+ parse(text, _attributes) {
1724
+ let parsed;
1725
+ let inputLength;
1726
+ if (typeof text === "string") {
1727
+ inputLength = text.length;
1728
+ if (text.trim() === "") {
1729
+ throw new LlmExeError(`No JSON value found in input.`, {
1730
+ code: "parser.parse_failed",
1731
+ context: {
1732
+ operation: "JsonParser.parse",
1733
+ parser: "json",
1734
+ reason: "empty_input",
1735
+ expected: "JSON object or array",
1736
+ inputLength
1737
+ }
1738
+ });
1739
+ }
1740
+ try {
1741
+ parsed = JSON.parse(normalizeExactResponseJsonText(text));
1742
+ } catch (cause) {
1743
+ if (this.match === "extract") {
1744
+ const candidates = extractJsonCandidates(text);
1745
+ if (candidates.length === 1) {
1746
+ parsed = candidates[0].parsed;
1747
+ } else if (candidates.length > 1) {
1748
+ throw new LlmExeError(`Multiple JSON values found in input.`, {
1749
+ code: "parser.parse_failed",
1750
+ context: {
1751
+ operation: "JsonParser.parse",
1752
+ parser: "json",
1753
+ reason: "ambiguous_json_match",
1754
+ expected: "one JSON object or array",
1755
+ match: this.match,
1756
+ inputLength,
1757
+ matchCount: candidates.length
1758
+ }
1759
+ });
1760
+ } else {
1761
+ throw new LlmExeError(`No JSON value found in input.`, {
1762
+ code: "parser.parse_failed",
1763
+ context: {
1764
+ operation: "JsonParser.parse",
1765
+ parser: "json",
1766
+ reason: "no_json_value",
1767
+ expected: "JSON object or array",
1768
+ match: this.match,
1769
+ inputLength
1770
+ },
1771
+ cause
1772
+ });
1773
+ }
1774
+ } else {
1775
+ throw new LlmExeError(`Invalid JSON input.`, {
1776
+ code: "parser.parse_failed",
1777
+ context: {
1778
+ operation: "JsonParser.parse",
1779
+ parser: "json",
1780
+ reason: "invalid_json",
1781
+ expected: "JSON object or array",
1782
+ inputLength
1783
+ },
1784
+ cause
1785
+ });
1786
+ }
1787
+ }
1788
+ } else if (Array.isArray(text) || isPlainObject2(text)) {
1789
+ parsed = text;
1790
+ } else {
1791
+ throw new LlmExeError(
1792
+ `Invalid input. Expected JSON string, plain object, or array. Received ${text === null ? "null" : typeof text}.`,
1793
+ {
1794
+ code: "parser.invalid_input",
1795
+ context: {
1796
+ operation: "JsonParser.parse",
1797
+ parser: "json",
1798
+ reason: "invalid_input_type",
1799
+ expected: "JSON string, plain object, or array",
1800
+ received: text === null ? "null" : typeof text
1801
+ }
1802
+ }
1803
+ );
1804
+ }
1805
+ if (!Array.isArray(parsed) && !isPlainObject2(parsed)) {
1806
+ throw new LlmExeError(`Invalid JSON root type.`, {
1807
+ code: "parser.parse_failed",
1808
+ context: {
1809
+ operation: "JsonParser.parse",
1810
+ parser: "json",
1811
+ reason: "invalid_json_root_type",
1812
+ expected: "JSON object or array",
1813
+ received: parsed === null ? "null" : typeof parsed,
1814
+ inputLength
1815
+ }
1816
+ });
1817
+ }
1818
+ if (this.schema) {
1819
+ if (this.shouldValidateSchema) {
1820
+ const valid = validateParserSchema(this.schema, parsed);
1821
+ if (valid && valid.length) {
1822
+ throw new LlmExeError(valid[0].message, {
1823
+ code: "parser.schema_validation_failed",
1824
+ context: {
1825
+ operation: "JsonParser.parse",
1826
+ parser: "json",
1827
+ schemaErrors: valid.map((error) => error.message)
1828
+ }
1829
+ });
1830
+ }
1831
+ }
1832
+ if (this.shouldValidateSchema) {
1833
+ return applyParserSchemaDefaultsAndFilter(
1834
+ this.schema,
1835
+ parsed
1836
+ );
1837
+ }
1838
+ return enforceParserSchema(this.schema, parsed);
1839
+ }
1840
+ return parsed;
1841
+ }
1842
+ };
1843
+
1844
+ // src/utils/modules/camelCase.ts
1845
+ function camelCase(input) {
1846
+ if (!input) return input;
1847
+ input = input.replace(/[^a-zA-Z0-9_]+/g, " ").trim();
1848
+ const words = input.split(/\s+|_/);
1849
+ return words.map((word, index) => {
1850
+ if (index === 0) {
1851
+ return word.toLowerCase();
1852
+ } else {
1853
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
1854
+ }
1855
+ }).join("");
1856
+ }
1857
+
1858
+ // src/parser/_listBoundary.ts
1859
+ var LIST_MARKER_PATTERN = /^(?:[-*]\s+|\d+\.\s+|•\s*)/;
1860
+ function normalizeListLines(input, context) {
1861
+ const lines = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1862
+ if (lines.length === 0) {
1863
+ throw new LlmExeError(`No list items found in input.`, {
1864
+ code: "parser.parse_failed",
1865
+ context: {
1866
+ operation: context.operation,
1867
+ parser: context.parser,
1868
+ reason: "empty_input",
1869
+ inputLength: input.length
1870
+ }
1871
+ });
1872
+ }
1873
+ const markedStates = lines.map((line) => LIST_MARKER_PATTERN.test(line));
1874
+ const hasMarked = markedStates.some(Boolean);
1875
+ const hasUnmarked = markedStates.some((marked) => !marked);
1876
+ if (hasMarked && hasUnmarked) {
1877
+ throw new LlmExeError(`Mixed marked and unmarked list items found.`, {
1878
+ code: "parser.parse_failed",
1879
+ context: {
1880
+ operation: context.operation,
1881
+ parser: context.parser,
1882
+ reason: "mixed_list_markers",
1883
+ inputLength: input.length
1884
+ }
1885
+ });
1886
+ }
1887
+ return {
1888
+ lines: hasMarked ? lines.map((line) => line.replace(LIST_MARKER_PATTERN, "").trim()) : lines,
1889
+ marked: hasMarked
1890
+ };
1891
+ }
1892
+
1893
+ // src/parser/parsers/ListToJsonParser.ts
1894
+ var ListToJsonParser = class extends BaseParserWithJson {
1895
+ constructor(options = {}) {
1896
+ super("listToJson", options);
1897
+ __publicField(this, "keyTransform");
1898
+ __publicField(this, "shouldValidateSchema");
1899
+ this.keyTransform = options.keyTransform ?? "camelCase";
1900
+ this.shouldValidateSchema = !!options.schema && options.validateSchema !== false;
1901
+ }
1902
+ /**
1903
+ * v3 parser contract:
1904
+ * Category: converter
1905
+ * Mode: line-oriented format conversion
1906
+ *
1907
+ * Uses the shared list boundary. Parses normalized lines as key/value pairs
1908
+ * split at the first colon. Duplicate keys throw because object output would
1909
+ * overwrite data.
1910
+ *
1911
+ */
1912
+ parse(text) {
1913
+ if (typeof text !== "string") {
1914
+ throw new LlmExeError(
1915
+ `Invalid input. Expected string. Received ${text === null ? "null" : Array.isArray(text) ? "array" : typeof text}.`,
1916
+ {
1917
+ code: "parser.invalid_input",
1918
+ context: {
1919
+ operation: "ListToJsonParser.parse",
1920
+ parser: "listToJson",
1921
+ reason: "invalid_input_type",
1922
+ expected: "string",
1923
+ received: text === null ? "null" : Array.isArray(text) ? "array" : typeof text
1924
+ }
1925
+ }
1926
+ );
1927
+ }
1928
+ const { lines } = normalizeListLines(text, {
1929
+ operation: "ListToJsonParser.parse",
1930
+ parser: "listToJson"
1931
+ });
1932
+ const output = {};
1933
+ lines.forEach((line) => {
1934
+ const colonIndex = line.indexOf(":");
1935
+ if (colonIndex === -1) {
1936
+ throw new LlmExeError(`Malformed key/value line.`, {
1937
+ code: "parser.parse_failed",
1938
+ context: {
1939
+ operation: "ListToJsonParser.parse",
1940
+ parser: "listToJson",
1941
+ reason: "malformed_line",
1942
+ inputLength: text.length
1943
+ }
1944
+ });
1945
+ }
1946
+ const key = line.slice(0, colonIndex).trim();
1947
+ if (!key) {
1948
+ throw new LlmExeError(`Empty key in key/value line.`, {
1949
+ code: "parser.parse_failed",
1950
+ context: {
1951
+ operation: "ListToJsonParser.parse",
1952
+ parser: "listToJson",
1953
+ reason: "empty_key",
1954
+ inputLength: text.length
1955
+ }
1956
+ });
1957
+ }
1958
+ const transformedKey = this.keyTransform === "preserve" ? key : camelCase(key);
1959
+ if (Object.prototype.hasOwnProperty.call(output, transformedKey)) {
1960
+ throw new LlmExeError(`Duplicate key in key/value input.`, {
1961
+ code: "parser.parse_failed",
1962
+ context: {
1963
+ operation: "ListToJsonParser.parse",
1964
+ parser: "listToJson",
1965
+ reason: "duplicate_key",
1966
+ inputLength: text.length
1967
+ }
1968
+ });
1969
+ }
1970
+ output[transformedKey] = line.slice(colonIndex + 1).trim();
1971
+ });
1972
+ if (this.schema) {
1973
+ const coerced = coerceParserSchemaValues(this.schema, output);
1974
+ if (this.shouldValidateSchema) {
1975
+ const valid = validateParserSchema(this.schema, coerced);
1976
+ if (valid && valid.length) {
1977
+ throw new LlmExeError(valid[0].message, {
1978
+ code: "parser.schema_validation_failed",
1979
+ context: {
1980
+ operation: "ListToJsonParser.parse",
1981
+ parser: "listToJson",
1982
+ schemaErrors: valid.map((error) => error.message)
1983
+ }
1984
+ });
1985
+ }
1986
+ }
1987
+ return applyParserSchemaDefaultsAndFilter(this.schema, coerced);
1988
+ }
1989
+ return output;
1990
+ }
1991
+ };
1992
+
1993
+ // src/parser/parsers/ListToKeyValueParser.ts
1994
+ var ListToKeyValueParser = class extends BaseParser {
1995
+ constructor(options) {
1996
+ super("listToKeyValue");
1997
+ __publicField(this, "keyTransform");
1998
+ this.keyTransform = options?.keyTransform ?? "preserve";
1999
+ }
2000
+ /**
2001
+ * v3 parser contract:
2002
+ * Category: converter
2003
+ * Mode: line-oriented collector
2004
+ *
2005
+ * Uses the shared list boundary. Parses normalized lines as key/value pairs
2006
+ * split at the first colon. Preserves duplicate keys because output is an
2007
+ * ordered array. Keys are returned as written by default; pass
2008
+ * keyTransform: "camelCase" to match listToJson's key normalization.
2009
+ *
2010
+ */
2011
+ parse(text, _attributes) {
2012
+ if (typeof text !== "string") {
2013
+ throw new LlmExeError(
2014
+ `Invalid input. Expected string. Received ${text === null ? "null" : Array.isArray(text) ? "array" : typeof text}.`,
2015
+ {
2016
+ code: "parser.invalid_input",
2017
+ context: {
2018
+ operation: "ListToKeyValueParser.parse",
2019
+ parser: "listToKeyValue",
2020
+ reason: "invalid_input_type",
2021
+ expected: "string",
2022
+ received: text === null ? "null" : Array.isArray(text) ? "array" : typeof text
2023
+ }
2024
+ }
2025
+ );
2026
+ }
2027
+ const { lines } = normalizeListLines(text, {
2028
+ operation: "ListToKeyValueParser.parse",
2029
+ parser: "listToKeyValue"
2030
+ });
2031
+ let res = [];
2032
+ for (const line of lines) {
2033
+ const colonIndex = line.indexOf(":");
2034
+ if (colonIndex === -1) {
2035
+ throw new LlmExeError(`Malformed key/value line.`, {
2036
+ code: "parser.parse_failed",
2037
+ context: {
2038
+ operation: "ListToKeyValueParser.parse",
2039
+ parser: "listToKeyValue",
2040
+ reason: "malformed_line",
2041
+ inputLength: text.length
2042
+ }
2043
+ });
2044
+ }
2045
+ const rawKey = line.slice(0, colonIndex).trim();
2046
+ if (!rawKey) {
2047
+ throw new LlmExeError(`Empty key in key/value line.`, {
2048
+ code: "parser.parse_failed",
2049
+ context: {
2050
+ operation: "ListToKeyValueParser.parse",
2051
+ parser: "listToKeyValue",
2052
+ reason: "empty_key",
2053
+ inputLength: text.length
2054
+ }
2055
+ });
2056
+ }
2057
+ const key = this.keyTransform === "camelCase" ? camelCase(rawKey) : rawKey;
2058
+ res.push({ key, value: line.slice(colonIndex + 1).trim() });
2059
+ }
2060
+ return res;
2061
+ }
2062
+ };
2063
+
2064
+ // src/parser/parsers/CustomParser.ts
2065
+ var CustomParser = class extends BaseParser {
2066
+ constructor(name, parserFn) {
2067
+ super(name);
2068
+ __publicField(this, "parserFn");
2069
+ this.parserFn = parserFn;
2070
+ }
2071
+ parse(text, context) {
2072
+ return this.parserFn.call(this, text, context);
2073
+ }
2074
+ };
2075
+
2076
+ // src/parser/parsers/ListToArrayParser.ts
2077
+ var ListToArrayParser = class extends BaseParser {
2078
+ constructor() {
2079
+ super("listToArray");
2080
+ }
2081
+ parse(text, _attributes) {
2082
+ if (typeof text !== "string") {
2083
+ throw new LlmExeError(
2084
+ `Invalid input. Expected string. Received ${text === null ? "null" : Array.isArray(text) ? "array" : typeof text}.`,
2085
+ {
2086
+ code: "parser.invalid_input",
2087
+ context: {
2088
+ operation: "ListToArrayParser.parse",
2089
+ parser: "listToArray",
2090
+ reason: "invalid_input_type",
2091
+ expected: "string",
2092
+ received: text === null ? "null" : Array.isArray(text) ? "array" : typeof text
2093
+ }
2094
+ }
2095
+ );
2096
+ }
2097
+ const normalized = normalizeListLines(text, {
2098
+ operation: "ListToArrayParser.parse",
2099
+ parser: "listToArray"
2100
+ });
2101
+ if (!normalized.marked && normalized.lines.length === 1) {
2102
+ throw new LlmExeError(`Input is not list-like.`, {
2103
+ code: "parser.parse_failed",
2104
+ context: {
2105
+ operation: "ListToArrayParser.parse",
2106
+ parser: "listToArray",
2107
+ reason: "not_list_like",
2108
+ inputLength: text.length
2109
+ }
2110
+ });
2111
+ }
2112
+ return normalized.lines;
2113
+ }
2114
+ };
2115
+
2116
+ // src/utils/modules/handlebars/hbs.ts
2117
+ import Handlebars from "handlebars";
2118
+
2119
+ // src/utils/modules/handlebars/utils/registerHelpers.ts
2120
+ function _registerHelpers(helpers, instance) {
2121
+ if (helpers && Array.isArray(helpers)) {
2122
+ for (const helper of helpers) {
2123
+ if (helper.name && typeof helper.name === "string" && typeof helper.handler === "function") {
2124
+ if (instance) {
2125
+ instance.registerHelper(helper.name, helper.handler);
2126
+ }
2127
+ }
2128
+ }
2129
+ }
2130
+ }
2131
+
2132
+ // src/utils/modules/handlebars/templates/index.ts
2133
+ var MarkdownCode = `{{#if code}}\`\`\`{{#if language}}{{language}}{{/if}}
2134
+ {{{code}}}
2135
+ \`\`\`{{/if}}`;
2136
+ var ThoughtActionResult = `
2137
+ {{#if title}}{{#if attributes.stepsTaken.length}}{{title}}
2138
+ {{/if}}{{/if}}
2139
+ {{#each attributes.stepsTaken as | step |}}
2140
+ {{#if step.thought}}Thought: {{step.result}}{{/if}}
2141
+ {{#if step.result}}Action: {{step.action}}{{/if}}
2142
+ {{#if step.result}}Result: {{step.result}}{{/if}}
2143
+ {{/each}}`;
2144
+ var ChatConversationHistory = `{{~#if title}}{{~#if chat_history.length}}{{title}}
2145
+ {{~/if}}{{~/if}}
2146
+ {{#each chat_history as | item |}}
2147
+ {{~#eq item.role 'user'}}{{#if @last}}{{../mostRecentRolePrefix}}{{../userName}}{{../mostRecentRoleSuffix}}{{else}}{{../userName}}{{/if}}: {{#if @last}}{{../mostRecentMessagePrefix}}{{/if}}{{{item.content}}}{{#if @last}}{{../mostRecentMessageSuffix}}{{/if}}
2148
+ {{/eq}}
2149
+ {{~#eq item.role 'assistant'}}{{#if @last}}{{../mostRecentRolePrefix}}{{../assistantName}}{{../mostRecentRoleSuffix}}{{else}}{{../assistantName}}{{/if}}: {{#if @last}}{{../mostRecentMessagePrefix}}{{/if}}{{{item.content}}}{{#if @last}}{{../mostRecentMessageSuffix}}{{/if}}
2150
+ {{/eq}}
2151
+ {{~#eq item.role 'system'}}{{../systemName}}: {{{item.content}}}
2152
+ {{/eq}}
2153
+ {{~/each}}`;
2154
+ var DialogueHistory = `{{>ChatConversationHistory title=title chat_history=(getKeyOr key []) assistantName=(getOr assistant 'Assistant') userName=(getOr user 'User') systemName=(getOr system 'System') mostRecentRolePrefix=mostRecentRolePrefix mostRecentRoleSuffix=mostRecentRoleSuffix mostRecentMessagePrefix=mostRecentMessagePrefix mostRecentMessageSuffix=mostRecentMessageSuffix}}`;
2155
+ var SingleChatMessage = `{{~#eq role 'user'}}{{getOr name 'User'}}: {{{content}}}{{~/eq}}
2156
+ {{~#eq role 'assistant'}}{{getOr assistant 'Assistant'}}: {{{content}}}{{~/eq}}
2157
+ {{~#eq role 'system'}}{{getOr system 'System'}}: {{{content}}}{{~/eq}}`;
2158
+ var ThoughtsAndObservations = `{{~#each thoughts as | step |}}
2159
+ {{~#if step.thought}}Thought: {{{step.thought}}}
2160
+ {{/if}}
2161
+ {{~#if step.observation}}Observation: {{{step.observation}}}
2162
+ {{/if}}
2163
+ {{~/each}}`;
2164
+ var JsonSchema = `{{#if (getKeyOr key false)}}
2165
+ \`\`\`json
2166
+ {{{indentJson (getKeyOr key) collapse}}}
2167
+ \`\`\`
2168
+ {{~/if}}`;
2169
+ var JsonSchemaExampleJson = `{{#if (getOr key false)}}
2170
+ \`\`\`json
2171
+ {{{jsonSchemaExample key (getOr property '') collapse}}}
2172
+ \`\`\`
2173
+ {{~/if}}`;
2174
+ var partials = {
2175
+ JsonSchema,
2176
+ JsonSchemaExampleJson,
2177
+ MarkdownCode,
2178
+ DialogueHistory,
2179
+ SingleChatMessage,
2180
+ ChatConversationHistory,
2181
+ ThoughtsAndObservations,
2182
+ ThoughtActionResult
2183
+ };
2184
+
2185
+ // src/utils/modules/handlebars/helpers/index.ts
2186
+ var helpers_exports = {};
2187
+ __export(helpers_exports, {
2188
+ cut: () => cutFn,
2189
+ eq: () => eq,
2190
+ getKeyOr: () => getKeyOr,
2191
+ getOr: () => getOr,
2192
+ ifCond: () => ifCond,
2193
+ indentJson: () => indentJson,
2194
+ join: () => join,
2195
+ jsonSchemaExample: () => jsonSchemaExample,
2196
+ neq: () => neq,
2197
+ objectToList: () => objectToList,
2198
+ pluralize: () => pluralize,
2199
+ substring: () => substringFn,
2200
+ with: () => withFn
2201
+ });
2202
+
2203
+ // src/utils/modules/json.ts
2204
+ var maybeStringifyJSON = (objOrMaybeString) => {
2205
+ if (!objOrMaybeString || typeof objOrMaybeString !== "object") {
2206
+ return objOrMaybeString;
2207
+ }
2208
+ try {
2209
+ const result = JSON.stringify(objOrMaybeString);
2210
+ return result;
2211
+ } catch (error) {
2212
+ }
2213
+ return "";
2214
+ };
2215
+ var maybeParseJSON = (objOrMaybeJSON) => {
2216
+ if (!objOrMaybeJSON) return {};
2217
+ if (typeof objOrMaybeJSON === "string") {
2218
+ try {
2219
+ const cleanMarkdown = helpJsonMarkup(objOrMaybeJSON);
2220
+ const result = JSON.parse(cleanMarkdown);
2221
+ if (typeof result === "object" && result !== null) {
2222
+ return result;
2223
+ }
2224
+ } catch (error) {
2225
+ }
2226
+ }
2227
+ if (typeof objOrMaybeJSON === "object" && objOrMaybeJSON !== null) {
2228
+ return objOrMaybeJSON;
2229
+ }
2230
+ return {};
2231
+ };
789
2232
  function isObjectStringified(maybeObject) {
790
2233
  if (typeof maybeObject !== "string") return false;
791
2234
  const trimmed = maybeObject.trim();
@@ -1047,7 +2490,15 @@ function isEmpty(value) {
1047
2490
  // src/utils/modules/handlebars/helpers/async/with.ts
1048
2491
  async function withFnAsync(context, options) {
1049
2492
  if (arguments.length !== 2) {
1050
- throw new Error("#with requires exactly one argument");
2493
+ throw new LlmExeError("#with requires exactly one argument", {
2494
+ code: "template.invalid_helper_arguments",
2495
+ context: {
2496
+ operation: "handlebars.asyncHelper.with",
2497
+ helper: "with",
2498
+ expected: 1,
2499
+ received: arguments.length
2500
+ }
2501
+ });
1051
2502
  }
1052
2503
  if (isPromise(context)) {
1053
2504
  context = await context;
@@ -1075,7 +2526,15 @@ async function withFnAsync(context, options) {
1075
2526
  // src/utils/modules/handlebars/helpers/async/if.ts
1076
2527
  async function ifFnAsync(conditional, options) {
1077
2528
  if (arguments.length !== 2) {
1078
- throw new Error("#if requires exactly one argument");
2529
+ throw new LlmExeError("#if requires exactly one argument", {
2530
+ code: "template.invalid_helper_arguments",
2531
+ context: {
2532
+ operation: "handlebars.asyncHelper.if",
2533
+ helper: "if",
2534
+ expected: 1,
2535
+ received: arguments.length
2536
+ }
2537
+ });
1079
2538
  }
1080
2539
  if (isPromise(conditional)) {
1081
2540
  conditional = await conditional;
@@ -1097,7 +2556,15 @@ function isReadableStream(obj) {
1097
2556
  // src/utils/modules/handlebars/helpers/async/each.ts
1098
2557
  async function eachFnAsync(arg1, options) {
1099
2558
  if (!options) {
1100
- throw new Error("Must pass iterator to #each");
2559
+ throw new LlmExeError("Must pass iterator to #each", {
2560
+ code: "template.invalid_helper_arguments",
2561
+ context: {
2562
+ operation: "handlebars.asyncHelper.each",
2563
+ helper: "each",
2564
+ expected: "an iterator",
2565
+ received: typeof options
2566
+ }
2567
+ });
1101
2568
  }
1102
2569
  const { fn } = options;
1103
2570
  const { inverse } = options;
@@ -1193,7 +2660,15 @@ async function eachFnAsync(arg1, options) {
1193
2660
  // src/utils/modules/handlebars/helpers/async/unless.ts
1194
2661
  async function unlessFnAsync(conditional, options) {
1195
2662
  if (arguments.length !== 2) {
1196
- throw new Error("#unless requires exactly one argument");
2663
+ throw new LlmExeError("#unless requires exactly one argument", {
2664
+ code: "template.invalid_helper_arguments",
2665
+ context: {
2666
+ operation: "handlebars.asyncHelper.unless",
2667
+ helper: "unless",
2668
+ expected: 1,
2669
+ received: arguments.length
2670
+ }
2671
+ });
1197
2672
  }
1198
2673
  return ifFnAsync.call(this, conditional, {
1199
2674
  fn: options.inverse,
@@ -1366,455 +2841,627 @@ function replaceTemplateString(templateString, substitutions = {}, configuration
1366
2841
  const tempPartials = [];
1367
2842
  if (Array.isArray(configuration.helpers)) {
1368
2843
  hbs.registerHelpers(configuration.helpers);
1369
- tempHelpers.push(...configuration.helpers.map((a) => a.name));
1370
- }
1371
- if (Array.isArray(configuration.partials)) {
1372
- hbs.registerPartials(configuration.partials);
1373
- tempPartials.push(...configuration.partials.map((a) => a.name));
1374
- }
1375
- const template = hbs.handlebars.compile(templateString);
1376
- const res = template(substitutions, {
1377
- allowedProtoMethods: {
1378
- substring: true
1379
- }
1380
- });
1381
- tempHelpers.forEach(function(n) {
1382
- hbs.handlebars.unregisterHelper(n);
1383
- });
1384
- tempPartials.forEach(function(n) {
1385
- hbs.handlebars.unregisterPartial(n);
1386
- });
1387
- return res;
1388
- }
1389
-
1390
- // src/utils/modules/replaceTemplateStringAsync.ts
1391
- async function replaceTemplateStringAsync(templateString, substitutions = {}, configuration = {
1392
- helpers: [],
1393
- partials: []
1394
- }) {
1395
- if (!templateString) return Promise.resolve(templateString || "");
1396
- const tempHelpers = [];
1397
- const tempPartials = [];
1398
- if (Array.isArray(configuration.helpers)) {
1399
- hbsAsync.registerHelpers(configuration.helpers);
1400
- tempHelpers.push(...configuration.helpers.map((a) => a.name));
1401
- }
1402
- if (Array.isArray(configuration.partials)) {
1403
- hbsAsync.registerPartials(configuration.partials);
1404
- tempPartials.push(...configuration.partials.map((a) => a.name));
1405
- }
1406
- const template = hbsAsync.handlebars.compile(templateString);
1407
- const res = await template(substitutions, {
1408
- allowedProtoMethods: {
1409
- substring: true
1410
- }
1411
- });
1412
- tempHelpers.forEach(function(n) {
1413
- hbsAsync.handlebars.unregisterHelper(n);
1414
- });
1415
- tempPartials.forEach(function(n) {
1416
- hbsAsync.handlebars.unregisterPartial(n);
1417
- });
1418
- return res;
1419
- }
1420
-
1421
- // src/utils/modules/asyncCallWithTimeout.ts
1422
- var asyncCallWithTimeout = async (asyncPromise, timeLimit = 1e4) => {
1423
- let timeoutHandle;
1424
- const timeoutPromise = new Promise((_resolve, reject) => {
1425
- timeoutHandle = setTimeout(() => {
1426
- return reject(
1427
- new Error(`LLM call timed out after ${timeLimit}ms`)
1428
- );
1429
- }, timeLimit);
1430
- });
1431
- return Promise.race([asyncPromise, timeoutPromise]).finally(() => {
1432
- clearTimeout(timeoutHandle);
1433
- });
1434
- };
1435
-
1436
- // src/utils/modules/guessProviderFromModel.ts
1437
- function isModelKnownOpenAi(payload) {
1438
- const model = payload.model.toLowerCase();
1439
- if (model.startsWith("gpt-") || model.startsWith("chatgpt-")) {
1440
- return true;
1441
- }
1442
- if (model === "o1" || model.startsWith("o1-")) {
1443
- return true;
1444
- }
1445
- if (model === "o3-mini") {
1446
- return true;
1447
- }
1448
- return false;
1449
- }
1450
- function isModelKnownAnthropic(payload) {
1451
- const model = payload.model.toLowerCase();
1452
- if (model.startsWith("claude-")) {
1453
- return true;
1454
- }
1455
- return false;
1456
- }
1457
- function isModelKnownXai(payload) {
1458
- const model = payload.model.toLowerCase();
1459
- if (model.startsWith("grok-")) {
1460
- return true;
1461
- }
1462
- return false;
1463
- }
1464
- function isModelKnownBedrockAnthropic(payload) {
1465
- const model = payload.model.toLowerCase();
1466
- if (model.startsWith("anthropic.claude-")) {
1467
- return true;
1468
- }
1469
- return false;
1470
- }
1471
- function guessProviderFromModel(payload) {
1472
- switch (true) {
1473
- case isModelKnownOpenAi(payload):
1474
- return "openai";
1475
- case isModelKnownXai(payload):
1476
- return "xai";
1477
- case isModelKnownBedrockAnthropic(payload):
1478
- return "bedrock:anthropic";
1479
- case isModelKnownAnthropic(payload):
1480
- return "anthropic";
1481
- default:
1482
- throw new Error("Unsupported model");
1483
- }
1484
- }
1485
-
1486
- // src/utils/modules/index.ts
1487
- function registerHelpers(helpers) {
1488
- hbs.registerHelpers(helpers);
1489
- hbsAsync.registerHelpers(helpers);
1490
- }
1491
- function registerPartials(partials2) {
1492
- hbs.registerPartials(partials2);
1493
- hbsAsync.registerPartials(partials2);
1494
- }
1495
-
1496
- // src/parser/_utils.ts
1497
- import { validate as validateSchema } from "jsonschema";
1498
- function enforceParserSchema(schema, parsed) {
1499
- if (!schema || !parsed || typeof parsed !== "object") {
1500
- return parsed;
1501
- }
1502
- return filterObjectOnSchema(schema, parsed);
1503
- }
1504
- function validateParserSchema(schema, parsed) {
1505
- if (!schema || !parsed || typeof parsed !== "object") {
1506
- return null;
1507
- }
1508
- const validate = validateSchema(parsed, schema);
1509
- if (validate.errors.length) {
1510
- return validate.errors;
2844
+ tempHelpers.push(...configuration.helpers.map((a) => a.name));
1511
2845
  }
1512
- return null;
2846
+ if (Array.isArray(configuration.partials)) {
2847
+ hbs.registerPartials(configuration.partials);
2848
+ tempPartials.push(...configuration.partials.map((a) => a.name));
2849
+ }
2850
+ const template = hbs.handlebars.compile(templateString);
2851
+ const res = template(substitutions, {
2852
+ allowedProtoMethods: {
2853
+ substring: true
2854
+ }
2855
+ });
2856
+ tempHelpers.forEach(function(n) {
2857
+ hbs.handlebars.unregisterHelper(n);
2858
+ });
2859
+ tempPartials.forEach(function(n) {
2860
+ hbs.handlebars.unregisterPartial(n);
2861
+ });
2862
+ return res;
1513
2863
  }
1514
2864
 
1515
- // src/parser/parsers/JsonParser.ts
1516
- var JsonParser = class extends BaseParserWithJson {
1517
- constructor(options = {}) {
1518
- super("json", options);
2865
+ // src/parser/parsers/ReplaceStringTemplateParser.ts
2866
+ var ReplaceStringTemplateParser = class extends BaseParser {
2867
+ constructor() {
2868
+ super("replaceStringTemplate");
1519
2869
  }
1520
- parse(text, _attributes) {
1521
- const parsed = maybeParseJSON(helpJsonMarkup(text));
1522
- if (this.schema) {
1523
- const enforce = enforceParserSchema(this.schema, parsed);
1524
- if (this.validateSchema) {
1525
- const valid = validateParserSchema(this.schema, enforce);
1526
- if (valid && valid.length) {
1527
- throw new LlmExeError(valid[0].message, "parser", {
1528
- parser: "json",
1529
- output: parsed,
1530
- error: valid[0].message
1531
- });
2870
+ parse(text, attributes) {
2871
+ if (typeof text !== "string") {
2872
+ throw new LlmExeError(
2873
+ `Invalid input. Expected string. Received ${text === null ? "null" : Array.isArray(text) ? "array" : typeof text}.`,
2874
+ {
2875
+ code: "parser.invalid_input",
2876
+ context: {
2877
+ operation: "ReplaceStringTemplateParser.parse",
2878
+ parser: "replaceStringTemplate",
2879
+ reason: "invalid_input_type",
2880
+ expected: "string",
2881
+ received: text === null ? "null" : Array.isArray(text) ? "array" : typeof text
2882
+ }
2883
+ }
2884
+ );
2885
+ }
2886
+ if (attributes !== void 0 && (attributes === null || typeof attributes !== "object" || Array.isArray(attributes))) {
2887
+ throw new LlmExeError(`Invalid attributes. Expected object.`, {
2888
+ code: "parser.invalid_input",
2889
+ context: {
2890
+ operation: "ReplaceStringTemplateParser.parse",
2891
+ parser: "replaceStringTemplate",
2892
+ reason: "invalid_attributes",
2893
+ expected: "object",
2894
+ received: attributes === null ? "null" : Array.isArray(attributes) ? "array" : typeof attributes
1532
2895
  }
2896
+ });
2897
+ }
2898
+ try {
2899
+ return replaceTemplateString(text, attributes);
2900
+ } catch (cause) {
2901
+ let received = typeof cause;
2902
+ if (cause instanceof Error) {
2903
+ received = cause.name;
1533
2904
  }
1534
- return enforce;
2905
+ throw new LlmExeError(`Template replacement failed.`, {
2906
+ code: "parser.parse_failed",
2907
+ context: {
2908
+ operation: "ReplaceStringTemplateParser.parse",
2909
+ parser: "replaceStringTemplate",
2910
+ reason: "template_replacement_failed",
2911
+ inputLength: text.length,
2912
+ received
2913
+ },
2914
+ cause
2915
+ });
1535
2916
  }
1536
- return parsed;
1537
2917
  }
1538
2918
  };
1539
2919
 
1540
- // src/utils/modules/camelCase.ts
1541
- function camelCase(input) {
1542
- if (!input) return input;
1543
- input = input.replace(/[^a-zA-Z0-9_]+/g, " ").trim();
1544
- const words = input.split(/\s+|_/);
1545
- return words.map((word, index) => {
1546
- if (index === 0) {
1547
- return word.toLowerCase();
1548
- } else {
1549
- return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
1550
- }
1551
- }).join("");
1552
- }
1553
-
1554
- // src/parser/parsers/ListToJsonParser.ts
1555
- var ListToJsonParser = class extends BaseParserWithJson {
1556
- constructor(options = {}) {
1557
- super("listToJson", options);
1558
- __publicField(this, "keyTransform");
1559
- this.keyTransform = options.keyTransform ?? "camelCase";
2920
+ // src/parser/parsers/MarkdownCodeBlocks.ts
2921
+ var MarkdownCodeBlocksParser = class extends BaseParser {
2922
+ constructor() {
2923
+ super("markdownCodeBlocks");
1560
2924
  }
1561
- parse(text) {
1562
- const lines = text.split("\n");
1563
- const output = {};
1564
- lines.forEach((line) => {
1565
- const colonIndex = line.indexOf(":");
1566
- if (colonIndex !== -1) {
1567
- const key = line.slice(0, colonIndex);
1568
- const value = line.slice(colonIndex + 1).trim();
1569
- if (value) {
1570
- const transformedKey = this.keyTransform === "preserve" ? key.trim() : camelCase(key);
1571
- output[transformedKey] = value;
2925
+ parse(input, _attributes) {
2926
+ if (typeof input !== "string") {
2927
+ throw new LlmExeError(
2928
+ `Invalid input. Expected string. Received ${input === null ? "null" : Array.isArray(input) ? "array" : typeof input}.`,
2929
+ {
2930
+ code: "parser.invalid_input",
2931
+ context: {
2932
+ operation: "MarkdownCodeBlocksParser.parse",
2933
+ parser: "markdownCodeBlocks",
2934
+ reason: "invalid_input_type",
2935
+ expected: "string",
2936
+ received: input === null ? "null" : Array.isArray(input) ? "array" : typeof input
2937
+ }
1572
2938
  }
1573
- }
1574
- });
1575
- if (this.schema) {
1576
- const parsed = enforceParserSchema(this.schema, output);
1577
- if (this?.validateSchema) {
1578
- const valid = validateParserSchema(this.schema, parsed);
1579
- if (valid && valid.length) {
1580
- throw new LlmExeError(valid[0].message, "parser", {
1581
- parser: "json",
1582
- output: parsed,
1583
- error: valid[0].message
1584
- });
2939
+ );
2940
+ }
2941
+ const out = [];
2942
+ const fenceCount = input.match(/```/g)?.length ?? 0;
2943
+ if (fenceCount % 2 !== 0) {
2944
+ throw new LlmExeError(`Malformed markdown code block.`, {
2945
+ code: "parser.parse_failed",
2946
+ context: {
2947
+ operation: "MarkdownCodeBlocksParser.parse",
2948
+ parser: "markdownCodeBlocks",
2949
+ reason: "malformed_code_block",
2950
+ inputLength: input.length
1585
2951
  }
2952
+ });
2953
+ }
2954
+ const regex = input.matchAll(new RegExp(/```(\w*)\n([\s\S]*?)```/, "g"));
2955
+ for (const iterator of regex) {
2956
+ if (iterator) {
2957
+ const [_input, language, code] = iterator;
2958
+ out.push({
2959
+ language,
2960
+ code
2961
+ });
1586
2962
  }
1587
- return parsed;
1588
2963
  }
1589
- return output;
2964
+ return out;
1590
2965
  }
1591
2966
  };
1592
2967
 
1593
- // src/parser/parsers/ListToKeyValueParser.ts
1594
- var ListToKeyValueParser = class extends BaseParser {
1595
- constructor(options) {
1596
- super("listToKeyValue", options);
2968
+ // src/parser/parsers/MarkdownCodeBlock.ts
2969
+ var MarkdownCodeBlockParser = class extends BaseParser {
2970
+ constructor() {
2971
+ super("markdownCodeBlock");
1597
2972
  }
1598
- parse(text) {
1599
- const lines = text.split("\n").map((s) => s.replace("- ", "").replace(/'/g, "'"));
1600
- let res = [];
1601
- for (const line of lines) {
1602
- const [key, value] = line.split(":");
1603
- if (key && value) {
1604
- res.push({ key: key?.trim(), value: value?.trim() });
1605
- }
2973
+ parse(input, _attributes) {
2974
+ if (typeof input !== "string") {
2975
+ throw new LlmExeError(
2976
+ `Invalid input. Expected string. Received ${input === null ? "null" : Array.isArray(input) ? "array" : typeof input}.`,
2977
+ {
2978
+ code: "parser.invalid_input",
2979
+ context: {
2980
+ operation: "MarkdownCodeBlockParser.parse",
2981
+ parser: "markdownCodeBlock",
2982
+ reason: "invalid_input_type",
2983
+ expected: "string",
2984
+ received: input === null ? "null" : Array.isArray(input) ? "array" : typeof input
2985
+ }
2986
+ }
2987
+ );
1606
2988
  }
1607
- return res;
2989
+ if (input.trim() === "") {
2990
+ throw new LlmExeError(`No markdown code block found.`, {
2991
+ code: "parser.parse_failed",
2992
+ context: {
2993
+ operation: "MarkdownCodeBlockParser.parse",
2994
+ parser: "markdownCodeBlock",
2995
+ reason: "empty_input",
2996
+ inputLength: input.length
2997
+ }
2998
+ });
2999
+ }
3000
+ const blocks = new MarkdownCodeBlocksParser().parse(input);
3001
+ if (blocks.length === 0) {
3002
+ throw new LlmExeError(`No markdown code block found.`, {
3003
+ code: "parser.parse_failed",
3004
+ context: {
3005
+ operation: "MarkdownCodeBlockParser.parse",
3006
+ parser: "markdownCodeBlock",
3007
+ reason: "no_code_block",
3008
+ inputLength: input.length
3009
+ }
3010
+ });
3011
+ }
3012
+ if (blocks.length > 1) {
3013
+ throw new LlmExeError(`Multiple markdown code blocks found.`, {
3014
+ code: "parser.parse_failed",
3015
+ context: {
3016
+ operation: "MarkdownCodeBlockParser.parse",
3017
+ parser: "markdownCodeBlock",
3018
+ reason: "multiple_code_blocks",
3019
+ inputLength: input.length,
3020
+ matchCount: blocks.length
3021
+ }
3022
+ });
3023
+ }
3024
+ return blocks[0];
1608
3025
  }
1609
3026
  };
1610
3027
 
1611
- // src/parser/parsers/CustomParser.ts
1612
- var CustomParser = class extends BaseParser {
1613
- /**
1614
- * Creates a new CustomParser instance.
1615
- * @param {string} name The name of the parser.
1616
- * @param {any} parserFn The custom parsing function.
1617
- */
1618
- constructor(name, parserFn) {
1619
- super(name);
1620
- /**
1621
- * Custom parsing function.
1622
- * @type {any}
1623
- */
1624
- __publicField(this, "parserFn");
1625
- this.parserFn = parserFn;
3028
+ // src/parser/parsers/StringExtractParser.ts
3029
+ var REGEX_METACHAR = /[.*+?^${}()|[\]\\]/g;
3030
+ var WORD_CHAR = "[\\p{L}\\p{N}]";
3031
+ function escapeRegex(value) {
3032
+ return value.replace(REGEX_METACHAR, "\\$&");
3033
+ }
3034
+ function describeType(value) {
3035
+ if (value === null) return "null";
3036
+ if (Array.isArray(value)) return "array";
3037
+ return typeof value;
3038
+ }
3039
+ var StringExtractParser = class extends BaseParser {
3040
+ constructor(options) {
3041
+ super("stringExtract");
3042
+ __publicField(this, "enum", []);
3043
+ __publicField(this, "ignoreCase");
3044
+ __publicField(this, "match");
3045
+ if (options?.enum) {
3046
+ this.enum.push(...options.enum);
3047
+ }
3048
+ this.ignoreCase = options?.ignoreCase ?? true;
3049
+ this.match = options?.match ?? "word";
1626
3050
  }
1627
3051
  /**
1628
- * Parses the text using the custom parsing function.
1629
- * @param {string} text The text to be parsed.
1630
- * @param {any} inputValues Additional input values for the parser function.
1631
- * @returns {O} The parsed value.
3052
+ * v3 parser contract:
3053
+ * Category: extractor
3054
+ * Mode: configured value extraction
3055
+ *
3056
+ * Returns the single configured enum value found in input. Matching is
3057
+ * word-bounded by default; pass match: "exact" to require exact input or
3058
+ * match: "substring" for legacy contains() behavior. Case-insensitive by
3059
+ * default.
3060
+ *
1632
3061
  */
1633
- parse(text, inputValues) {
1634
- return this.parserFn.call(this, text, inputValues);
3062
+ parse(text, _attributes) {
3063
+ if (typeof text !== "string") {
3064
+ const received = describeType(text);
3065
+ throw new LlmExeError(
3066
+ `Invalid input. Expected string. Received ${received}.`,
3067
+ {
3068
+ code: "parser.invalid_input",
3069
+ context: {
3070
+ operation: "StringExtractParser.parse",
3071
+ parser: "stringExtract",
3072
+ reason: "invalid_input_type",
3073
+ expected: "string",
3074
+ received
3075
+ }
3076
+ }
3077
+ );
3078
+ }
3079
+ if (this.enum.length === 0) {
3080
+ throw new LlmExeError(`No enum values configured.`, {
3081
+ code: "parser.parse_failed",
3082
+ context: {
3083
+ operation: "StringExtractParser.parse",
3084
+ parser: "stringExtract",
3085
+ reason: "no_enum_values",
3086
+ expected: "non-empty enum",
3087
+ inputLength: text.length
3088
+ }
3089
+ });
3090
+ }
3091
+ if (text.length === 0) {
3092
+ if (this.enum.includes("")) {
3093
+ return "";
3094
+ }
3095
+ throw new LlmExeError(`No matching enum value found in input.`, {
3096
+ code: "parser.parse_failed",
3097
+ context: {
3098
+ operation: "StringExtractParser.parse",
3099
+ parser: "stringExtract",
3100
+ reason: "empty_input",
3101
+ expected: this.enum,
3102
+ inputLength: text.length
3103
+ }
3104
+ });
3105
+ }
3106
+ const matches = this.findMatches(text);
3107
+ const uniqueMatches = Array.from(new Set(matches));
3108
+ if (uniqueMatches.length === 1) {
3109
+ return uniqueMatches[0];
3110
+ }
3111
+ if (uniqueMatches.length > 1) {
3112
+ throw new LlmExeError(`Multiple enum values found in input.`, {
3113
+ code: "parser.parse_failed",
3114
+ context: {
3115
+ operation: "StringExtractParser.parse",
3116
+ parser: "stringExtract",
3117
+ reason: "ambiguous_enum_match",
3118
+ expected: this.enum,
3119
+ match: this.match,
3120
+ inputLength: text.length,
3121
+ matchCount: uniqueMatches.length
3122
+ }
3123
+ });
3124
+ }
3125
+ throw new LlmExeError(`No matching enum value found in input.`, {
3126
+ code: "parser.parse_failed",
3127
+ context: {
3128
+ operation: "StringExtractParser.parse",
3129
+ parser: "stringExtract",
3130
+ reason: "no_enum_match",
3131
+ expected: this.enum,
3132
+ match: this.match,
3133
+ inputLength: text.length
3134
+ }
3135
+ });
1635
3136
  }
1636
- };
1637
-
1638
- // src/parser/parsers/ListToArrayParser.ts
1639
- var ListToArrayParser = class extends BaseParser {
1640
- constructor() {
1641
- super("listToArray");
3137
+ findMatches(text) {
3138
+ switch (this.match) {
3139
+ case "exact":
3140
+ return this.matchExact(text);
3141
+ case "substring":
3142
+ return this.matchSubstring(text);
3143
+ case "word":
3144
+ default:
3145
+ return this.matchWord(text);
3146
+ }
3147
+ }
3148
+ matchExact(text) {
3149
+ const candidate = this.ignoreCase ? text.trim().toLowerCase() : text.trim();
3150
+ return this.enum.filter((option) => {
3151
+ if (option === "") return false;
3152
+ const normalized = this.ignoreCase ? option.toLowerCase() : option;
3153
+ return candidate === normalized;
3154
+ });
1642
3155
  }
1643
- parse(text) {
1644
- const lines = text.split("\n").map((s) => s.replace(/^(?:[-*] |\d+\. )/, "").replace(/'/g, "\u2019").trim());
1645
- return lines;
3156
+ matchSubstring(text) {
3157
+ const haystack = this.ignoreCase ? text.toLowerCase() : text;
3158
+ return this.enum.filter((option) => {
3159
+ if (option === "") return false;
3160
+ const needle = this.ignoreCase ? option.toLowerCase() : option;
3161
+ return haystack.includes(needle);
3162
+ });
3163
+ }
3164
+ matchWord(text) {
3165
+ const flags = this.ignoreCase ? "iu" : "u";
3166
+ return this.enum.filter((option) => {
3167
+ if (option === "") return false;
3168
+ const pattern = new RegExp(
3169
+ `(?<!${WORD_CHAR})${escapeRegex(option)}(?!${WORD_CHAR})`,
3170
+ flags
3171
+ );
3172
+ return pattern.test(text);
3173
+ });
1646
3174
  }
1647
3175
  };
1648
3176
 
1649
- // src/parser/parsers/ReplaceStringTemplateParser.ts
1650
- var ReplaceStringTemplateParser = class extends BaseParser {
1651
- constructor(options) {
1652
- super("replaceStringTemplate", options);
3177
+ // src/parser/_functions.ts
3178
+ function createParser(...args) {
3179
+ const [type, options] = args;
3180
+ switch (type) {
3181
+ case "json":
3182
+ return new JsonParser(options);
3183
+ case "listToJson":
3184
+ return new ListToJsonParser(options);
3185
+ case "stringExtract":
3186
+ return new StringExtractParser(options);
3187
+ case "markdownCodeBlocks":
3188
+ return new MarkdownCodeBlocksParser();
3189
+ case "markdownCodeBlock":
3190
+ return new MarkdownCodeBlockParser();
3191
+ case "listToArray":
3192
+ return new ListToArrayParser();
3193
+ case "listToKeyValue":
3194
+ return new ListToKeyValueParser(options);
3195
+ case "replaceStringTemplate":
3196
+ return new ReplaceStringTemplateParser();
3197
+ case "boolean":
3198
+ return new BooleanParser(options);
3199
+ case "number":
3200
+ return new NumberParser(options);
3201
+ case "string":
3202
+ return new StringParser();
3203
+ default:
3204
+ throw new LlmExeError(`Invalid parser type: "${type}"`, {
3205
+ code: "parser.invalid_type",
3206
+ context: {
3207
+ operation: "createParser",
3208
+ parser: type,
3209
+ availableParsers: [
3210
+ "json",
3211
+ "string",
3212
+ "boolean",
3213
+ "number",
3214
+ "stringExtract",
3215
+ "listToArray",
3216
+ "listToJson",
3217
+ "listToKeyValue",
3218
+ "replaceStringTemplate",
3219
+ "markdownCodeBlock",
3220
+ "markdownCodeBlocks"
3221
+ ],
3222
+ resolution: "Use a registered parser type, or define a custom parser."
3223
+ }
3224
+ });
1653
3225
  }
1654
- parse(text, attributes) {
1655
- return replaceTemplateString(text, attributes);
3226
+ }
3227
+ function createCustomParser(name, parserFn) {
3228
+ return new CustomParser(name, parserFn);
3229
+ }
3230
+
3231
+ // src/utils/index.ts
3232
+ var utils_exports = {};
3233
+ __export(utils_exports, {
3234
+ LLM_EXE_ERROR_SYMBOL: () => LLM_EXE_ERROR_SYMBOL,
3235
+ LlmExeError: () => LlmExeError,
3236
+ assert: () => assert,
3237
+ asyncCallWithTimeout: () => asyncCallWithTimeout,
3238
+ defineSchema: () => defineSchema,
3239
+ filterObjectOnSchema: () => filterObjectOnSchema,
3240
+ guessProviderFromModel: () => guessProviderFromModel,
3241
+ importHelpers: () => importHelpers,
3242
+ importPartials: () => importPartials,
3243
+ isLlmExeError: () => isLlmExeError,
3244
+ isObjectStringified: () => isObjectStringified,
3245
+ maybeParseJSON: () => maybeParseJSON,
3246
+ maybeStringifyJSON: () => maybeStringifyJSON,
3247
+ registerHelpers: () => registerHelpers,
3248
+ registerPartials: () => registerPartials,
3249
+ replaceTemplateString: () => replaceTemplateString,
3250
+ replaceTemplateStringAsync: () => replaceTemplateStringAsync
3251
+ });
3252
+
3253
+ // src/utils/modules/assert.ts
3254
+ function assert(condition, message) {
3255
+ if (condition === void 0 || condition === null || condition === false) {
3256
+ if (typeof message === "string") {
3257
+ throw new Error(message);
3258
+ } else if (message instanceof Error) {
3259
+ throw message;
3260
+ } else {
3261
+ throw new Error(`Assertion error`);
3262
+ }
1656
3263
  }
1657
- };
3264
+ }
1658
3265
 
1659
- // src/parser/singleKeyObjectToString.ts
1660
- function singleKeyObjectToString(input) {
1661
- try {
1662
- const parsed = JSON.parse(input);
1663
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
1664
- const keys = Object.keys(parsed);
1665
- if (keys.length === 1) {
1666
- const value = parsed[keys[0]];
1667
- if (typeof value === "string") {
1668
- return value;
1669
- } else {
1670
- return input;
1671
- }
3266
+ // src/utils/modules/defineSchema.ts
3267
+ import { asConst } from "json-schema-to-ts";
3268
+ function defineSchema(obj) {
3269
+ obj.additionalProperties = false;
3270
+ return asConst(obj);
3271
+ }
3272
+
3273
+ // src/utils/modules/handlebars/utils/importPartials.ts
3274
+ function importPartials(_partials) {
3275
+ let partials2 = [];
3276
+ if (_partials) {
3277
+ const externalPartialKeys = Object.keys(
3278
+ _partials
3279
+ );
3280
+ for (const externalPartialKey of externalPartialKeys) {
3281
+ if (typeof externalPartialKey === "string") {
3282
+ partials2.push({
3283
+ name: externalPartialKey,
3284
+ template: _partials[externalPartialKey]
3285
+ });
1672
3286
  }
1673
3287
  }
1674
- } catch {
1675
3288
  }
1676
- return input;
3289
+ return partials2;
1677
3290
  }
1678
3291
 
1679
- // src/parser/parsers/MarkdownCodeBlocks.ts
1680
- var MarkdownCodeBlocksParser = class extends BaseParser {
1681
- constructor(options) {
1682
- super("markdownCodeBlocks", options);
1683
- }
1684
- parse(input) {
1685
- const out = [];
1686
- if (isObjectStringified(input)) {
1687
- input = singleKeyObjectToString(input);
1688
- }
1689
- const regex = input.matchAll(new RegExp(/```(\w*)\n([\s\S]*?)```/, "g"));
1690
- for (const iterator of regex) {
1691
- if (iterator) {
1692
- const [_input, language, code] = iterator;
1693
- out.push({
1694
- language,
1695
- code
3292
+ // src/utils/modules/handlebars/utils/importHelpers.ts
3293
+ function importHelpers(_helpers) {
3294
+ let helpers = [];
3295
+ if (_helpers) {
3296
+ const externalHelperKeys = Object.keys(
3297
+ _helpers
3298
+ );
3299
+ for (const externalHelperKey of externalHelperKeys) {
3300
+ if (typeof externalHelperKey === "string") {
3301
+ helpers.push({
3302
+ name: externalHelperKey,
3303
+ handler: _helpers[externalHelperKey]
1696
3304
  });
1697
3305
  }
1698
3306
  }
1699
- return out;
1700
3307
  }
1701
- };
3308
+ return helpers;
3309
+ }
1702
3310
 
1703
- // src/parser/parsers/MarkdownCodeBlock.ts
1704
- var MarkdownCodeBlockParser = class extends BaseParser {
1705
- constructor(options) {
1706
- super("markdownCodeBlock", options);
3311
+ // src/utils/modules/replaceTemplateStringAsync.ts
3312
+ async function replaceTemplateStringAsync(templateString, substitutions = {}, configuration = {
3313
+ helpers: [],
3314
+ partials: []
3315
+ }) {
3316
+ if (!templateString) return Promise.resolve(templateString || "");
3317
+ const tempHelpers = [];
3318
+ const tempPartials = [];
3319
+ if (Array.isArray(configuration.helpers)) {
3320
+ hbsAsync.registerHelpers(configuration.helpers);
3321
+ tempHelpers.push(...configuration.helpers.map((a) => a.name));
1707
3322
  }
1708
- parse(input) {
1709
- const [block] = new MarkdownCodeBlocksParser().parse(input);
1710
- if (!block) {
1711
- return {
1712
- code: "",
1713
- language: ""
1714
- };
1715
- }
1716
- return block;
3323
+ if (Array.isArray(configuration.partials)) {
3324
+ hbsAsync.registerPartials(configuration.partials);
3325
+ tempPartials.push(...configuration.partials.map((a) => a.name));
1717
3326
  }
3327
+ const template = hbsAsync.handlebars.compile(templateString);
3328
+ const res = await template(substitutions, {
3329
+ allowedProtoMethods: {
3330
+ substring: true
3331
+ }
3332
+ });
3333
+ tempHelpers.forEach(function(n) {
3334
+ hbsAsync.handlebars.unregisterHelper(n);
3335
+ });
3336
+ tempPartials.forEach(function(n) {
3337
+ hbsAsync.handlebars.unregisterPartial(n);
3338
+ });
3339
+ return res;
3340
+ }
3341
+
3342
+ // src/utils/modules/asyncCallWithTimeout.ts
3343
+ var asyncCallWithTimeout = async (asyncPromise, timeLimit = 1e4) => {
3344
+ let timeoutHandle;
3345
+ const timeoutPromise = new Promise((_resolve, reject) => {
3346
+ timeoutHandle = setTimeout(() => {
3347
+ return reject(
3348
+ new Error(`LLM call timed out after ${timeLimit}ms`)
3349
+ );
3350
+ }, timeLimit);
3351
+ });
3352
+ return Promise.race([asyncPromise, timeoutPromise]).finally(() => {
3353
+ clearTimeout(timeoutHandle);
3354
+ });
1718
3355
  };
1719
3356
 
1720
- // src/parser/parsers/StringExtractParser.ts
1721
- var StringExtractParser = class extends BaseParser {
1722
- constructor(options) {
1723
- super("stringExtract", options);
1724
- __publicField(this, "enum", []);
1725
- __publicField(this, "ignoreCase");
1726
- if (options?.enum) {
1727
- this.enum.push(...options.enum);
1728
- }
1729
- if (options?.ignoreCase) {
1730
- this.ignoreCase = true;
1731
- }
3357
+ // src/utils/modules/guessProviderFromModel.ts
3358
+ function isModelKnownOpenAi(payload) {
3359
+ const model = payload.model.toLowerCase();
3360
+ if (model.startsWith("gpt-") || model.startsWith("chatgpt-")) {
3361
+ return true;
1732
3362
  }
1733
- parse(text) {
1734
- assert(
1735
- typeof text === "string",
1736
- `Invalid input. Expected string. Received ${typeof text}.`
1737
- );
1738
- for (const option of this.enum) {
1739
- const regex = this.ignoreCase ? new RegExp(option.toLowerCase(), "i") : new RegExp(option);
1740
- if (regex.test(text)) {
1741
- return option;
1742
- }
1743
- }
1744
- throw new LlmExeError(
1745
- `No matching enum value found in input.`,
1746
- "parser",
1747
- {
1748
- parser: "stringExtract",
1749
- output: text,
1750
- error: `No matching enum value found in input. Expected one of: ${this.enum.join(", ")}`
1751
- }
1752
- );
3363
+ if (model === "o1" || model.startsWith("o1-")) {
3364
+ return true;
1753
3365
  }
1754
- };
1755
-
1756
- // src/parser/_functions.ts
1757
- function createParser(type, options = {}) {
1758
- switch (type) {
1759
- case "json":
1760
- return new JsonParser(options);
1761
- case "markdownCodeBlocks":
1762
- return new MarkdownCodeBlocksParser();
1763
- case "markdownCodeBlock":
1764
- return new MarkdownCodeBlockParser();
1765
- case "listToArray":
1766
- return new ListToArrayParser();
1767
- case "listToJson":
1768
- return new ListToJsonParser(options);
1769
- case "listToKeyValue":
1770
- return new ListToKeyValueParser();
1771
- case "replaceStringTemplate":
1772
- return new ReplaceStringTemplateParser();
1773
- case "boolean":
1774
- return new BooleanParser();
1775
- case "number":
1776
- return new NumberParser();
1777
- case "stringExtract":
1778
- return new StringExtractParser(options);
1779
- case "string":
1780
- return new StringParser();
3366
+ if (model === "o3-mini") {
3367
+ return true;
3368
+ }
3369
+ return false;
3370
+ }
3371
+ function isModelKnownAnthropic(payload) {
3372
+ const model = payload.model.toLowerCase();
3373
+ if (model.startsWith("claude-")) {
3374
+ return true;
3375
+ }
3376
+ return false;
3377
+ }
3378
+ function isModelKnownXai(payload) {
3379
+ const model = payload.model.toLowerCase();
3380
+ if (model.startsWith("grok-")) {
3381
+ return true;
3382
+ }
3383
+ return false;
3384
+ }
3385
+ function isModelKnownBedrockAnthropic(payload) {
3386
+ const model = payload.model.toLowerCase();
3387
+ if (model.startsWith("anthropic.claude-")) {
3388
+ return true;
3389
+ }
3390
+ return false;
3391
+ }
3392
+ function guessProviderFromModel(payload) {
3393
+ switch (true) {
3394
+ case isModelKnownOpenAi(payload):
3395
+ return "openai";
3396
+ case isModelKnownXai(payload):
3397
+ return "xai";
3398
+ case isModelKnownBedrockAnthropic(payload):
3399
+ return "bedrock:anthropic";
3400
+ case isModelKnownAnthropic(payload):
3401
+ return "anthropic";
1781
3402
  default:
1782
- throw new Error(
1783
- `Invalid parser type: "${type}". Valid types are: json, string, boolean, number, stringExtract, listToArray, listToJson, listToKeyValue, replaceStringTemplate, markdownCodeBlock, markdownCodeBlocks`
1784
- );
3403
+ throw new LlmExeError("Unsupported model", {
3404
+ code: "configuration.invalid_provider",
3405
+ context: {
3406
+ operation: "guessProviderFromModel",
3407
+ model: payload.model,
3408
+ // Mirrors the switch above. Keep these in sync when adding a new
3409
+ // isModelKnown* case.
3410
+ availableProviders: [
3411
+ "openai",
3412
+ "xai",
3413
+ "bedrock:anthropic",
3414
+ "anthropic"
3415
+ ],
3416
+ resolution: "Pass a known model prefix (gpt-, chatgpt-, o1, o3-mini, claude-, grok-, anthropic.claude-) or specify the provider explicitly."
3417
+ }
3418
+ });
1785
3419
  }
1786
3420
  }
1787
- function createCustomParser(name, parserFn) {
1788
- return new CustomParser(name, parserFn);
3421
+
3422
+ // src/utils/modules/index.ts
3423
+ function registerHelpers(helpers) {
3424
+ hbs.registerHelpers(helpers);
3425
+ hbsAsync.registerHelpers(helpers);
3426
+ }
3427
+ function registerPartials(partials2) {
3428
+ hbs.registerPartials(partials2);
3429
+ hbsAsync.registerPartials(partials2);
1789
3430
  }
1790
3431
 
1791
3432
  // src/parser/parsers/LlmNativeFunctionParser.ts
1792
3433
  var LlmFunctionParser = class extends BaseParser {
1793
3434
  constructor(options) {
1794
- super("functionCall", options, "function_call");
3435
+ super("functionCall", "function_call");
1795
3436
  __publicField(this, "parser");
1796
3437
  this.parser = options.parser;
1797
3438
  }
1798
3439
  parse(text, _options) {
1799
3440
  if (typeof text === "string") {
1800
- return this.parser.parse(text);
3441
+ return this.parser.parse(text, _options);
1801
3442
  }
1802
3443
  const { content } = text;
1803
3444
  const functionUses = content?.filter((a) => a.type === "function_use") || [];
1804
3445
  if (functionUses.length === 0) {
1805
3446
  const [item] = content;
1806
- return this.parser.parse(item.text);
3447
+ return this.parser.parse(item.text, _options);
1807
3448
  }
1808
3449
  return content;
1809
3450
  }
1810
3451
  };
1811
3452
  var LlmNativeFunctionParser = class extends BaseParser {
1812
3453
  constructor(options) {
1813
- super("openAiFunction", options, "function_call");
3454
+ super("openAiFunction", "function_call");
1814
3455
  __publicField(this, "parser");
1815
3456
  this.parser = options.parser;
1816
3457
  }
1817
3458
  parse(text, _options) {
3459
+ if (typeof text === "string") {
3460
+ return this.parser.parse(text, _options);
3461
+ }
3462
+ if (typeof text?.text === "string") {
3463
+ return this.parser.parse(text.text, _options);
3464
+ }
1818
3465
  const { content } = text;
1819
3466
  const functionUse = content?.find((a) => a.type === "function_use");
1820
3467
  if (functionUse && "name" in functionUse && "input" in functionUse) {
@@ -1823,7 +3470,10 @@ var LlmNativeFunctionParser = class extends BaseParser {
1823
3470
  arguments: maybeParseJSON(functionUse.input)
1824
3471
  };
1825
3472
  }
1826
- return this.parser.parse(text?.text ?? text);
3473
+ const textContent = content?.find(
3474
+ (item) => item.type === "text" && typeof item.text === "string"
3475
+ );
3476
+ return this.parser.parse(textContent?.text, _options);
1827
3477
  }
1828
3478
  };
1829
3479
  var OpenAiFunctionParser = LlmNativeFunctionParser;
@@ -1850,17 +3500,29 @@ var LlmExecutor = class extends BaseExecutor {
1850
3500
  this.promptFn = null;
1851
3501
  }
1852
3502
  }
3503
+ /**
3504
+ * Runs the executor against the configured LLM and prompt.
3505
+ *
3506
+ * `null` and `undefined` are rejected with a `TypeError`: the declared
3507
+ * input type requires an object, and silently coercing missing input hides a
3508
+ * clear contract violation. Use `{}` for prompts that declare no template
3509
+ * variables. See issue #410.
3510
+ */
1853
3511
  async execute(_input, _options) {
1854
- const input = _input ?? {};
3512
+ if (_input === null || typeof _input === "undefined") {
3513
+ throw new TypeError(
3514
+ `[llm-exe] Executor "${this.name}" received null or undefined as input. execute() expects an object matching the prompt's input type.`
3515
+ );
3516
+ }
1855
3517
  if (this?.parser instanceof JsonParser && this.parser.schema) {
1856
3518
  _options = Object.assign(_options || {}, {
1857
3519
  jsonSchema: this.parser.schema
1858
3520
  });
1859
3521
  }
1860
- return super.execute(input, _options);
3522
+ return super.execute(_input, _options);
1861
3523
  }
1862
- async handler(_input, ..._args) {
1863
- const call = await this.llm.call(_input, ..._args);
3524
+ async handler(_input, _options, _context) {
3525
+ const call = await this.llm.call(_input, _options, _context);
1864
3526
  return call;
1865
3527
  }
1866
3528
  async getHandlerInput(_input) {
@@ -1879,15 +3541,26 @@ var LlmExecutor = class extends BaseExecutor {
1879
3541
  return prompt.format(_input);
1880
3542
  }
1881
3543
  }
1882
- throw new Error("Missing prompt");
3544
+ throw new LlmExeError("Missing prompt", {
3545
+ code: "executor.missing_prompt",
3546
+ context: {
3547
+ operation: "LlmExecutor.getHandlerInput",
3548
+ executorName: this.name,
3549
+ executorType: this.type,
3550
+ traceId: this.getTraceId() ?? void 0,
3551
+ resolution: "Provide a prompt (or prompt factory) when constructing the LLM executor."
3552
+ }
3553
+ });
1883
3554
  }
1884
- getHandlerOutput(out, _metadata) {
3555
+ getHandlerOutput(out, _metadata, _options, _context) {
3556
+ const parse = this.parser.parse.bind(this.parser);
3557
+ const parserArg = _context ?? _metadata;
1885
3558
  if (this.parser.target === "function_call") {
1886
3559
  const outToStr = out.getResult();
1887
- return this.parser.parse(outToStr, _metadata);
3560
+ return parse(outToStr, parserArg);
1888
3561
  } else {
1889
3562
  const outToStr = out.getResultText();
1890
- return this.parser.parse(outToStr, _metadata);
3563
+ return parse(outToStr, parserArg);
1891
3564
  }
1892
3565
  }
1893
3566
  metadata() {
@@ -1989,7 +3662,17 @@ var CallableExecutor = class {
1989
3662
  } else if (typeof options.handler === "function") {
1990
3663
  this._handler = createCoreExecutor(options.handler);
1991
3664
  } else {
1992
- throw new Error("Invalid handler");
3665
+ throw new LlmExeError("Invalid handler", {
3666
+ code: "callable.invalid_handler",
3667
+ context: {
3668
+ operation: "CallableExecutor.constructor",
3669
+ functionName: options.name,
3670
+ key: options?.key || options.name,
3671
+ expected: "function or BaseExecutor",
3672
+ received: typeof options.handler,
3673
+ resolution: "Pass a function or a BaseExecutor instance as the handler."
3674
+ }
3675
+ });
1993
3676
  }
1994
3677
  }
1995
3678
  async execute(input) {
@@ -2007,7 +3690,16 @@ var CallableExecutor = class {
2007
3690
  }
2008
3691
  return { result: true, attributes: {} };
2009
3692
  } catch (error) {
2010
- return { result: false, attributes: { error: error.message } };
3693
+ const wrapped = error instanceof LlmExeError ? error : new LlmExeError(error?.message ?? String(error), {
3694
+ code: "callable.validation_failed",
3695
+ context: {
3696
+ operation: "CallableExecutor.validateInput",
3697
+ functionName: this.name,
3698
+ key: this.key
3699
+ },
3700
+ cause: error
3701
+ });
3702
+ return { result: false, attributes: { error: wrapped.message } };
2011
3703
  }
2012
3704
  }
2013
3705
  visibilityHandler(input, attributes) {
@@ -2044,10 +3736,19 @@ var UseExecutorsBase = class {
2044
3736
  async callFunction(name, input) {
2045
3737
  try {
2046
3738
  const handler = this.getFunction(name);
2047
- assert(
2048
- handler,
2049
- `[invalid handler] The handler (${name}) does not exist.`
2050
- );
3739
+ if (!handler) {
3740
+ throw new LlmExeError(
3741
+ `[invalid handler] The handler (${name}) does not exist.`,
3742
+ {
3743
+ code: "callable.handler_not_found",
3744
+ context: {
3745
+ operation: "UseExecutorsBase.callFunction",
3746
+ functionName: name,
3747
+ availableFunctions: this.handlers.map((h) => h.name)
3748
+ }
3749
+ }
3750
+ );
3751
+ }
2051
3752
  const result = await handler.execute(ensureInputIsObject(input));
2052
3753
  return result;
2053
3754
  } catch (error) {
@@ -2057,10 +3758,19 @@ var UseExecutorsBase = class {
2057
3758
  async validateFunctionInput(name, input) {
2058
3759
  try {
2059
3760
  const handler = this.getFunction(name);
2060
- assert(
2061
- handler,
2062
- `[invalid handler] The handler (${name}) does not exist.`
2063
- );
3761
+ if (!handler) {
3762
+ throw new LlmExeError(
3763
+ `[invalid handler] The handler (${name}) does not exist.`,
3764
+ {
3765
+ code: "callable.handler_not_found",
3766
+ context: {
3767
+ operation: "UseExecutorsBase.validateFunctionInput",
3768
+ functionName: name,
3769
+ availableFunctions: this.handlers.map((h) => h.name)
3770
+ }
3771
+ }
3772
+ );
3773
+ }
2064
3774
  const result = await handler.validateInput(
2065
3775
  ensureInputIsObject(input)
2066
3776
  );
@@ -2075,18 +3785,59 @@ var UseExecutorsBase = class {
2075
3785
  function createCallableExecutor(options) {
2076
3786
  return new CallableExecutor(options);
2077
3787
  }
2078
- var UseExecutors = class extends UseExecutorsBase {
2079
- constructor(handlers) {
2080
- super(handlers);
2081
- }
2082
- };
2083
- function useExecutors(executors) {
2084
- return new UseExecutors(
2085
- executors.map((e) => {
2086
- if (e instanceof CallableExecutor) return e;
2087
- return createCallableExecutor(e);
2088
- })
2089
- );
3788
+ var UseExecutors = class extends UseExecutorsBase {
3789
+ constructor(handlers) {
3790
+ super(handlers);
3791
+ }
3792
+ };
3793
+ function useExecutors(executors) {
3794
+ return new UseExecutors(
3795
+ executors.map((e) => {
3796
+ if (e instanceof CallableExecutor) return e;
3797
+ return createCallableExecutor(e);
3798
+ })
3799
+ );
3800
+ }
3801
+
3802
+ // src/utils/guards.ts
3803
+ var guards_exports = {};
3804
+ __export(guards_exports, {
3805
+ hasFunctionCall: () => hasFunctionCall,
3806
+ hasToolCall: () => hasToolCall,
3807
+ isAssistantMessage: () => isAssistantMessage,
3808
+ isFunctionCall: () => isFunctionCall,
3809
+ isOutputResult: () => isOutputResult,
3810
+ isOutputResultContentText: () => isOutputResultContentText,
3811
+ isSystemMessage: () => isSystemMessage,
3812
+ isToolCall: () => isToolCall,
3813
+ isUserMessage: () => isUserMessage
3814
+ });
3815
+ function isOutputResult(obj) {
3816
+ return !!(obj && typeof obj === "object" && "id" in obj && "stopReason" in obj && "content" in obj && Array.isArray(obj.content));
3817
+ }
3818
+ function isOutputResultContentText(obj) {
3819
+ return !!(obj && typeof obj === "object" && "text" in obj && "type" in obj && obj.type === "text" && typeof obj.text === "string");
3820
+ }
3821
+ function isFunctionCall(result) {
3822
+ return !!(result && typeof result === "object" && "functionId" in result && "type" in result && result.type === "function_use");
3823
+ }
3824
+ function isToolCall(result) {
3825
+ return isFunctionCall(result);
3826
+ }
3827
+ function hasFunctionCall(results) {
3828
+ return !!(results && Array.isArray(results) && results.some(isToolCall));
3829
+ }
3830
+ function hasToolCall(results) {
3831
+ return hasFunctionCall(results);
3832
+ }
3833
+ function isUserMessage(message) {
3834
+ return message.role === "user";
3835
+ }
3836
+ function isAssistantMessage(message) {
3837
+ return message.role === "assistant" || message.role === "model";
3838
+ }
3839
+ function isSystemMessage(message) {
3840
+ return message.role === "system";
2090
3841
  }
2091
3842
 
2092
3843
  // src/utils/modules/deepClone.ts
@@ -2131,13 +3882,38 @@ function withDefaultModel(obj1, model) {
2131
3882
  return copy;
2132
3883
  }
2133
3884
 
2134
- // src/utils/modules/getEnvironmentVariable.ts
2135
- function getEnvironmentVariable(name) {
2136
- if (typeof process === "object" && process?.env) {
2137
- return process.env[name];
2138
- } else {
2139
- return void 0;
3885
+ // src/llm/_utils.deprecationWarning.ts
3886
+ var warned = /* @__PURE__ */ new Set();
3887
+ function emitDeprecationWarning(config, context) {
3888
+ if (!config.deprecated) return;
3889
+ if (typeof process !== "object" || typeof process?.emitWarning !== "function") {
3890
+ return;
2140
3891
  }
3892
+ const { shorthand, message } = config.deprecated;
3893
+ if (warned.has(shorthand)) return;
3894
+ warned.add(shorthand);
3895
+ const detail = JSON.stringify({
3896
+ shorthand,
3897
+ model: config.options?.model?.default,
3898
+ provider: config.provider,
3899
+ executorName: context?.executorName,
3900
+ traceId: context?.traceId
3901
+ });
3902
+ process.emitWarning(message, {
3903
+ type: "DeprecationWarning",
3904
+ code: "LLM_EXE_DEPRECATED",
3905
+ detail
3906
+ });
3907
+ }
3908
+ function deprecateShorthand(shorthand, args) {
3909
+ const entry = {
3910
+ ...args.config,
3911
+ deprecated: Object.freeze({
3912
+ shorthand,
3913
+ message: args.message
3914
+ })
3915
+ };
3916
+ return { [shorthand]: entry };
2141
3917
  }
2142
3918
 
2143
3919
  // src/llm/output/_util.ts
@@ -2419,7 +4195,10 @@ var openai = {
2419
4195
  "openai.gpt-4o": withDefaultModel(openAiChatV1, "gpt-4o"),
2420
4196
  "openai.gpt-4o-mini": withDefaultModel(openAiChatV1, "gpt-4o-mini"),
2421
4197
  // Deprecated
2422
- "openai.o4-mini": withDefaultModel(openAiChatV1, "o4-mini")
4198
+ ...deprecateShorthand("openai.o4-mini", {
4199
+ config: withDefaultModel(openAiChatV1, "o4-mini"),
4200
+ message: 'Shorthand "openai.o4-mini" is deprecated and may be removed in a future release.'
4201
+ })
2423
4202
  };
2424
4203
 
2425
4204
  // src/llm/config/anthropic/promptSanitizeMessageCallback.ts
@@ -2788,38 +4567,38 @@ var anthropic = {
2788
4567
  "claude-sonnet-4-5"
2789
4568
  ),
2790
4569
  // Deprecated
2791
- "anthropic.claude-opus-4-6": withDefaultModel(
2792
- anthropicChatV1,
2793
- "claude-opus-4-6"
2794
- ),
2795
- "anthropic.claude-opus-4-1": withDefaultModel(
2796
- anthropicChatV1,
2797
- "claude-opus-4-1-20250805"
2798
- ),
2799
- "anthropic.claude-sonnet-4": withDefaultModel(
2800
- anthropicChatV1,
2801
- "claude-sonnet-4-0"
2802
- ),
2803
- "anthropic.claude-opus-4": withDefaultModel(
2804
- anthropicChatV1,
2805
- "claude-opus-4-0"
2806
- ),
2807
- "anthropic.claude-3-7-sonnet": withDefaultModel(
2808
- anthropicChatV1,
2809
- "claude-3-7-sonnet-20250219"
2810
- ),
2811
- "anthropic.claude-3-5-sonnet": withDefaultModel(
2812
- anthropicChatV1,
2813
- "claude-3-5-sonnet-latest"
2814
- ),
2815
- "anthropic.claude-3-5-haiku": withDefaultModel(
2816
- anthropicChatV1,
2817
- "claude-3-5-haiku-latest"
2818
- ),
2819
- "anthropic.claude-3-opus": withDefaultModel(
2820
- anthropicChatV1,
2821
- "claude-3-opus-20240229"
2822
- )
4570
+ ...deprecateShorthand("anthropic.claude-opus-4-6", {
4571
+ config: withDefaultModel(anthropicChatV1, "claude-opus-4-6"),
4572
+ message: 'Shorthand "anthropic.claude-opus-4-6" is deprecated and may be removed in a future release.'
4573
+ }),
4574
+ ...deprecateShorthand("anthropic.claude-opus-4-1", {
4575
+ config: withDefaultModel(anthropicChatV1, "claude-opus-4-1-20250805"),
4576
+ message: 'Shorthand "anthropic.claude-opus-4-1" is deprecated and may be removed in a future release.'
4577
+ }),
4578
+ ...deprecateShorthand("anthropic.claude-sonnet-4", {
4579
+ config: withDefaultModel(anthropicChatV1, "claude-sonnet-4-0"),
4580
+ message: 'Shorthand "anthropic.claude-sonnet-4" is deprecated and may be removed in a future release.'
4581
+ }),
4582
+ ...deprecateShorthand("anthropic.claude-opus-4", {
4583
+ config: withDefaultModel(anthropicChatV1, "claude-opus-4-0"),
4584
+ message: 'Shorthand "anthropic.claude-opus-4" is deprecated and may be removed in a future release.'
4585
+ }),
4586
+ ...deprecateShorthand("anthropic.claude-3-7-sonnet", {
4587
+ config: withDefaultModel(anthropicChatV1, "claude-3-7-sonnet-20250219"),
4588
+ message: 'Shorthand "anthropic.claude-3-7-sonnet" is deprecated and may be removed in a future release.'
4589
+ }),
4590
+ ...deprecateShorthand("anthropic.claude-3-5-sonnet", {
4591
+ config: withDefaultModel(anthropicChatV1, "claude-3-5-sonnet-latest"),
4592
+ message: 'Shorthand "anthropic.claude-3-5-sonnet" is deprecated and may be removed in a future release.'
4593
+ }),
4594
+ ...deprecateShorthand("anthropic.claude-3-5-haiku", {
4595
+ config: withDefaultModel(anthropicChatV1, "claude-3-5-haiku-latest"),
4596
+ message: 'Shorthand "anthropic.claude-3-5-haiku" is deprecated and may be removed in a future release.'
4597
+ }),
4598
+ ...deprecateShorthand("anthropic.claude-3-opus", {
4599
+ config: withDefaultModel(anthropicChatV1, "claude-3-opus-20240229"),
4600
+ message: 'Shorthand "anthropic.claude-3-opus" is deprecated and may be removed in a future release.'
4601
+ })
2823
4602
  };
2824
4603
 
2825
4604
  // src/llm/config/x/index.ts
@@ -2848,15 +4627,31 @@ var xai = {
2848
4627
 
2849
4628
  // src/llm/output/_utils/combineJsonl.ts
2850
4629
  function combineJsonl(jsonl) {
2851
- const lines = jsonl.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
4630
+ const rawLines = jsonl.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
4631
+ const lines = rawLines.map((line, idx) => {
2852
4632
  try {
2853
4633
  return JSON.parse(line);
2854
4634
  } catch (e) {
2855
- throw new Error(`Invalid JSON: ${line}`);
4635
+ throw new LlmExeError(`Invalid JSON: ${line}`, {
4636
+ code: "llm.invalid_jsonl_response",
4637
+ context: {
4638
+ operation: "combineJsonl",
4639
+ lineNumber: idx + 1,
4640
+ lineExcerpt: line
4641
+ },
4642
+ cause: e
4643
+ });
2856
4644
  }
2857
4645
  });
2858
4646
  if (lines.length === 0) {
2859
- throw new Error("No JSON lines provided.");
4647
+ throw new LlmExeError("No JSON lines provided.", {
4648
+ code: "llm.invalid_jsonl_response",
4649
+ context: {
4650
+ operation: "combineJsonl",
4651
+ received: 0,
4652
+ expected: "at least one JSONL line"
4653
+ }
4654
+ });
2860
4655
  }
2861
4656
  lines.sort(
2862
4657
  (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
@@ -2865,7 +4660,14 @@ function combineJsonl(jsonl) {
2865
4660
  combinedContent = combinedContent.replace(/\\u003c/g, "<").replace(/\\u003e/g, ">");
2866
4661
  const finalLine = lines.find((line) => line.done === true);
2867
4662
  if (!finalLine) {
2868
- throw new Error("No line found where done = true.");
4663
+ throw new LlmExeError("No line found where done = true.", {
4664
+ code: "llm.invalid_jsonl_response",
4665
+ context: {
4666
+ operation: "combineJsonl",
4667
+ lineCount: lines.length,
4668
+ expected: "a final line with done = true"
4669
+ }
4670
+ });
2869
4671
  }
2870
4672
  const result = {
2871
4673
  model: finalLine.model,
@@ -3030,7 +4832,16 @@ function googleGeminiPromptSanitize(_messages, _inputBodyObj, _outputObj) {
3030
4832
  }
3031
4833
  if (Array.isArray(_messages)) {
3032
4834
  if (_messages.length === 0) {
3033
- throw new Error("Empty messages array");
4835
+ throw new LlmExeError("Empty messages array", {
4836
+ code: "prompt.invalid_messages",
4837
+ context: {
4838
+ operation: "googleGeminiPromptSanitize",
4839
+ provider: "google",
4840
+ received: "empty array",
4841
+ expected: "a non-empty messages array",
4842
+ resolution: "Pass at least one message to the prompt."
4843
+ }
4844
+ });
3034
4845
  }
3035
4846
  if (_messages.length === 1 && _messages[0].role === "system") {
3036
4847
  return [{ role: "user", parts: [{ text: _messages[0].content }] }];
@@ -3058,7 +4869,16 @@ function googleGeminiPromptSanitize(_messages, _inputBodyObj, _outputObj) {
3058
4869
  const result = _messages.map(googleGeminiPromptMessageCallback);
3059
4870
  return mergeConsecutiveSameRole2(result);
3060
4871
  }
3061
- throw new Error("Invalid messages format");
4872
+ throw new LlmExeError("Invalid messages format", {
4873
+ code: "prompt.invalid_messages",
4874
+ context: {
4875
+ operation: "googleGeminiPromptSanitize",
4876
+ provider: "google",
4877
+ received: typeof _messages,
4878
+ expected: "string or messages array",
4879
+ resolution: "Pass a string or an array of chat messages."
4880
+ }
4881
+ });
3062
4882
  }
3063
4883
 
3064
4884
  // src/llm/output/google.gemini/formatResult.ts
@@ -3174,18 +4994,6 @@ var googleGeminiChatV1 = {
3174
4994
  };
3175
4995
  var google = {
3176
4996
  "google.chat.v1": googleGeminiChatV1,
3177
- "google.gemini-2.5-flash": withDefaultModel(
3178
- googleGeminiChatV1,
3179
- "gemini-2.5-flash"
3180
- ),
3181
- "google.gemini-2.5-flash-lite": withDefaultModel(
3182
- googleGeminiChatV1,
3183
- "gemini-2.5-flash-lite"
3184
- ),
3185
- "google.gemini-2.5-pro": withDefaultModel(
3186
- googleGeminiChatV1,
3187
- "gemini-2.5-pro"
3188
- ),
3189
4997
  "google.gemini-3.1-flash-lite": withDefaultModel(
3190
4998
  googleGeminiChatV1,
3191
4999
  "gemini-3.1-flash-lite"
@@ -3195,6 +5003,18 @@ var google = {
3195
5003
  "gemini-3.5-flash"
3196
5004
  ),
3197
5005
  // Deprecated
5006
+ ...deprecateShorthand("google.gemini-2.5-flash", {
5007
+ config: withDefaultModel(googleGeminiChatV1, "gemini-2.5-flash"),
5008
+ message: 'Model "google.gemini-2.5-flash" is deprecated and will shut down on 2026-06-17.'
5009
+ }),
5010
+ ...deprecateShorthand("google.gemini-2.5-flash-lite", {
5011
+ config: withDefaultModel(googleGeminiChatV1, "gemini-2.5-flash-lite"),
5012
+ message: 'Model "google.gemini-2.5-flash-lite" is deprecated and will shut down on 2026-07-22.'
5013
+ }),
5014
+ ...deprecateShorthand("google.gemini-2.5-pro", {
5015
+ config: withDefaultModel(googleGeminiChatV1, "gemini-2.5-pro"),
5016
+ message: 'Model "google.gemini-2.5-pro" is deprecated and will shut down on 2026-06-17.'
5017
+ }),
3198
5018
  "google.gemini-2.0-flash": withDefaultModel(
3199
5019
  googleGeminiChatV1,
3200
5020
  "gemini-2.0-flash"
@@ -3235,81 +5055,28 @@ var configs = {
3235
5055
  };
3236
5056
  function getLlmConfig(provider) {
3237
5057
  if (!provider) {
3238
- throw new LlmExeError(`Missing provider`, "unknown", {
3239
- error: "Missing provider",
3240
- resolution: "Provide a valid provider"
5058
+ throw new LlmExeError(`Missing provider`, {
5059
+ code: "configuration.missing_provider",
5060
+ context: {
5061
+ operation: "getLlmConfig",
5062
+ availableProviders: Object.keys(configs),
5063
+ resolution: "Provide a valid provider"
5064
+ }
3241
5065
  });
3242
5066
  }
3243
5067
  const pick2 = configs[provider];
3244
5068
  if (pick2) {
3245
5069
  return pick2;
3246
5070
  }
3247
- throw new LlmExeError(`Invalid provider: ${provider}`, "unknown", {
3248
- provider,
3249
- error: `Invalid provider: ${provider}`,
3250
- resolution: "Provide a valid provider"
3251
- });
3252
- }
3253
-
3254
- // src/utils/modules/maskApiKeysInDebug.ts
3255
- function maskApiKeys(log) {
3256
- return log.replace(
3257
- /\b(Bearer\s+[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+|Bearer\s+[A-Za-z0-9\-_]{20,}|sk-[A-Za-z0-9]{20,}|AKIA[A-Z0-9]{16}|[A-Za-z0-9]{32,})\b/g,
3258
- (match) => {
3259
- if (match.length <= 8) return match;
3260
- const prefix = match.substring(0, 4);
3261
- const suffix = match.substring(match.length - 4);
3262
- const maskLength = match.length - 8;
3263
- return `${prefix}${"*".repeat(maskLength)}${suffix}`;
3264
- }
3265
- );
3266
- }
3267
-
3268
- // src/utils/modules/debug.ts
3269
- function debug(...args) {
3270
- const debugValue = getEnvironmentVariable("LLM_EXE_DEBUG");
3271
- const logs = [];
3272
- for (const arg of args) {
3273
- if (arg && typeof arg === "object") {
3274
- if (arg instanceof Error) {
3275
- } else if (arg instanceof Array) {
3276
- logs.push(arg.map((item) => JSON.stringify(item, null, 2)));
3277
- } else if (arg instanceof Map) {
3278
- logs.push(
3279
- Array.from(arg.entries()).map(
3280
- ([key, value]) => `${key}: ${JSON.stringify(value, null, 2)}`
3281
- )
3282
- );
3283
- } else if (arg instanceof Set) {
3284
- logs.push(Array.from(arg).map((item) => JSON.stringify(item, null, 2)));
3285
- } else if (arg instanceof Date) {
3286
- logs.push(arg.toISOString());
3287
- } else if (arg instanceof RegExp) {
3288
- logs.push(arg.toString());
3289
- } else {
3290
- try {
3291
- let shouldMask = false;
3292
- if (arg.headers && arg.headers.Authorization) {
3293
- shouldMask = true;
3294
- }
3295
- let str = JSON.stringify(arg, null, 2);
3296
- if (shouldMask) {
3297
- str = maskApiKeys(str);
3298
- }
3299
- logs.push(str);
3300
- } catch (error) {
3301
- console.error("Error parsing object:", error);
3302
- }
3303
- }
3304
- } else if (typeof arg === "string") {
3305
- logs.push(arg);
3306
- } else {
3307
- logs.push(arg);
5071
+ throw new LlmExeError(`Invalid provider: ${provider}`, {
5072
+ code: "configuration.invalid_provider",
5073
+ context: {
5074
+ operation: "getLlmConfig",
5075
+ provider,
5076
+ availableProviders: Object.keys(configs),
5077
+ resolution: "Provide a valid provider"
3308
5078
  }
3309
- }
3310
- if (typeof debugValue === "string" && debugValue !== "" && debugValue.toLowerCase() !== "undefined" && debugValue.toLowerCase() !== "null") {
3311
- console.debug(...logs);
3312
- }
5079
+ });
3313
5080
  }
3314
5081
 
3315
5082
  // src/utils/modules/isValidUrl.ts
@@ -3327,41 +5094,93 @@ async function apiRequest(url, options) {
3327
5094
  const finalOptions = {
3328
5095
  ...options
3329
5096
  };
5097
+ debug(url, finalOptions);
5098
+ if (!url || !isValidUrl(url)) {
5099
+ const safeUrl = safeRequestUrl(url);
5100
+ throw new LlmExeError("Invalid URL", {
5101
+ code: "request.invalid_url",
5102
+ context: {
5103
+ operation: "apiRequest",
5104
+ url: safeUrl,
5105
+ expected: "valid URL"
5106
+ }
5107
+ });
5108
+ }
5109
+ let response;
3330
5110
  try {
3331
- debug(url, finalOptions);
3332
- if (!url || !isValidUrl(url)) {
3333
- throw new Error("Invalid URL");
3334
- }
3335
- const response = await fetch(url, finalOptions);
3336
- if (!response.ok) {
3337
- let message = `HTTP error. Status: ${response.status}. Error Message: ${response?.statusText || "Unknown error."}`;
3338
- try {
3339
- const text = await response.text();
3340
- if (text) {
3341
- let detail = text;
3342
- try {
3343
- const body = JSON.parse(text);
3344
- detail = body?.error?.message || body?.error || body?.message || text;
3345
- if (typeof detail !== "string") detail = JSON.stringify(detail);
3346
- } catch {
3347
- }
3348
- message = `HTTP error. Status Code: ${response.status}. Error Message: ${detail}`;
5111
+ response = await fetch(url, finalOptions);
5112
+ } catch (fetchError) {
5113
+ const innerMsg = fetchError instanceof Error ? fetchError.message : "Error";
5114
+ const safeUrl = safeRequestUrl(url);
5115
+ throw new LlmExeError(`Request to ${safeUrl} failed: ${innerMsg}`, {
5116
+ code: "request.http_error",
5117
+ context: {
5118
+ operation: "apiRequest",
5119
+ url: safeUrl
5120
+ },
5121
+ cause: fetchError
5122
+ });
5123
+ }
5124
+ if (!response.ok) {
5125
+ let bodyText = "";
5126
+ let bodyJson = void 0;
5127
+ let bodyReadError = void 0;
5128
+ try {
5129
+ bodyText = await response.text();
5130
+ if (bodyText) {
5131
+ try {
5132
+ bodyJson = JSON.parse(bodyText);
5133
+ } catch {
3349
5134
  }
3350
- } catch {
3351
5135
  }
3352
- throw new Error(message);
3353
- }
3354
- const contentType = response.headers.get("content-type");
3355
- if (contentType?.includes("application/json")) {
3356
- const responseData = await response.json();
3357
- return responseData;
5136
+ } catch (e) {
5137
+ bodyReadError = e;
5138
+ }
5139
+ let message;
5140
+ if (bodyText) {
5141
+ let detail = bodyText;
5142
+ if (bodyJson) {
5143
+ const b = bodyJson;
5144
+ detail = b.error?.message || b.error || b.message || bodyText;
5145
+ if (typeof detail !== "string") detail = JSON.stringify(detail);
5146
+ }
5147
+ const safeDetail = safeProviderString(detail) ?? "";
5148
+ message = `HTTP error. Status Code: ${response.status}. Error Message: ${safeDetail}`;
3358
5149
  } else {
3359
- const responseData = await response.text();
3360
- return responseData;
3361
- }
3362
- } catch (error) {
3363
- const message = error instanceof Error ? error.message : "Error";
3364
- throw new Error(`Request to ${url} failed: ${message}`);
5150
+ message = `HTTP error. Status: ${response.status}. Error Message: ${response.statusText || "Unknown error."}`;
5151
+ }
5152
+ const safeUrl = safeRequestUrl(url);
5153
+ const wrappedMessage = `Request to ${safeUrl} failed: ${message}`;
5154
+ const safeHeaders = safeResponseHeaders(response.headers);
5155
+ const providerError = parseProviderErrorGeneric({
5156
+ status: response.status,
5157
+ statusText: response.statusText,
5158
+ headers: safeHeaders,
5159
+ bodyJson,
5160
+ bodyText
5161
+ });
5162
+ throw new LlmExeError(wrappedMessage, {
5163
+ code: "request.http_error",
5164
+ context: {
5165
+ operation: "apiRequest",
5166
+ url: safeUrl,
5167
+ status: response.status,
5168
+ statusText: response.statusText,
5169
+ responseHeaders: safeHeaders,
5170
+ providerError,
5171
+ providerErrorBody: bodyJson !== void 0 ? safeProviderErrorBody(bodyJson) : void 0,
5172
+ providerErrorRaw: bodyText ? safeProviderErrorBody(bodyText) : void 0
5173
+ },
5174
+ cause: bodyReadError
5175
+ });
5176
+ }
5177
+ const contentType = response.headers.get("content-type");
5178
+ if (contentType?.includes("application/json")) {
5179
+ const responseData = await response.json();
5180
+ return responseData;
5181
+ } else {
5182
+ const responseData = await response.text();
5183
+ return responseData;
3365
5184
  }
3366
5185
  }
3367
5186
 
@@ -3482,7 +5301,19 @@ async function runWithTemporaryEnv(env, handler) {
3482
5301
  // src/utils/modules/getAwsAuthorizationHeaders.ts
3483
5302
  async function getAwsAuthorizationHeaders(req, props) {
3484
5303
  if (!props.url || !props.regionName) {
3485
- throw new Error("URL and region name are required for AWS authorization");
5304
+ throw new LlmExeError(
5305
+ "URL and region name are required for AWS authorization",
5306
+ {
5307
+ code: "auth.aws_signing_input_missing",
5308
+ context: {
5309
+ operation: "getAwsAuthorizationHeaders",
5310
+ url: props.url,
5311
+ regionName: props.regionName,
5312
+ expected: "both url and regionName to be set",
5313
+ resolution: "Set AWS_REGION as an environment variable (or pass regionName) and provide a valid request URL."
5314
+ }
5315
+ }
5316
+ );
3486
5317
  }
3487
5318
  const providerChain = fromNodeProviderChain();
3488
5319
  const credentials = await runWithTemporaryEnv(
@@ -3521,6 +5352,12 @@ async function getAwsAuthorizationHeaders(req, props) {
3521
5352
  }
3522
5353
 
3523
5354
  // src/llm/_utils.parseHeaders.ts
5355
+ var REPLACED_HEADERS_EXCERPT_MAX = 500;
5356
+ function safeReplacedHeaders(value) {
5357
+ if (!value) return "";
5358
+ const truncated = value.length > REPLACED_HEADERS_EXCERPT_MAX ? value.slice(0, REPLACED_HEADERS_EXCERPT_MAX) + "\u2026(truncated)" : value;
5359
+ return redactSecrets(truncated);
5360
+ }
3524
5361
  async function parseHeaders(config, replacements, payload) {
3525
5362
  const replace = replaceTemplateStringSimple(config.headers, replacements);
3526
5363
  let parsedHeaders = {};
@@ -3532,8 +5369,21 @@ async function parseHeaders(config, replacements, payload) {
3532
5369
  }
3533
5370
  } catch (error) {
3534
5371
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
3535
- throw new Error(
3536
- `Failed to parse headers configuration: ${errorMessage}. Headers template: "${config.headers}". After replacement: "${replace}"`
5372
+ const safeReplaced = safeReplacedHeaders(replace);
5373
+ throw new LlmExeError(
5374
+ `Failed to parse headers configuration: ${errorMessage}. Headers template: "${config.headers}". After replacement: "${safeReplaced}"`,
5375
+ {
5376
+ code: "configuration.invalid_headers",
5377
+ context: {
5378
+ operation: "parseHeaders",
5379
+ provider: config.provider,
5380
+ key: config.key,
5381
+ headerTemplate: config.headers,
5382
+ replacedHeadersExcerpt: safeReplaced,
5383
+ resolution: "Fix the headers template so replacement produces a JSON object."
5384
+ },
5385
+ cause: error
5386
+ }
3537
5387
  );
3538
5388
  }
3539
5389
  }
@@ -3687,29 +5537,47 @@ async function useLlm_call(state, messages, _options) {
3687
5537
  headers: {},
3688
5538
  body
3689
5539
  });
3690
- const response = config.provider === "openai.chat-mock" ? {
3691
- id: "0123-45-6789",
3692
- model: "model",
3693
- created: (/* @__PURE__ */ new Date()).getTime(),
3694
- usage: { completion_tokens: 0, prompt_tokens: 0, total_tokens: 0 },
3695
- choices: [
3696
- {
3697
- message: {
3698
- role: "assistant",
3699
- content: `Hello world from LLM! The input was ${JSON.stringify(
3700
- messages
3701
- )}`
3702
- }
3703
- }
3704
- ]
3705
- } : await apiRequest(url, {
3706
- method: config.method,
3707
- body,
3708
- headers
3709
- });
3710
5540
  const { transformResponse = OutputDefault } = config;
3711
- const normalized = transformResponse(response, config);
3712
- return BaseLlmOutput(normalized);
5541
+ if (config.provider === "openai.chat-mock") {
5542
+ const mockResponse = {
5543
+ id: "0123-45-6789",
5544
+ model: "model",
5545
+ created: (/* @__PURE__ */ new Date()).getTime(),
5546
+ usage: { completion_tokens: 0, prompt_tokens: 0, total_tokens: 0 },
5547
+ choices: [
5548
+ {
5549
+ message: {
5550
+ role: "assistant",
5551
+ content: `Hello world from LLM! The input was ${JSON.stringify(messages)}`
5552
+ }
5553
+ }
5554
+ ]
5555
+ };
5556
+ return BaseLlmOutput(transformResponse(mockResponse, config));
5557
+ }
5558
+ try {
5559
+ const response = await apiRequest(url, {
5560
+ method: config.method,
5561
+ body,
5562
+ headers
5563
+ });
5564
+ return BaseLlmOutput(transformResponse(response, config));
5565
+ } catch (e) {
5566
+ if (!isLlmExeError(e, "request.http_error")) throw e;
5567
+ const ctx = e.context ?? {};
5568
+ const status = typeof ctx.status === "number" ? ctx.status : void 0;
5569
+ const code = status ? statusToLlmProviderCode(status) : "llm.provider_http_error";
5570
+ throw new LlmExeError(e.message, {
5571
+ code,
5572
+ context: {
5573
+ ...ctx,
5574
+ operation: "useLlm_call",
5575
+ provider: state.provider,
5576
+ model: state.model
5577
+ },
5578
+ cause: e
5579
+ });
5580
+ }
3713
5581
  }
3714
5582
 
3715
5583
  // src/llm/_utils.stateFromOptions.ts
@@ -3733,7 +5601,16 @@ function stateFromOptions(options, config) {
3733
5601
  if (Array.isArray(thisConfig?.required)) {
3734
5602
  const [required, message = `Error: [${key}] is required`] = thisConfig.required;
3735
5603
  if (required && typeof value === "undefined") {
3736
- throw new Error(message);
5604
+ throw new LlmExeError(message, {
5605
+ code: "configuration.missing_option",
5606
+ context: {
5607
+ operation: "stateFromOptions",
5608
+ provider: config.provider,
5609
+ key: config.key,
5610
+ option: String(key),
5611
+ resolution: `Provide a value for "${String(key)}" via options or environment.`
5612
+ }
5613
+ });
3737
5614
  }
3738
5615
  }
3739
5616
  }
@@ -3780,8 +5657,12 @@ function apiRequestWrapper(config, options, handler, doNotRetryErrorMessages = [
3780
5657
  const numOfAttempts = options.numOfAttempts || 2;
3781
5658
  const jitter = options.jitter || "none";
3782
5659
  let traceId = options?.traceId || null;
3783
- async function call(messages, options2) {
5660
+ async function call(messages, options2, context) {
3784
5661
  try {
5662
+ emitDeprecationWarning(config, {
5663
+ executorName: context?.executor?.name,
5664
+ traceId: context?.traceId ?? getTraceId() ?? void 0
5665
+ });
3785
5666
  metrics.total_calls++;
3786
5667
  const result = await backOff(
3787
5668
  () => asyncCallWithTimeout(
@@ -3959,8 +5840,19 @@ var embeddingConfigs = {
3959
5840
  const isV3 = /embed-(english|multilingual)-v3/.test(model);
3960
5841
  if (isV3) {
3961
5842
  if (value === 1024) return void 0;
3962
- throw new Error(
3963
- `Cohere Embed v3 only supports 1024-dimensional output (model: "${model}", requested: ${value}). Use cohere.embed-v4:0 for configurable dimensions.`
5843
+ throw new LlmExeError(
5844
+ `Cohere Embed v3 only supports 1024-dimensional output (model: "${model}", requested: ${value}). Use cohere.embed-v4:0 for configurable dimensions.`,
5845
+ {
5846
+ code: "embedding.unsupported_dimensions",
5847
+ context: {
5848
+ operation: "embedding.dimensionTransform",
5849
+ provider: "amazon:cohere.embedding",
5850
+ model,
5851
+ dimensions: value,
5852
+ expected: 1024,
5853
+ resolution: "Use cohere.embed-v4:0 for configurable dimensions."
5854
+ }
5855
+ }
3964
5856
  );
3965
5857
  }
3966
5858
  return value;
@@ -3971,13 +5863,28 @@ var embeddingConfigs = {
3971
5863
  };
3972
5864
  function getEmbeddingConfig(provider) {
3973
5865
  if (!provider) {
3974
- throw new Error(`Missing provider`);
5866
+ throw new LlmExeError(`Missing provider`, {
5867
+ code: "embedding.missing_provider",
5868
+ context: {
5869
+ operation: "getEmbeddingConfig",
5870
+ availableProviders: Object.keys(embeddingConfigs),
5871
+ resolution: "Provide a valid embedding provider key."
5872
+ }
5873
+ });
3975
5874
  }
3976
5875
  const pick2 = embeddingConfigs[provider];
3977
5876
  if (pick2) {
3978
5877
  return pick2;
3979
5878
  }
3980
- throw new Error(`Invalid provider: ${provider}`);
5879
+ throw new LlmExeError(`Invalid provider: ${provider}`, {
5880
+ code: "embedding.invalid_provider",
5881
+ context: {
5882
+ operation: "getEmbeddingConfig",
5883
+ provider,
5884
+ availableProviders: Object.keys(embeddingConfigs),
5885
+ resolution: "Provide a valid embedding provider key."
5886
+ }
5887
+ });
3981
5888
  }
3982
5889
 
3983
5890
  // src/embedding/output/BaseEmbeddingOutput.ts
@@ -4081,7 +5988,16 @@ function getEmbeddingOutputParser(config, response) {
4081
5988
  case "amazon:cohere.embedding.v1":
4082
5989
  return CohereBedrockEmbedding(response, config);
4083
5990
  default:
4084
- throw new Error("Unsupported provider");
5991
+ throw new LlmExeError("Unsupported provider", {
5992
+ code: "embedding.invalid_response_shape",
5993
+ context: {
5994
+ operation: "getEmbeddingOutputParser",
5995
+ provider: config.key,
5996
+ model: config.model,
5997
+ availableProviders: Object.keys(embeddingConfigs),
5998
+ resolution: "Use a supported embedding provider key."
5999
+ }
6000
+ });
4085
6001
  }
4086
6002
  }
4087
6003
 
@@ -4101,12 +6017,29 @@ async function createEmbedding_call(state, _input, _options) {
4101
6017
  headers: {},
4102
6018
  body
4103
6019
  });
4104
- const request = await apiRequest(url, {
4105
- method: config.method,
4106
- body,
4107
- headers
4108
- });
4109
- return getEmbeddingOutputParser(state, request);
6020
+ try {
6021
+ const request = await apiRequest(url, {
6022
+ method: config.method,
6023
+ body,
6024
+ headers
6025
+ });
6026
+ return getEmbeddingOutputParser(state, request);
6027
+ } catch (e) {
6028
+ if (!isLlmExeError(e, "request.http_error")) throw e;
6029
+ const ctx = e.context ?? {};
6030
+ const status = typeof ctx.status === "number" ? ctx.status : void 0;
6031
+ const code = status ? statusToEmbeddingProviderCode(status) : "embedding.provider_http_error";
6032
+ throw new LlmExeError(e.message, {
6033
+ code,
6034
+ context: {
6035
+ ...ctx,
6036
+ operation: "createEmbedding_call",
6037
+ provider: state.provider,
6038
+ model: state.model
6039
+ },
6040
+ cause: e
6041
+ });
6042
+ }
4110
6043
  }
4111
6044
 
4112
6045
  // src/embedding/embedding.ts
@@ -4115,6 +6048,245 @@ function createEmbedding(provider, options) {
4115
6048
  return apiRequestWrapper(config, options, createEmbedding_call);
4116
6049
  }
4117
6050
 
6051
+ // src/prompt/_templateValidation.ts
6052
+ var BUILT_IN_HELPERS = /* @__PURE__ */ new Set([
6053
+ "if",
6054
+ "unless",
6055
+ "each",
6056
+ "with",
6057
+ "lookup",
6058
+ "log",
6059
+ "blockHelperMissing",
6060
+ "helperMissing"
6061
+ ]);
6062
+ function isKnownHelper(name, customHelpers) {
6063
+ if (BUILT_IN_HELPERS.has(name)) return true;
6064
+ if (customHelpers && Object.prototype.hasOwnProperty.call(customHelpers, name)) {
6065
+ return true;
6066
+ }
6067
+ return false;
6068
+ }
6069
+ function pathRootSegment(path) {
6070
+ if (path.depth > 0) return null;
6071
+ if (path.data) return null;
6072
+ if (path.parts.length === 0) return null;
6073
+ return path.parts[0];
6074
+ }
6075
+ function pathToCollectedString(path) {
6076
+ const parts = path.parts.slice();
6077
+ return parts.join(".");
6078
+ }
6079
+ function collectFromPath(path, source, locals, options, references, seen) {
6080
+ const root = pathRootSegment(path);
6081
+ if (!root) return;
6082
+ if (locals.has(root)) return;
6083
+ const collected = pathToCollectedString(path);
6084
+ const dedupKey = `${source}::${collected}`;
6085
+ if (seen.has(dedupKey)) return;
6086
+ seen.add(dedupKey);
6087
+ references.push({
6088
+ path: collected,
6089
+ source,
6090
+ location: options.location
6091
+ });
6092
+ }
6093
+ function collectFromExpression(expr, source, locals, options, references, missingHelpers, seen) {
6094
+ if (expr.type === "PathExpression") {
6095
+ collectFromPath(expr, source, locals, options, references, seen);
6096
+ return;
6097
+ }
6098
+ if (expr.type === "SubExpression") {
6099
+ walkCall(
6100
+ expr.path,
6101
+ expr.params,
6102
+ expr.hash,
6103
+ locals,
6104
+ options,
6105
+ references,
6106
+ missingHelpers,
6107
+ seen
6108
+ );
6109
+ return;
6110
+ }
6111
+ }
6112
+ function walkHash(hash, locals, options, references, missingHelpers, seen) {
6113
+ if (!hash || !hash.pairs) return;
6114
+ for (const pair of hash.pairs) {
6115
+ collectFromExpression(
6116
+ pair.value,
6117
+ "hash",
6118
+ locals,
6119
+ options,
6120
+ references,
6121
+ missingHelpers,
6122
+ seen
6123
+ );
6124
+ }
6125
+ }
6126
+ function walkCall(path, params, hash, locals, options, references, missingHelpers, seen) {
6127
+ const root = pathRootSegment(path);
6128
+ const hasArgs = params.length > 0 || hash !== void 0 && hash.pairs.length > 0;
6129
+ if (hasArgs) {
6130
+ if (root) {
6131
+ if (!isKnownHelper(path.original, options.helpers) && !locals.has(root)) {
6132
+ missingHelpers.add(path.original);
6133
+ }
6134
+ }
6135
+ for (const param of params) {
6136
+ collectFromExpression(
6137
+ param,
6138
+ "helper-param",
6139
+ locals,
6140
+ options,
6141
+ references,
6142
+ missingHelpers,
6143
+ seen
6144
+ );
6145
+ }
6146
+ walkHash(hash, locals, options, references, missingHelpers, seen);
6147
+ return;
6148
+ }
6149
+ if (root && isKnownHelper(path.original, options.helpers)) {
6150
+ return;
6151
+ }
6152
+ collectFromPath(path, "mustache", locals, options, references, seen);
6153
+ }
6154
+ function walkProgram(program, parentLocals, options, references, missingHelpers, seen) {
6155
+ if (!program) return;
6156
+ const locals = new Set(parentLocals);
6157
+ if (program.blockParams) {
6158
+ for (const bp of program.blockParams) {
6159
+ locals.add(bp);
6160
+ }
6161
+ }
6162
+ for (const node of program.body) {
6163
+ walkStatement(node, locals, options, references, missingHelpers, seen);
6164
+ }
6165
+ }
6166
+ function walkStatement(node, locals, options, references, missingHelpers, seen) {
6167
+ switch (node.type) {
6168
+ case "MustacheStatement": {
6169
+ const m = node;
6170
+ if (m.path.type === "PathExpression") {
6171
+ walkCall(
6172
+ m.path,
6173
+ m.params,
6174
+ m.hash,
6175
+ locals,
6176
+ options,
6177
+ references,
6178
+ missingHelpers,
6179
+ seen
6180
+ );
6181
+ }
6182
+ return;
6183
+ }
6184
+ case "BlockStatement": {
6185
+ const b = node;
6186
+ const root = pathRootSegment(b.path);
6187
+ if (root && !isKnownHelper(b.path.original, options.helpers) && !locals.has(root)) {
6188
+ missingHelpers.add(b.path.original);
6189
+ }
6190
+ for (const param of b.params) {
6191
+ collectFromExpression(
6192
+ param,
6193
+ "block-param",
6194
+ locals,
6195
+ options,
6196
+ references,
6197
+ missingHelpers,
6198
+ seen
6199
+ );
6200
+ }
6201
+ walkHash(b.hash, locals, options, references, missingHelpers, seen);
6202
+ const isEach = b.path.original === "each";
6203
+ const hasBlockParams = b.program?.blockParams && b.program.blockParams.length > 0 || b.inverse?.blockParams && b.inverse.blockParams.length > 0;
6204
+ if (isEach && !hasBlockParams) {
6205
+ return;
6206
+ }
6207
+ walkProgram(b.program, locals, options, references, missingHelpers, seen);
6208
+ walkProgram(b.inverse, locals, options, references, missingHelpers, seen);
6209
+ return;
6210
+ }
6211
+ case "PartialStatement": {
6212
+ const p = node;
6213
+ for (const param of p.params) {
6214
+ collectFromExpression(
6215
+ param,
6216
+ "helper-param",
6217
+ locals,
6218
+ options,
6219
+ references,
6220
+ missingHelpers,
6221
+ seen
6222
+ );
6223
+ }
6224
+ walkHash(p.hash, locals, options, references, missingHelpers, seen);
6225
+ return;
6226
+ }
6227
+ }
6228
+ }
6229
+ function collectAll(template, options) {
6230
+ const references = [];
6231
+ const missingHelpers = /* @__PURE__ */ new Set();
6232
+ let ast;
6233
+ try {
6234
+ ast = hbs.handlebars.parse(template);
6235
+ } catch {
6236
+ return { references, missingHelpers: [] };
6237
+ }
6238
+ const seen = /* @__PURE__ */ new Set();
6239
+ walkProgram(ast, /* @__PURE__ */ new Set(), options, references, missingHelpers, seen);
6240
+ return { references, missingHelpers: Array.from(missingHelpers) };
6241
+ }
6242
+ function hasInputPath(input, path) {
6243
+ if (!input || typeof input !== "object") return false;
6244
+ let current = input;
6245
+ for (const part of path.split(".")) {
6246
+ if (!current || typeof current !== "object") return false;
6247
+ if (!Object.prototype.hasOwnProperty.call(current, part)) return false;
6248
+ current = current[part];
6249
+ }
6250
+ return current !== void 0;
6251
+ }
6252
+ function validateTemplateInputReferences(template, input, options = {}) {
6253
+ const { references, missingHelpers } = collectAll(template, options);
6254
+ const missingVariables = [];
6255
+ const seenMissing = /* @__PURE__ */ new Set();
6256
+ for (const ref of references) {
6257
+ if (hasInputPath(input, ref.path)) continue;
6258
+ const key = ref.path;
6259
+ if (seenMissing.has(key)) continue;
6260
+ seenMissing.add(key);
6261
+ missingVariables.push(ref);
6262
+ }
6263
+ return { references, missingVariables, missingHelpers };
6264
+ }
6265
+
6266
+ // src/prompt/errors.ts
6267
+ function buildMessage(context) {
6268
+ const { missingVariables, missingHelpers } = context;
6269
+ const vars = missingVariables.length ? `Missing variables: ${formatErrorList(missingVariables)}.` : "";
6270
+ const helpers = missingHelpers.length ? `Missing helpers: ${formatErrorList(missingHelpers)}.` : "";
6271
+ if (vars && helpers) {
6272
+ return `Prompt template has unresolved references. ${vars} ${helpers}`;
6273
+ }
6274
+ if (vars) {
6275
+ return `Prompt template references variables not provided. ${vars}`;
6276
+ }
6277
+ return `Prompt template references helpers not registered. ${helpers}`;
6278
+ }
6279
+ function missingTemplateReferencesError(context) {
6280
+ return createLlmExeError(
6281
+ {
6282
+ code: "prompt.missing_template_variable",
6283
+ message: buildMessage,
6284
+ resolution: "Pass every variable referenced by the prompt template, or register any custom helpers used."
6285
+ },
6286
+ context
6287
+ );
6288
+ }
6289
+
4118
6290
  // src/prompt/_base.ts
4119
6291
  var BasePrompt = class {
4120
6292
  /**
@@ -4126,6 +6298,7 @@ var BasePrompt = class {
4126
6298
  __publicField(this, "messages", []);
4127
6299
  __publicField(this, "partials", []);
4128
6300
  __publicField(this, "helpers", []);
6301
+ __publicField(this, "validateInput", false);
4129
6302
  __publicField(this, "replaceTemplateString", replaceTemplateString);
4130
6303
  __publicField(this, "replaceTemplateStringAsync", replaceTemplateStringAsync);
4131
6304
  __publicField(this, "filters", {
@@ -4151,6 +6324,9 @@ var BasePrompt = class {
4151
6324
  if (options.replaceTemplateString) {
4152
6325
  this.replaceTemplateString = options.replaceTemplateString;
4153
6326
  }
6327
+ if (options.validateInput !== void 0) {
6328
+ this.validateInput = options.validateInput;
6329
+ }
4154
6330
  }
4155
6331
  }
4156
6332
  /**
@@ -4202,6 +6378,40 @@ var BasePrompt = class {
4202
6378
  this.helpers.push(...helpers);
4203
6379
  return this;
4204
6380
  }
6381
+ /**
6382
+ * Returns the Handlebars-bearing strings that should be validated, along
6383
+ * with a location label used for error context. Subclasses with structured
6384
+ * message content (e.g. ChatPrompt) should override.
6385
+ */
6386
+ getTemplateContents() {
6387
+ return this.messages.map((message, index) => {
6388
+ if (!message.content || Array.isArray(message.content)) {
6389
+ return null;
6390
+ }
6391
+ return {
6392
+ content: message.content,
6393
+ location: `messages[${index}].content`
6394
+ };
6395
+ }).filter(
6396
+ (entry) => entry !== null
6397
+ );
6398
+ }
6399
+ preflightValidate(values) {
6400
+ if (this.validateInput === false || !this.validateInput) {
6401
+ return;
6402
+ }
6403
+ try {
6404
+ this.validate(values);
6405
+ } catch (error) {
6406
+ if (this.validateInput === "warn" && isLlmExeError(error, "prompt.missing_template_variable")) {
6407
+ if (typeof process === "object" && typeof process?.emitWarning === "function") {
6408
+ process.emitWarning(error);
6409
+ }
6410
+ return;
6411
+ }
6412
+ throw error;
6413
+ }
6414
+ }
4205
6415
  /**
4206
6416
  * format description
4207
6417
  * @param values The message content
@@ -4209,6 +6419,7 @@ var BasePrompt = class {
4209
6419
  * @return returns messages formatted with template replacement
4210
6420
  */
4211
6421
  format(values, separator = "\n\n") {
6422
+ this.preflightValidate(values);
4212
6423
  const replacements = this.getReplacements(values);
4213
6424
  const messages = this.messages.map((message) => {
4214
6425
  return message.content && !Array.isArray(message.content) ? this.replaceTemplateString(
@@ -4229,6 +6440,7 @@ var BasePrompt = class {
4229
6440
  * @return returns messages formatted with template replacement
4230
6441
  */
4231
6442
  async formatAsync(values, separator = "\n\n") {
6443
+ this.preflightValidate(values);
4232
6444
  const replacements = this.getReplacements(values);
4233
6445
  const _messages = await Promise.all(this.messages.map((message) => {
4234
6446
  return message.content && !Array.isArray(message.content) ? this.replaceTemplateStringAsync(
@@ -4252,8 +6464,18 @@ var BasePrompt = class {
4252
6464
  }
4253
6465
  getReplacements(values) {
4254
6466
  if (values === void 0 || values === null) {
4255
- throw new Error(
4256
- "format() requires an input object. Did you forget to pass arguments?"
6467
+ throw new LlmExeError(
6468
+ "format() requires an input object. Did you forget to pass arguments?",
6469
+ {
6470
+ code: "prompt.missing_input",
6471
+ context: {
6472
+ operation: "BasePrompt.getReplacements",
6473
+ promptType: this.type,
6474
+ expected: "object",
6475
+ received: values === null ? "null" : "undefined",
6476
+ resolution: "Pass an object of template values to format()."
6477
+ }
6478
+ }
4257
6479
  );
4258
6480
  }
4259
6481
  const { input = "", ...restOfValues } = values;
@@ -4268,11 +6490,52 @@ var BasePrompt = class {
4268
6490
  return replacements;
4269
6491
  }
4270
6492
  /**
4271
- * Validates the prompt structure.
4272
- * @return {boolean} Returns false if the prompt has no messages defined.
6493
+ * Validates that `input` provides every variable referenced by this prompt's
6494
+ * templates, and that every identifiable helper call is registered.
6495
+ *
6496
+ * @breaking v3: previously returned `this.messages.length > 0` with no
6497
+ * `input` parameter. For the old behavior, read `prompt.messages.length > 0`
6498
+ * directly.
6499
+ *
6500
+ * @throws LlmExeError with code `"prompt.missing_template_variable"` listing
6501
+ * all missing variables and helpers.
4273
6502
  */
4274
- validate() {
4275
- return this.messages.length > 0;
6503
+ validate(input) {
6504
+ const allMissingVariables = [];
6505
+ const allMissingHelpers = /* @__PURE__ */ new Set();
6506
+ const registeredHelpers = this.helpers.reduce(
6507
+ (acc, h) => {
6508
+ acc[h.name] = h.handler;
6509
+ return acc;
6510
+ },
6511
+ {}
6512
+ );
6513
+ const knownHelpers = {
6514
+ ...hbs.handlebars.helpers,
6515
+ ...registeredHelpers
6516
+ };
6517
+ for (const template of this.getTemplateContents()) {
6518
+ const result = validateTemplateInputReferences(template.content, input, {
6519
+ helpers: knownHelpers,
6520
+ location: template.location
6521
+ });
6522
+ allMissingVariables.push(...result.missingVariables);
6523
+ for (const helper of result.missingHelpers) {
6524
+ allMissingHelpers.add(helper);
6525
+ }
6526
+ }
6527
+ if (allMissingVariables.length === 0 && allMissingHelpers.size === 0) {
6528
+ return;
6529
+ }
6530
+ const dedupedVariables = Array.from(
6531
+ new Set(allMissingVariables.map((r) => r.path))
6532
+ );
6533
+ throw missingTemplateReferencesError({
6534
+ operation: "Prompt.validate",
6535
+ promptType: this.type,
6536
+ missingVariables: dedupedVariables,
6537
+ missingHelpers: Array.from(allMissingHelpers)
6538
+ });
4276
6539
  }
4277
6540
  };
4278
6541
 
@@ -4574,6 +6837,7 @@ var ChatPrompt = class extends BasePrompt {
4574
6837
  * @return formatted prompt.
4575
6838
  */
4576
6839
  format(values) {
6840
+ this.preflightValidate(values);
4577
6841
  const messagesOut = [];
4578
6842
  const replacements = this.getReplacements(values);
4579
6843
  const safeToParseTemplate = ["assistant", "system"];
@@ -4699,6 +6963,7 @@ var ChatPrompt = class extends BasePrompt {
4699
6963
  * @return formatted prompt.
4700
6964
  */
4701
6965
  async formatAsync(values) {
6966
+ this.preflightValidate(values);
4702
6967
  const messagesOut = [];
4703
6968
  const replacements = this.getReplacements(values);
4704
6969
  const safeToParseTemplate = ["assistant", "system"];
@@ -5001,16 +7266,26 @@ var Dialogue = class extends BaseStateItem {
5001
7266
  }
5002
7267
  setFunctionCallMessage(input) {
5003
7268
  if (!input || typeof input !== "object") {
5004
- throw new LlmExeError(`Invalid arguments`, "state", {
5005
- error: `Invalid arguments: input must be an object`,
5006
- module: "dialogue"
7269
+ throw new LlmExeError(`Invalid arguments`, {
7270
+ code: "state.invalid_arguments",
7271
+ context: {
7272
+ operation: "Dialogue.setFunctionCallMessage",
7273
+ module: "dialogue",
7274
+ expected: "object",
7275
+ received: typeof input
7276
+ }
5007
7277
  });
5008
7278
  }
5009
7279
  if ("function_call" in input) {
5010
7280
  if (!input.function_call || typeof input.function_call !== "object") {
5011
- throw new LlmExeError(`Invalid arguments`, "state", {
5012
- error: `Invalid arguments: input must be an object`,
5013
- module: "dialogue"
7281
+ throw new LlmExeError(`Invalid arguments`, {
7282
+ code: "state.invalid_arguments",
7283
+ context: {
7284
+ operation: "Dialogue.setFunctionCallMessage",
7285
+ module: "dialogue",
7286
+ expected: "object",
7287
+ received: typeof input.function_call
7288
+ }
5014
7289
  });
5015
7290
  }
5016
7291
  this.value.push({
@@ -5245,6 +7520,8 @@ export {
5245
7520
  CustomParser,
5246
7521
  DefaultState,
5247
7522
  DefaultStateItem,
7523
+ LLM_EXE_ERROR_SYMBOL,
7524
+ LlmExeError,
5248
7525
  LlmExecutorOpenAiFunctions,
5249
7526
  LlmExecutorWithFunctions,
5250
7527
  LlmNativeFunctionParser,
@@ -5265,6 +7542,7 @@ export {
5265
7542
  createStateItem,
5266
7543
  defineSchema,
5267
7544
  guards_exports as guards,
7545
+ isLlmExeError,
5268
7546
  registerHelpers,
5269
7547
  registerPartials,
5270
7548
  useExecutors,