@umituz/web-ai 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/{ABTesting-DDqNhHtP.d.ts → AIErrors-B2ogd1PB.d.ts} +14 -1
  2. package/dist/{ABTesting-DWBtKTMQ.d.cts → AIErrors-C5EasV_c.d.cts} +14 -1
  3. package/dist/{ProviderConfig-a55tLhWT.d.cts → ProviderConfig-DIFa9nmG.d.cts} +10 -1
  4. package/dist/{ProviderConfig-a55tLhWT.d.ts → ProviderConfig-DIFa9nmG.d.ts} +10 -1
  5. package/dist/chat/index.cjs +114 -23
  6. package/dist/chat/index.cjs.map +1 -1
  7. package/dist/chat/index.d.cts +4 -4
  8. package/dist/chat/index.d.ts +4 -4
  9. package/dist/chat/index.js +114 -23
  10. package/dist/chat/index.js.map +1 -1
  11. package/dist/{chat.service-DBKq9I2c.d.cts → chat.service-CL1qtYxe.d.cts} +7 -7
  12. package/dist/{chat.service-RQn8UTXD.d.ts → chat.service-q05GnhEm.d.ts} +7 -7
  13. package/dist/content/index.cjs +393 -186
  14. package/dist/content/index.cjs.map +1 -1
  15. package/dist/content/index.d.cts +7 -8
  16. package/dist/content/index.d.ts +7 -8
  17. package/dist/content/index.js +393 -186
  18. package/dist/content/index.js.map +1 -1
  19. package/dist/core/index.cjs +112 -15
  20. package/dist/core/index.cjs.map +1 -1
  21. package/dist/core/index.d.cts +5 -33
  22. package/dist/core/index.d.ts +5 -33
  23. package/dist/core/index.js +111 -16
  24. package/dist/core/index.js.map +1 -1
  25. package/dist/{create-ai-DGZFBOA1.d.cts → create-ai-DOI9jD3v.d.cts} +4 -21
  26. package/dist/{create-ai-BhNaz86i.d.ts → create-ai-DXEoke8-.d.ts} +4 -21
  27. package/dist/hooks/index.cjs +359 -169
  28. package/dist/hooks/index.cjs.map +1 -1
  29. package/dist/hooks/index.d.cts +6 -6
  30. package/dist/hooks/index.d.ts +6 -6
  31. package/dist/hooks/index.js +359 -169
  32. package/dist/hooks/index.js.map +1 -1
  33. package/dist/index-DIHz8_OJ.d.ts +48 -0
  34. package/dist/index-DKHNcMbl.d.cts +48 -0
  35. package/dist/index.cjs +245 -94
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +6 -6
  38. package/dist/index.d.ts +6 -6
  39. package/dist/index.js +244 -95
  40. package/dist/index.js.map +1 -1
  41. package/dist/{provider.factory-COsnprLH.d.cts → provider.factory-BxsfK4WW.d.cts} +1 -1
  42. package/dist/{provider.factory-CpWFyjbw.d.ts → provider.factory-DfDfk0dK.d.ts} +1 -1
  43. package/dist/providers/index.cjs +281 -124
  44. package/dist/providers/index.cjs.map +1 -1
  45. package/dist/providers/index.d.cts +11 -12
  46. package/dist/providers/index.d.ts +11 -12
  47. package/dist/providers/index.js +281 -124
  48. package/dist/providers/index.js.map +1 -1
  49. package/dist/wizard/index.d.cts +1 -1
  50. package/dist/wizard/index.d.ts +1 -1
  51. package/package.json +4 -1
  52. package/dist/index-DjX9ZKuM.d.cts +0 -14
  53. package/dist/index-DxLvE588.d.ts +0 -14
@@ -73,25 +73,49 @@ var VIDEO_DURATION_OPTIONS = VIDEO_DURATION_OPTIONS_SECONDS;
73
73
  var getDimensionsForAspectRatio = resolveDimensionsForAspectRatio;
74
74
  var getRecommendedQuality = recommendQualityForAspectRatio;
75
75
 
