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