76
- // src/content/domain/errors/AIErrors.ts
77
- var AIError = class _AIError extends Error {
78
- code;
79
- cause;
80
- constructor(message, code, cause) {
81
- super(message);
82
- this.name = "AIError";
83
- this.code = code;
84
- this.cause = cause;
85
- }
86
- static from(error, fallbackCode = "UNKNOWN") {
87
- if (error instanceof _AIError) return error;
88
- if (error instanceof Error) {
89
- return new _AIError(error.message, mapMessageToCode(error.message, fallbackCode), error);
90
- }
91
- return new _AIError(String(error), fallbackCode, error);
92
- }
93
- };
94
- function mapMessageToCode(message, fallback) {
76
+ // src/core/errors/ai-error-types.ts
77
+ var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
78
+ AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
79
+ AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
80
+ AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
81
+ AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
82
+ AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
83
+ AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
84
+ AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
85
+ AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
86
+ return AIErrorType2;
87
+ })(AIErrorType || {});
88
+
89
+ // src/core/errors/ai-error.ts
90
+ var KNOWN_ERROR_TYPES = new Set(Object.values(AIErrorType));
91
+ function mapCodeToType(code) {
92
+ switch (code) {
93
+ case "VALIDATION_ERROR":
94
+ case "CLIENT_ERROR":
95
+ return "CLIENT_ERROR" /* CLIENT_ERROR */;
96
+ case "INVALID_API_KEY":
97
+ return "INVALID_API_KEY" /* INVALID_API_KEY */;
98
+ case "MISSING_CONFIG":
99
+ case "PROVIDER_UNAVAILABLE":
100
+ return "MISSING_CONFIG" /* MISSING_CONFIG */;
101
+ case "PROVIDER_QUOTA_EXHAUSTED":
102
+ case "RATE_LIMIT_ERROR":
103
+ return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
104
+ case "NETWORK_ERROR":
105
+ case "TIMEOUT":
106
+ return "NETWORK_ERROR" /* NETWORK_ERROR */;
107
+ case "ABORT_ERROR":
108
+ case "ABORTED":
109
+ return "ABORT_ERROR" /* ABORT_ERROR */;
110
+ case "SERVER_ERROR":
111
+ case "PROVIDER_ERROR":
112
+ case "NO_RESULT":
113
+ return "SERVER_ERROR" /* SERVER_ERROR */;
114
+ default:
115
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
116
+ }
117
+ }
118
+ function mapMessageToCode(message, fallback = "UNKNOWN") {
95
119
  const lower = message.toLowerCase();
96
120
  if (lower.includes("validation")) return "VALIDATION_ERROR";
97
121
  if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
@@ -104,37 +128,39 @@ function mapMessageToCode(message, fallback) {
104
128
  if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
105
129
  return fallback;
106
130
  }
107
- var ValidationError = class extends AIError {
108
- constructor(message) {
109
- super(message, "VALIDATION_ERROR");
110
- this.name = "ValidationError";
111
- }
112
- };
113
- var ProviderUnavailableError = class extends AIError {
114
- constructor(providerId) {
115
- super(`Provider ${providerId} is not available`, "PROVIDER_UNAVAILABLE");
116
- this.name = "ProviderUnavailableError";
117
- }
118
- };
119
- var ProviderQuotaExhaustedError = class extends AIError {
120
- constructor(providerId) {
121
- super(`Provider ${providerId} quota exhausted`, "PROVIDER_QUOTA_EXHAUSTED");
122
- this.name = "ProviderQuotaExhaustedError";
123
- }
124
- };
125
-
126
- // src/core/errors/ai-error.ts
127
- var AIError2 = class extends Error {
131
+ var AIError = class _AIError extends Error {
128
132
  type;
133
+ code;
129
134
  cause;
130
135
  metadata;
131
- constructor(type, message, cause, metadata = {}) {
132
- super(message);
136
+ constructor(typeOrMessage, messageOrCode, cause, metadata = {}) {
137
+ let effectiveType;
138
+ let effectiveMessage;
139
+ let effectiveCode;
140
+ if (KNOWN_ERROR_TYPES.has(typeOrMessage)) {
141
+ effectiveType = typeOrMessage;
142
+ effectiveMessage = messageOrCode ?? "";
143
+ effectiveCode = effectiveType;
144
+ } else {
145
+ effectiveMessage = typeOrMessage;
146
+ effectiveCode = messageOrCode ?? "UNKNOWN";
147
+ effectiveType = mapCodeToType(effectiveCode);
148
+ }
149
+ super(effectiveMessage);
133
150
  this.name = "AIError";
134
- this.type = type;
151
+ this.type = effectiveType;
152
+ this.code = effectiveCode;
135
153
  this.cause = cause;
136
154
  this.metadata = metadata;
137
155
  }
156
+ static from(error, fallbackCode = "UNKNOWN") {
157
+ if (error instanceof _AIError) return error;
158
+ if (error instanceof Error) {
159
+ const code = mapMessageToCode(error.message, fallbackCode);
160
+ return new _AIError(error.message, code, error);
161
+ }
162
+ return new _AIError(String(error), fallbackCode, error);
163
+ }
138
164
  toString() {
139
165
  const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
140
166
  return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
@@ -143,12 +169,33 @@ var AIError2 = class extends Error {
143
169
  return {
144
170
  name: this.name,
145
171
  type: this.type,
172
+ code: this.code,
146
173
  message: this.message,
147
174
  metadata: this.metadata
148
175
  };
149
176
  }
150
177
  };
151
178
 
179
+ // src/content/domain/errors/AIErrors.ts
180
+ var ValidationError = class extends AIError {
181
+ constructor(message) {
182
+ super(message, "VALIDATION_ERROR");
183
+ this.name = "ValidationError";
184
+ }
185
+ };
186
+ var ProviderUnavailableError = class extends AIError {
187
+ constructor(providerId) {
188
+ super(`Provider ${providerId} is not available`, "PROVIDER_UNAVAILABLE");
189
+ this.name = "ProviderUnavailableError";
190
+ }
191
+ };
192
+ var ProviderQuotaExhaustedError = class extends AIError {
193
+ constructor(providerId) {
194
+ super(`Provider ${providerId} quota exhausted`, "PROVIDER_QUOTA_EXHAUSTED");
195
+ this.name = "ProviderQuotaExhaustedError";
196
+ }
197
+ };
198
+
152
199
  // src/core/errors/error-classifier.ts
153
200
  var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
154
201
  "NETWORK_ERROR" /* NETWORK_ERROR */,
@@ -291,7 +338,7 @@ var ProviderFactory = class {
291
338
  }
292
339
  }
293
340
  }
294
- const failure = new AIError2(
341
+ const failure = new AIError(
295
342
  "SERVER_ERROR" /* SERVER_ERROR */,
296
343
  `All providers failed for capability 'text'`,
297
344
  lastError instanceof Error ? lastError : void 0
@@ -305,12 +352,16 @@ var ProviderFactory = class {
305
352
  let wake = null;
306
353
  let finished = false;
307
354
  let failure;
355
+ const controller = new AbortController();
356
+ const onAbort = () => controller.abort();
357
+ request.signal?.addEventListener("abort", onAbort);
358
+ const effectiveRequest = { ...request, signal: controller.signal };
308
359
  const notify = () => {
309
360
  const resolve = wake;
310
361
  wake = null;
311
362
  resolve?.();
312
363
  };
313
- const completion = this.chatStream(request, {
364
+ const completion = this.chatStream(effectiveRequest, {
314
365
  onChunk: (chunk) => {
315
366
  queue.push(chunk);
316
367
  notify();
@@ -326,18 +377,23 @@ var ProviderFactory = class {
326
377
  notify();
327
378
  }
328
379
  );
329
- while (!finished || queue.length > 0) {
330
- if (queue.length === 0 && !finished) {
331
- await new Promise((resolve) => {
332
- wake = resolve;
333
- });
334
- }
335
- while (queue.length > 0) {
336
- yield queue.shift();
380
+ try {
381
+ while (!finished || queue.length > 0) {
382
+ if (queue.length === 0 && !finished) {
383
+ await new Promise((resolve) => {
384
+ wake = resolve;
385
+ });
386
+ }
387
+ while (queue.length > 0) {
388
+ yield queue.shift();
389
+ }
390
+ if (failure) throw failure;
337
391
  }
338
- if (failure) throw failure;
392
+ await completion;
393
+ } finally {
394
+ controller.abort();
395
+ request.signal?.removeEventListener("abort", onAbort);
339
396
  }
340
- await completion;
341
397
  }
342
398
  async generateJson(request) {
343
399
  const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
@@ -445,7 +501,7 @@ var ProviderFactory = class {
445
501
  }
446
502
  }
447
503
  }
448
- throw new AIError2(
504
+ throw new AIError(
449
505
  "SERVER_ERROR" /* SERVER_ERROR */,
450
506
  `All providers failed for capability '${capability}'`,
451
507
  lastError instanceof Error ? lastError : void 0
@@ -474,7 +530,7 @@ var ProviderFactory = class {
474
530
  }
475
531
  }
476
532
  noProviderError(capability) {
477
- return new AIError2(
533
+ return new AIError(
478
534
  "MISSING_CONFIG" /* MISSING_CONFIG */,
479
535
  `No provider registered for capability '${capability}' \u2014 create one with createAI() and include its config block`
480
536
  );
@@ -484,15 +540,22 @@ var ProviderFactory = class {
484
540
  // src/core/constants/protocol.constants.ts
485
541
  var SSE_PREFIX = "data: ";
486
542
  var SSE_DONE_MARKER = "[DONE]";
487
- var JSON_CODE_BLOCK_PATTERN = /```json\n([\s\S]*?)\n```/;
543
+ var JSON_CODE_BLOCK_PATTERN = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
488
544
  var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
545
+ var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
489
546
 
490
547
  // src/core/parsers/sse-lines.ts
491
548
  function isSseDataLine(line) {
492
- return line.startsWith(SSE_PREFIX);
549
+ return line.startsWith(SSE_PREFIX) || line.startsWith("data:");
493
550
  }
494
551
  function extractSseDataPayload(line) {
495
- return line.slice(SSE_PREFIX.length);
552
+ if (line.startsWith(SSE_PREFIX)) {
553
+ return line.slice(SSE_PREFIX.length);
554
+ }
555
+ if (line.startsWith("data:")) {
556
+ return line.slice(5);
557
+ }
558
+ return line;
496
559
  }
497
560
  function isSseDoneMarker(payload) {
498
561
  return payload === SSE_DONE_MARKER;
@@ -501,7 +564,7 @@ function isSseDoneMarker(payload) {
501
564
  // src/core/parsers/json-content-extractor.ts
502
565
  function extractJsonFromCodeFence(content) {
503
566
  const match = content.match(JSON_CODE_BLOCK_PATTERN);
504
- if (match && match[1]) return match[1];
567
+ if (match && match[1]) return match[1].trim();
505
568
  return null;
506
569
  }
507
570
  function extractJsonObject(content) {
@@ -509,8 +572,27 @@ function extractJsonObject(content) {
509
572
  if (match && match[0]) return match[0];
510
573
  return null;
511
574
  }
575
+ function extractJsonArray(content) {
576
+ const match = content.match(JSON_ARRAY_PATTERN);
577
+ if (match && match[0]) return match[0];
578
+ return null;
579
+ }
580
+ function repairCommonJsonDefects(text) {
581
+ return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
582
+ }
512
583
  function extractJsonCandidate(content) {
513
- return extractJsonFromCodeFence(content) ?? extractJsonObject(content) ?? content;
584
+ const fromFence = extractJsonFromCodeFence(content);
585
+ if (fromFence) return fromFence;
586
+ const objectIndex = content.indexOf("{");
587
+ const arrayIndex = content.indexOf("[");
588
+ if (objectIndex !== -1 && (arrayIndex === -1 || objectIndex < arrayIndex)) {
589
+ const obj = extractJsonObject(content);
590
+ if (obj) return obj;
591
+ } else if (arrayIndex !== -1) {
592
+ const arr = extractJsonArray(content);
593
+ if (arr) return arr;
594
+ }
595
+ return content.trim();
514
596
  }
515
597
 
516
598
  // src/core/parsers/json-response.parser.ts
@@ -519,12 +601,17 @@ var JsonResponseParser = class {
519
601
  const candidate = extractJsonCandidate(rawContent);
520
602
  try {
521
603
  return JSON.parse(candidate);
522
- } catch (error) {
523
- throw new AIError2(
524
- "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
525
- "Failed to parse structured response as JSON",
526
- error instanceof Error ? error : void 0
527
- );
604
+ } catch {
605
+ try {
606
+ return JSON.parse(repairCommonJsonDefects(candidate));
607
+ } catch (error) {
608
+ throw new AIError(
609
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
610
+ "Failed to parse structured response as JSON",
611
+ error instanceof Error ? error : void 0,
612
+ { rawContent }
613
+ );
614
+ }
528
615
  }
529
616
  }
530
617
  };
@@ -650,7 +737,7 @@ var AnthropicHttpTransport = class {
650
737
  });
651
738
  if (!response.ok) await this.throwFromErrorResponse(response);
652
739
  if (!response.body) {
653
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
740
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
654
741
  }
655
742
  return response.body;
656
743
  } catch (error) {
@@ -683,7 +770,7 @@ var AnthropicHttpTransport = class {
683
770
  status: response.status,
684
771
  url: response.url
685
772
  };
686
- throw new AIError2(
773
+ throw new AIError(
687
774
  classifyHttpStatus(response.status),
688
775
  errorMessage,
689
776
  void 0,
@@ -691,24 +778,24 @@ var AnthropicHttpTransport = class {
691
778
  );
692
779
  }
693
780
  normalizeError(error) {
694
- if (error instanceof AIError2) return error;
781
+ if (error instanceof AIError) return error;
695
782
  if (error instanceof Error) {
696
783
  if (error.name === "AbortError") {
697
- return new AIError2(
784
+ return new AIError(
698
785
  "ABORT_ERROR" /* ABORT_ERROR */,
699
786
  "Request was aborted by the client",
700
787
  error
701
788
  );
702
789
  }
703
790
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
704
- return new AIError2(
791
+ return new AIError(
705
792
  "NETWORK_ERROR" /* NETWORK_ERROR */,
706
793
  "Network error: Unable to connect to the AI service",
707
794
  error
708
795
  );
709
796
  }
710
797
  }
711
- return new AIError2(
798
+ return new AIError(
712
799
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
713
800
  error instanceof Error ? error.message : "Unknown error occurred",
714
801
  error instanceof Error ? error : void 0
@@ -838,11 +925,14 @@ async function consumeAnthropicStream(stream, callbacks) {
838
925
  break;
839
926
  case "message_stop":
840
927
  break;
841
- case "error":
842
- throw new AIError2(
843
- event.error.type === "rate_limit_error" ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
928
+ case "error": {
929
+ const errType = event.error.type;
930
+ const mappedType = errType === "rate_limit_error" ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : errType === "authentication_error" ? "INVALID_API_KEY" /* INVALID_API_KEY */ : errType === "invalid_request_error" ? "CLIENT_ERROR" /* CLIENT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */;
931
+ throw new AIError(
932
+ mappedType,
844
933
  event.error.message || "Anthropic stream failed mid-flight"
845
934
  );
935
+ }
846
936
  }
847
937
  };
848
938
  try {
@@ -857,8 +947,8 @@ async function consumeAnthropicStream(stream, callbacks) {
857
947
  buffer += decoder.decode();
858
948
  if (buffer.length > 0) consumeLine(buffer);
859
949
  } catch (error) {
860
- if (error instanceof AIError2) throw error;
861
- throw new AIError2(
950
+ if (error instanceof AIError) throw error;
951
+ throw new AIError(
862
952
  "NETWORK_ERROR" /* NETWORK_ERROR */,
863
953
  "Stream was interrupted",
864
954
  error instanceof Error ? error : void 0
@@ -920,7 +1010,7 @@ var AnthropicProvider = class {
920
1010
  const messages = [...request.messages];
921
1011
  const lastUserIndex = findLastIndex(messages, (m) => m.role === "user");
922
1012
  if (lastUserIndex === -1) {
923
- throw new AIError2(
1013
+ throw new AIError(
924
1014
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
925
1015
  "generateStructured requires at least one user message"
926
1016
  );
@@ -975,7 +1065,7 @@ var AnthropicProvider = class {
975
1065
  finishReason
976
1066
  };
977
1067
  }
978
- throw new AIError2(
1068
+ throw new AIError(
979
1069
  "SERVER_ERROR" /* SERVER_ERROR */,
980
1070
  "No content generated from Anthropic API",
981
1071
  void 0,
@@ -1180,7 +1270,7 @@ var RetryManager = class {
1180
1270
  }
1181
1271
  shouldRetry(error, options) {
1182
1272
  if (options.shouldRetry) return options.shouldRetry(error);
1183
- if (error instanceof AIError2) {
1273
+ if (error instanceof AIError) {
1184
1274
  return isRetryableErrorType(error.type, DEFAULT_ERROR_CLASSIFICATION_RULE);
1185
1275
  }
1186
1276
  const lower = error.message.toLowerCase();
@@ -1417,7 +1507,7 @@ var GroqHttpTransport = class {
1417
1507
  });
1418
1508
  if (!response.ok) await this.throwFromErrorResponse(response);
1419
1509
  if (!response.body) {
1420
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1510
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1421
1511
  }
1422
1512
  return response.body;
1423
1513
  } catch (error) {
@@ -1465,7 +1555,7 @@ var GroqHttpTransport = class {
1465
1555
  status: response.status,
1466
1556
  url: response.url
1467
1557
  };
1468
- throw new AIError2(
1558
+ throw new AIError(
1469
1559
  classifyHttpStatus(response.status),
1470
1560
  errorMessage,
1471
1561
  void 0,
@@ -1473,24 +1563,24 @@ var GroqHttpTransport = class {
1473
1563
  );
1474
1564
  }
1475
1565
  normalizeError(error) {
1476
- if (error instanceof AIError2) return error;
1566
+ if (error instanceof AIError) return error;
1477
1567
  if (error instanceof Error) {
1478
1568
  if (error.name === "AbortError") {
1479
- return new AIError2(
1569
+ return new AIError(
1480
1570
  "ABORT_ERROR" /* ABORT_ERROR */,
1481
1571
  "Request was aborted by the client",
1482
1572
  error
1483
1573
  );
1484
1574
  }
1485
1575
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
1486
- return new AIError2(
1576
+ return new AIError(
1487
1577
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1488
1578
  "Network error: Unable to connect to the AI service",
1489
1579
  error
1490
1580
  );
1491
1581
  }
1492
1582
  }
1493
- return new AIError2(
1583
+ return new AIError(
1494
1584
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1495
1585
  error instanceof Error ? error.message : "Unknown error occurred",
1496
1586
  error instanceof Error ? error : void 0
@@ -1632,6 +1722,13 @@ async function consumeGroqStream(stream, callbacks) {
1632
1722
  } catch {
1633
1723
  return;
1634
1724
  }
1725
+ const errorBody = chunk.error;
1726
+ if (errorBody) {
1727
+ throw new AIError(
1728
+ errorBody.message?.toLowerCase().includes("rate limit") ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
1729
+ errorBody.message || "Groq stream failed mid-flight"
1730
+ );
1731
+ }
1635
1732
  if (chunk.model) model = chunk.model;
1636
1733
  const choice = chunk.choices?.[0];
1637
1734
  const delta = choice?.delta?.content;
@@ -1646,13 +1743,6 @@ async function consumeGroqStream(stream, callbacks) {
1646
1743
  if (tailUsage) {
1647
1744
  usage = mapGroqUsage(tailUsage);
1648
1745
  }
1649
- const errorBody = chunk.error;
1650
- if (errorBody) {
1651
- throw new AIError2(
1652
- "SERVER_ERROR" /* SERVER_ERROR */,
1653
- errorBody.message || "Groq stream failed mid-flight"
1654
- );
1655
- }
1656
1746
  };
1657
1747
  try {
1658
1748
  while (true) {
@@ -1666,8 +1756,8 @@ async function consumeGroqStream(stream, callbacks) {
1666
1756
  buffer += decoder.decode();
1667
1757
  if (buffer.length > 0) consumeLine(buffer);
1668
1758
  } catch (error) {
1669
- if (error instanceof AIError2) throw error;
1670
- throw new AIError2(
1759
+ if (error instanceof AIError) throw error;
1760
+ throw new AIError(
1671
1761
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1672
1762
  "Stream was interrupted",
1673
1763
  error instanceof Error ? error : void 0
@@ -1725,7 +1815,7 @@ var GroqProvider = class {
1725
1815
  const choice = response.choices?.[0];
1726
1816
  const text = choice?.message?.content ?? "";
1727
1817
  if (text.length === 0) {
1728
- throw new AIError2(
1818
+ throw new AIError(
1729
1819
  "SERVER_ERROR" /* SERVER_ERROR */,
1730
1820
  "No content generated from Groq API",
1731
1821
  void 0,
@@ -1747,7 +1837,7 @@ var GroqProvider = class {
1747
1837
  const messages = toGroqMessages(request.messages, request.systemPrompt);
1748
1838
  const lastUserIndex = findLastIndex2(messages, (m) => m.role === "user");
1749
1839
  if (lastUserIndex === -1) {
1750
- throw new AIError2(
1840
+ throw new AIError(
1751
1841
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1752
1842
  "generateStructured requires at least one user message"
1753
1843
  );
@@ -1761,7 +1851,7 @@ var GroqProvider = class {
1761
1851
  const choice = response.choices?.[0];
1762
1852
  const text = choice?.message?.content ?? "";
1763
1853
  if (text.length === 0) {
1764
- throw new AIError2(
1854
+ throw new AIError(
1765
1855
  "SERVER_ERROR" /* SERVER_ERROR */,
1766
1856
  "No content generated from Groq API",
1767
1857
  void 0,
@@ -1788,7 +1878,7 @@ var GroqProvider = class {
1788
1878
  try {
1789
1879
  const outcome = await consumeGroqStream(streamBody, callbacks);
1790
1880
  if (outcome.text.length === 0) {
1791
- throw new AIError2(
1881
+ throw new AIError(
1792
1882
  "SERVER_ERROR" /* SERVER_ERROR */,
1793
1883
  "No content generated from Groq API",
1794
1884
  void 0,
@@ -1972,7 +2062,9 @@ var GeminiProvider = class extends BaseProvider {
1972
2062
  headers: this.authHeaders()
1973
2063
  }
1974
2064
  );
1975
- return response.ok ? "healthy" : "unavailable";
2065
+ if (response.ok) return "healthy";
2066
+ if (response.status === 401 || response.status === 403) return "unavailable";
2067
+ return "degraded";
1976
2068
  } catch {
1977
2069
  return "unavailable";
1978
2070
  }
@@ -1983,6 +2075,8 @@ var GeminiProvider = class extends BaseProvider {
1983
2075
  async generate(request) {
1984
2076
  try {
1985
2077
  const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
2078
+ const systemMessages = request.messages.filter((m) => m.role === "system");
2079
+ const systemPrompt = request.systemPrompt && request.systemPrompt.length > 0 ? request.systemPrompt : systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n") : void 0;
1986
2080
  const response = await this.withRetry(async () => {
1987
2081
  return await this.fetchWithTimeout(
1988
2082
  `${this.config.baseUrl}/models/${model}:generateContent`,
@@ -1990,24 +2084,30 @@ var GeminiProvider = class extends BaseProvider {
1990
2084
  method: "POST",
1991
2085
  headers: this.authHeaders(),
1992
2086
  body: JSON.stringify({
1993
- systemInstruction: request.systemPrompt && request.systemPrompt.length > 0 ? { parts: [{ text: request.systemPrompt }] } : void 0,
2087
+ systemInstruction: systemPrompt && systemPrompt.length > 0 ? { parts: [{ text: systemPrompt }] } : void 0,
1994
2088
  contents: toGeminiContents(request.messages),
1995
2089
  generationConfig: {
1996
- temperature: request.temperature || 0.7,
1997
- maxOutputTokens: request.maxTokens || 2048,
1998
- topP: request.topP || 0.9
2090
+ temperature: request.temperature ?? 0.7,
2091
+ maxOutputTokens: request.maxTokens ?? 2048,
2092
+ topP: request.topP ?? 0.9
1999
2093
  }
2000
2094
  })
2001
2095
  }
2002
2096
  );
2003
2097
  });
2004
2098
  if (!response.ok) {
2005
- throw new Error(`Gemini API error: ${response.statusText}`);
2099
+ const message = await extractErrorMessageFromResponse(response);
2100
+ throw new AIError(
2101
+ classifyHttpStatus(response.status),
2102
+ `Gemini API error (${response.status}): ${message}`,
2103
+ void 0,
2104
+ { status: response.status, model }
2105
+ );
2006
2106
  }
2007
2107
  const data = await response.json();
2008
- const text = data.candidates[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
2108
+ const text = data.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
2009
2109
  if (text.length === 0) {
2010
- throw new AIError2("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
2110
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
2011
2111
  }
2012
2112
  return {
2013
2113
  text,
@@ -2018,11 +2118,11 @@ var GeminiProvider = class extends BaseProvider {
2018
2118
  } : void 0,
2019
2119
  model,
2020
2120
  providerId: this.id,
2021
- finishReason: mapGeminiFinishReason(data.candidates[0]?.finishReason)
2121
+ finishReason: mapGeminiFinishReason(data.candidates?.[0]?.finishReason)
2022
2122
  };
2023
2123
  } catch (error) {
2024
- if (error instanceof AIError2) throw error;
2025
- throw new AIError2(
2124
+ if (error instanceof AIError) throw error;
2125
+ throw new AIError(
2026
2126
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
2027
2127
  `Gemini text generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
2028
2128
  error instanceof Error ? error : void 0
@@ -2033,7 +2133,7 @@ var GeminiProvider = class extends BaseProvider {
2033
2133
  const messages = [...request.messages];
2034
2134
  const lastUserIndex = findLastIndex3(messages, (m) => m.role === "user");
2035
2135
  if (lastUserIndex === -1) {
2036
- throw new AIError2(
2136
+ throw new AIError(
2037
2137
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
2038
2138
  "generateStructured requires at least one user message"
2039
2139
  );
@@ -2070,12 +2170,18 @@ var GeminiProvider = class extends BaseProvider {
2070
2170
  );
2071
2171
  });
2072
2172
  if (!response.ok) {
2073
- throw new Error(`Gemini API error: ${response.statusText}`);
2173
+ const message = await extractErrorMessageFromResponse(response);
2174
+ throw new AIError(
2175
+ classifyHttpStatus(response.status),
2176
+ `Gemini image generation error (${response.status}): ${message}`,
2177
+ void 0,
2178
+ { status: response.status }
2179
+ );
2074
2180
  }
2075
2181
  const data = await response.json();
2076
- const imageData = data.candidates[0]?.content?.parts[0]?.inlineData;
2182
+ const imageData = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
2077
2183
  if (!imageData) {
2078
- throw new Error("No image data in response");
2184
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image data in Gemini response");
2079
2185
  }
2080
2186
  const url = `data:${imageData.mimeType};base64,${imageData.data}`;
2081
2187
  return {
@@ -2454,6 +2560,41 @@ var FalProvider = class extends BaseProvider {
2454
2560
  }
2455
2561
  };
2456
2562
 
2563
+ // src/providers/pruna/pruna-generate.ts
2564
+ var prunaCorePromise = null;
2565
+ async function loadPrunaCore() {
2566
+ if (!prunaCorePromise) {
2567
+ const specifier = "@umituz/pruna-provider/core";
2568
+ prunaCorePromise = import(specifier);
2569
+ }
2570
+ return prunaCorePromise;
2571
+ }
2572
+ async function* generateWithPruna(request, apiKey, signal, onProgress) {
2573
+ const prunaCore = await loadPrunaCore();
2574
+ if (prunaCore.generateWithPruna) {
2575
+ yield* prunaCore.generateWithPruna(request, apiKey, signal, onProgress);
2576
+ return;
2577
+ }
2578
+ if (!prunaCore.submitPrediction || !prunaCore.pollForResult) {
2579
+ throw new Error("@umituz/pruna-provider/core does not export required functions");
2580
+ }
2581
+ const modelId = request.model || (request.image ? "p-image-edit" : "p-image");
2582
+ const prediction = await prunaCore.submitPrediction(modelId, request, apiKey, signal, onProgress);
2583
+ if (prediction.output && typeof prediction.output === "object" && "url" in prediction.output) {
2584
+ yield {
2585
+ stage: "predicting",
2586
+ result: { url: prediction.output.url, model: modelId }
2587
+ };
2588
+ return;
2589
+ }
2590
+ const pollUrl = prediction.status_url || prediction.generation_url;
2591
+ if (!pollUrl) {
2592
+ throw new Error("Pruna prediction response missing polling URL");
2593
+ }
2594
+ const resultUrl = await prunaCore.pollForResult(pollUrl, apiKey, 60, 2e3, signal, onProgress);
2595
+ yield { stage: "polling", result: { url: resultUrl, model: modelId } };
2596
+ }
2597
+
2457
2598
  // src/providers/pruna/pruna.provider.ts
2458
2599
  var PrunaProvider = class {
2459
2600
  constructor(config) {
@@ -2469,17 +2610,70 @@ var PrunaProvider = class {
2469
2610
  async estimateCost(_request) {
2470
2611
  return 1;
2471
2612
  }
2472
- async generateImage(_request) {
2473
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2613
+ async generateImage(request) {
2614
+ let finalUrl;
2615
+ const model = this.config.models?.textToImage;
2616
+ for await (const stage of generateWithPruna(
2617
+ { prompt: request.prompt, model, quality: request.quality },
2618
+ this.config.apiKey
2619
+ )) {
2620
+ if (stage.error) throw stage.error;
2621
+ if (stage.result?.url) finalUrl = stage.result.url;
2622
+ }
2623
+ if (!finalUrl) {
2624
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image generated by Pruna AI");
2625
+ }
2626
+ return {
2627
+ id: Date.now().toString(),
2628
+ type: "image",
2629
+ url: finalUrl,
2630
+ metadata: { provider: this.id, model: model ?? "p-image" },
2631
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2632
+ };
2474
2633
  }
2475
- async generateVideo(_request) {
2476
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2634
+ async generateVideo(request) {
2635
+ let finalUrl;
2636
+ const model = this.config.models?.textToVideo;
2637
+ for await (const stage of generateWithPruna(
2638
+ { prompt: request.prompt, model, duration: request.duration, aspectRatio: request.aspectRatio },
2639
+ this.config.apiKey
2640
+ )) {
2641
+ if (stage.error) throw stage.error;
2642
+ if (stage.result?.url) finalUrl = stage.result.url;
2643
+ }
2644
+ if (!finalUrl) {
2645
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated by Pruna AI");
2646
+ }
2647
+ return {
2648
+ id: Date.now().toString(),
2649
+ type: "video",
2650
+ url: finalUrl,
2651
+ metadata: { provider: this.id, model: model ?? "p-video" },
2652
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2653
+ };
2477
2654
  }
2478
- async imageToVideo(_request) {
2479
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2655
+ async imageToVideo(request) {
2656
+ let finalUrl;
2657
+ for await (const stage of generateWithPruna(
2658
+ { image: request.imageUrl, prompt: request.prompt, duration: request.duration, motion: request.motion },
2659
+ this.config.apiKey
2660
+ )) {
2661
+ if (stage.error) throw stage.error;
2662
+ if (stage.result?.url) finalUrl = stage.result.url;
2663
+ }
2664
+ if (!finalUrl) {
2665
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated from image by Pruna AI");
2666
+ }
2667
+ return {
2668
+ id: Date.now().toString(),
2669
+ type: "video",
2670
+ url: finalUrl,
2671
+ metadata: { provider: this.id, model: "p-image-to-video" },
2672
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2673
+ };
2480
2674
  }
2481
2675
  async videoToVideo(_request) {
2482
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2676
+ throw new AIError("CLIENT_ERROR" /* CLIENT_ERROR */, "Pruna AI provider does not support video-to-video conversion");
2483
2677
  }
2484
2678
  };
2485
2679
 
@@ -3086,11 +3280,12 @@ var buildScriptResult = (parsed, duration) => {
3086
3280
  if (!script) {
3087
3281
  throw new Error("AI response missing script field");
3088
3282
  }
3283
+ const wordCount = script.trim().length === 0 ? 0 : script.trim().split(/\s+/).length;
3089
3284
  return {
3090
3285
  ...parsed,
3091
3286
  script,
3092
3287
  duration,
3093
- wordCount: script.split(" ").length
3288
+ wordCount
3094
3289
  };
3095
3290
  };
3096
3291
  var VideoScriptService = class {
@@ -3285,7 +3480,6 @@ var SeoService = class {
3285
3480
  analyzeKeywords(content, keywords) {
3286
3481
  const validatedContent = validateContentBody(content);
3287
3482
  const validatedKeywords = validateKeywordList(keywords);
3288
- void countWordsInContent(validatedContent);
3289
3483
  return validatedKeywords.map((keyword) => {
3290
3484
  const count = countKeywordOccurrences(validatedContent, keyword);
3291
3485
  const density = calculateKeywordDensity(validatedContent, keyword);
@@ -3380,68 +3574,20 @@ var ABTestService = class {
3380
3574
  });
3381
3575
  const scoreA = predictions[0]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
3382
3576
  const scoreB = predictions[1]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
3577
+ const isTie = scoreA === scoreB;
3383
3578
  const winner = selectHigherScoreVariantId(scoreA, scoreB, VARIANT_A_ID, VARIANT_B_ID);
3384
3579
  const improvement = calculateImprovementPercentage(scoreA, scoreB);
3385
3580
  return {
3386
3581
  winner,
3387
3582
  confidence: AB_TEST_DEFAULT_CONFIDENCE,
3388
- improvement: `+${improvement}% engagement`,
3389
- reasoning: winner === VARIANT_A_ID ? "Variant A has more engaging content" : "Variant B has more engaging content",
3583
+ improvement: isTie ? "0%" : `+${improvement}% engagement`,
3584
+ reasoning: isTie ? "Both variants have equal predicted engagement" : winner === VARIANT_A_ID ? "Variant A has more engaging content" : "Variant B has more engaging content",
3390
3585
  recommendations: [...DEFAULT_RECOMMENDATIONS]
3391
3586
  };
3392
3587
  }
3393
3588
  };
3394
3589
 
3395
- // src/content/infrastructure/utils/jsonParser.ts
3396
- function repairCommonJsonDefects(text) {
3397
- return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
3398
- }
3399
- function safeJSONParse(text, fallback, onError) {
3400
- try {
3401
- return JSON.parse(text);
3402
- } catch (error) {
3403
- try {
3404
- return JSON.parse(repairCommonJsonDefects(text));
3405
- } catch {
3406
- }
3407
- if (error instanceof SyntaxError) {
3408
- const positionMatch = error.message.match(/position (\d+)/);
3409
- const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
3410
- if (position > 0) {
3411
- text.substring(Math.max(0, position - 50), position + 50);
3412
- }
3413
- }
3414
- return fallback;
3415
- }
3416
- }
3417
- function extractJSON(text) {
3418
- const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
3419
- if (codeBlockMatch && codeBlockMatch[1]) {
3420
- const candidate = codeBlockMatch[1].trim();
3421
- if (candidate.startsWith("{") || candidate.startsWith("[")) {
3422
- return candidate;
3423
- }
3424
- }
3425
- const objectMatch = text.match(/\{[\s\S]*\}/);
3426
- if (objectMatch) {
3427
- return objectMatch[0];
3428
- }
3429
- const arrayMatch = text.match(/\[[\s\S]*\]/);
3430
- if (arrayMatch) {
3431
- return arrayMatch[0];
3432
- }
3433
- return null;
3434
- }
3435
- function parseAIResponse(response, fallback, onError) {
3436
- const json = extractJSON(response);
3437
- if (!json) {
3438
- return fallback;
3439
- }
3440
- return safeJSONParse(json, fallback);
3441
- }
3442
-
3443
3590
  // src/content/application/services/ImagePromptService.ts
3444
- var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
3445
3591
  var ImagePromptService = class {
3446
3592
  constructor(executor) {
3447
3593
  this.executor = executor;
@@ -3455,10 +3601,11 @@ var ImagePromptService = class {
3455
3601
  async generateImagePromptsForBlog(blogContent) {
3456
3602
  const truncated = truncateForBlogPromptPreview(validateContentBody(blogContent));
3457
3603
  const prompt = PromptBuilder.blogImagePrompts(truncated);
3458
- const response = await this.executor.runText(prompt, { maxTokens: 500, temperature: 0.8 });
3459
- const jsonMatch = response.match(JSON_ARRAY_PATTERN);
3460
- if (!jsonMatch) return [];
3461
- return safeJSONParse(jsonMatch[0], []);
3604
+ const prompts = await this.executor.runJson(prompt, [], {
3605
+ maxTokens: 500,
3606
+ temperature: 0.8
3607
+ });
3608
+ return Array.isArray(prompts) ? prompts : [];
3462
3609
  }
3463
3610
  };
3464
3611
 
@@ -3502,11 +3649,16 @@ var ContentCalendarService = class {
3502
3649
  const validatedNiche = validateTopicText(niche);
3503
3650
  const validatedDays = validateCalendarDays(days);
3504
3651
  const prompt = PromptBuilder.contentCalendar(validatedNiche, validatedDays);
3505
- const data = await this.executor.runJson(prompt, [], {
3506
- maxTokens: estimateTokenBudgetForCalendarDays(validatedDays),
3507
- temperature: 0.8
3508
- });
3509
- return data.map(toCalendarEntry);
3652
+ const data = await this.executor.runJson(
3653
+ prompt,
3654
+ [],
3655
+ {
3656
+ maxTokens: estimateTokenBudgetForCalendarDays(validatedDays),
3657
+ temperature: 0.8
3658
+ }
3659
+ );
3660
+ const items = Array.isArray(data) ? data : Array.isArray(data?.calendar) ? data.calendar : Array.isArray(data?.entries) ? data.entries : [];
3661
+ return items.map(toCalendarEntry);
3510
3662
  }
3511
3663
  };
3512
3664
 
@@ -3554,6 +3706,54 @@ var MediaGenerationService = class {
3554
3706
  }
3555
3707
  };
3556
3708
 
3709
+ // src/content/infrastructure/utils/jsonParser.ts
3710
+ function repairCommonJsonDefects2(text) {
3711
+ return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
3712
+ }
3713
+ function safeJSONParse(text, fallback, onError) {
3714
+ try {
3715
+ return JSON.parse(text);
3716
+ } catch (error) {
3717
+ try {
3718
+ return JSON.parse(repairCommonJsonDefects2(text));
3719
+ } catch {
3720
+ }
3721
+ if (error instanceof SyntaxError) {
3722
+ const positionMatch = error.message.match(/position (\d+)/);
3723
+ const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
3724
+ if (position > 0) {
3725
+ text.substring(Math.max(0, position - 50), position + 50);
3726
+ }
3727
+ }
3728
+ return fallback;
3729
+ }
3730
+ }
3731
+ function extractJSON(text) {
3732
+ const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
3733
+ if (codeBlockMatch && codeBlockMatch[1]) {
3734
+ const candidate = codeBlockMatch[1].trim();
3735
+ if (candidate.startsWith("{") || candidate.startsWith("[")) {
3736
+ return candidate;
3737
+ }
3738
+ }
3739
+ const objectMatch = text.match(/\{[\s\S]*\}/);
3740
+ if (objectMatch) {
3741
+ return objectMatch[0];
3742
+ }
3743
+ const arrayMatch = text.match(/\[[\s\S]*\]/);
3744
+ if (arrayMatch) {
3745
+ return arrayMatch[0];
3746
+ }
3747
+ return null;
3748
+ }
3749
+ function parseAIResponse(response, fallback, onError) {
3750
+ const json = extractJSON(response);
3751
+ if (!json) {
3752
+ return fallback;
3753
+ }
3754
+ return safeJSONParse(json, fallback);
3755
+ }
3756
+
3557
3757
  // src/content/application/services/GenerationExecutor.ts
3558
3758
  var GenerationExecutor = class {
3559
3759
  constructor(generator) {
@@ -3584,6 +3784,13 @@ var isTextGenerator = (value) => typeof value === "object" && value !== null &&
3584
3784
  function toAIProviderConfig(config) {
3585
3785
  const legacy = config;
3586
3786
  const modern = config;
3787
+ const anthropic = modern.anthropic ?? (legacy.anthropic?.enabled ? {
3788
+ apiKey: legacy.anthropic.apiKey,
3789
+ baseUrl: legacy.anthropic.baseUrl,
3790
+ timeoutMs: legacy.anthropic.timeout,
3791
+ models: legacy.anthropic.models,
3792
+ effort: legacy.anthropic.effort
3793
+ } : void 0);
3587
3794
  const groq = modern.groq ?? (legacy.groq?.enabled ? {
3588
3795
  apiKey: legacy.groq.apiKey,
3589
3796
  baseUrl: legacy.groq.baseUrl,
@@ -3598,7 +3805,7 @@ function toAIProviderConfig(config) {
3598
3805
  priority: modern.priority,
3599
3806
  fallbackEnabled: modern.fallbackEnabled,
3600
3807
  defaultTier: modern.defaultTier,
3601
- anthropic: modern.anthropic,
3808
+ ...anthropic ? { anthropic } : {},
3602
3809
  ...groq ? { groq } : {},
3603
3810
  ...gemini ? { gemini } : {},
3604
3811
  ...fal ? { fal } : {},