@adaptic/lumic-utils 1.0.24 → 1.0.25

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 (36) hide show
  1. package/dist/{apollo-client.client-CyxU1hTu.js → apollo-client.client-D6jG5B1n.js} +3 -3
  2. package/dist/{apollo-client.client-CyxU1hTu.js.map → apollo-client.client-D6jG5B1n.js.map} +1 -1
  3. package/dist/{apollo-client.client-9wJcufhf.js → apollo-client.client-DLW-p46Y.js} +4 -4
  4. package/dist/{apollo-client.client-9wJcufhf.js.map → apollo-client.client-DLW-p46Y.js.map} +1 -1
  5. package/dist/{apollo-client.server-CbagxkmK.js → apollo-client.server-CXLiLmSz.js} +3 -3
  6. package/dist/{apollo-client.server-CbagxkmK.js.map → apollo-client.server-CXLiLmSz.js.map} +1 -1
  7. package/dist/{apollo-client.server-DB3jLbBx.js → apollo-client.server-Cx30LaNh.js} +3 -3
  8. package/dist/{apollo-client.server-DB3jLbBx.js.map → apollo-client.server-Cx30LaNh.js.map} +1 -1
  9. package/dist/{index-EMQ1uJMh.js → index-Bvc61lYS.js} +348 -381
  10. package/dist/{index-EMQ1uJMh.js.map → index-Bvc61lYS.js.map} +1 -1
  11. package/dist/{index-KzQOh2uu.js → index-ChygDqeG.js} +348 -381
  12. package/dist/{index-KzQOh2uu.js.map → index-ChygDqeG.js.map} +1 -1
  13. package/dist/{index-B4yVKGNR.js → index-CqnZ-XiI.js} +2 -2
  14. package/dist/{index-B4yVKGNR.js.map → index-CqnZ-XiI.js.map} +1 -1
  15. package/dist/{index-CL79JTWc.js → index-D2TPDKw0.js} +2 -2
  16. package/dist/{index-CL79JTWc.js.map → index-D2TPDKw0.js.map} +1 -1
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.mjs +1 -1
  19. package/dist/test.cjs +17 -21
  20. package/dist/test.cjs.map +1 -1
  21. package/dist/test.mjs +17 -21
  22. package/dist/test.mjs.map +1 -1
  23. package/dist/types/functions/get-weather.d.ts +1 -1
  24. package/dist/types/functions/google-sheets.d.ts +2 -2
  25. package/dist/types/functions/json-llm-tools.d.ts +1 -1
  26. package/dist/types/functions/llm-anthropic.d.ts +1 -1
  27. package/dist/types/functions/llm-deepseek.d.ts +2 -2
  28. package/dist/types/functions/llm-openai-compatible.d.ts +2 -2
  29. package/dist/types/functions/llm-openai.d.ts +8 -3
  30. package/dist/types/functions/llm-utils.d.ts +1 -1
  31. package/dist/types/test-llm-functions-archive.d.ts +1 -1
  32. package/dist/types/test.d.ts +1 -1
  33. package/dist/types/types/openai-types.d.ts +26 -10
  34. package/dist/types/utils/aws-initialise.d.ts +4 -4
  35. package/dist/types/utils/retry.d.ts +8 -0
  36. package/package.json +2 -2
@@ -551,19 +551,10 @@ function getModelProvider(model) {
551
551
  return SUPPORTED_MODELS[model].provider;
552
552
  }
553
553
  /**
554
- * Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars.
555
- *
556
- * OpenAI `advanced` was reverted from `gpt-5.5` back to `gpt-5.4` on 2026-05-30:
557
- * the heavy tool-call prompt in adaptic-engine's trading-decision path was
558
- * timing out even after the engine raised `LLM_CALL_TIMEOUT_MS` to 90s (5+
559
- * tickers per loop still exceeded the budget). `gpt-5.5`'s 1M-context window
560
- * is not required on that path — the prompt fits comfortably inside `gpt-5.4`'s
561
- * envelope and `gpt-5.4` returns inside the original 25-30s p95. `gpt-5.5`
562
- * remains registered in OPENAI_MODELS and reachable by explicit model id;
563
- * only the `advanced` tier default is rolled back.
554
+ * Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars
564
555
  */
565
556
  const PROVIDER_DEFAULT_MODELS = {
566
- openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.4' },
557
+ openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.5' },
567
558
  anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7' },
568
559
  deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro' },
569
560
  kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6' },
@@ -1517,13 +1508,11 @@ let openai;
1517
1508
  function initializeOpenAI(apiKey) {
1518
1509
  const key = apiKey || getSecrets().openai.apiKey;
1519
1510
  if (!key) {
1520
- throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
1511
+ throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
1521
1512
  }
1522
1513
  return new OpenAI({
1523
1514
  apiKey: key,
1524
- defaultHeaders: {
1525
- "User-Agent": "My Server-side Application (compatible; OpenAI API Client)",
1526
- },
1515
+ defaultHeaders: { 'User-Agent': 'My Server-side Application (compatible; OpenAI API Client)' },
1527
1516
  });
1528
1517
  }
1529
1518
  /**
@@ -1541,7 +1530,9 @@ function initializeOpenAI(apiKey) {
1541
1530
  */
1542
1531
  async function fixJsonWithAI(jsonStr, options) {
1543
1532
  // Backward compatibility: handle string apiKey as first positional param
1544
- const opts = typeof options === "string" ? { apiKey: options } : (options ?? {});
1533
+ const opts = typeof options === 'string'
1534
+ ? { apiKey: options }
1535
+ : (options ?? {});
1545
1536
  const apiKey = opts.apiKey;
1546
1537
  const maxDepth = opts.maxDepth ?? 2;
1547
1538
  const depth = opts._depth ?? 0;
@@ -1554,18 +1545,18 @@ async function fixJsonWithAI(jsonStr, options) {
1554
1545
  openai = initializeOpenAI(apiKey);
1555
1546
  }
1556
1547
  const completion = await openai.chat.completions.create({
1557
- model: "gpt-5-mini",
1548
+ model: 'gpt-5-mini',
1558
1549
  messages: [
1559
1550
  {
1560
- role: "system",
1561
- content: "You are a JSON fixer. Return only valid JSON without any additional text or explanation.",
1551
+ role: 'system',
1552
+ content: 'You are a JSON fixer. Return only valid JSON without any additional text or explanation.'
1562
1553
  },
1563
1554
  {
1564
- role: "user",
1565
- content: `Fix this broken JSON:\n${jsonStr}`,
1566
- },
1555
+ role: 'user',
1556
+ content: `Fix this broken JSON:\n${jsonStr}`
1557
+ }
1567
1558
  ],
1568
- response_format: { type: "json_object" },
1559
+ response_format: { type: 'json_object' }
1569
1560
  });
1570
1561
  const fixedJson = completion.choices[0]?.message?.content;
1571
1562
  if (fixedJson && isValidJson(fixedJson)) {
@@ -1579,32 +1570,23 @@ async function fixJsonWithAI(jsonStr, options) {
1579
1570
  _depth: depth + 1,
1580
1571
  });
1581
1572
  }
1582
- throw new JsonParseError("Failed to fix JSON with AI: returned invalid JSON");
1573
+ throw new JsonParseError('Failed to fix JSON with AI: returned invalid JSON');
1583
1574
  }
1584
1575
  catch (err) {
1585
1576
  // Re-throw JsonParseError as-is (e.g. max depth exceeded)
1586
1577
  if (err instanceof JsonParseError) {
1587
1578
  throw err;
1588
1579
  }
1589
- const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
1580
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
1590
1581
  throw new JsonParseError(`Error fixing JSON with AI: ${errorMessage}`);
1591
1582
  }
1592
1583
  }
1593
1584
 
1594
1585
  // llm-utils.ts
1595
1586
  function normalizeModelName(model) {
1596
- return model.replace(/_/g, "-").toLowerCase();
1597
- }
1598
- const CODE_BLOCK_TYPES = [
1599
- "javascript",
1600
- "js",
1601
- "graphql",
1602
- "json",
1603
- "typescript",
1604
- "python",
1605
- "markdown",
1606
- "yaml",
1607
- ];
1587
+ return model.replace(/_/g, '-').toLowerCase();
1588
+ }
1589
+ const CODE_BLOCK_TYPES = ['javascript', 'js', 'graphql', 'json', 'typescript', 'python', 'markdown', 'yaml'];
1608
1590
  /**
1609
1591
  * Tries to parse JSON from the given content string according to the specified
1610
1592
  * response format. If the response format is 'json' or a JSON schema object, it
@@ -1628,9 +1610,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1628
1610
  if (recursionDepth > maxRetries) {
1629
1611
  throw new Error(`Maximum recursion depth (${maxRetries}) exceeded while parsing JSON. AI fixing may have returned broken JSON.`);
1630
1612
  }
1631
- if (responseFormat === "json" ||
1632
- (typeof responseFormat === "object" &&
1633
- responseFormat?.type === "json_schema")) {
1613
+ if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat?.type === 'json_schema')) {
1634
1614
  let cleanedContent = content.trim();
1635
1615
  let detectedType = null;
1636
1616
  // Remove code block markers if present
@@ -1641,8 +1621,8 @@ async function parseResponse(content, responseFormat, options = {}) {
1641
1621
  }
1642
1622
  return false;
1643
1623
  });
1644
- if (startsWithCodeBlock && cleanedContent.endsWith("```")) {
1645
- const firstLineEndIndex = cleanedContent.indexOf("\n");
1624
+ if (startsWithCodeBlock && cleanedContent.endsWith('```')) {
1625
+ const firstLineEndIndex = cleanedContent.indexOf('\n');
1646
1626
  if (firstLineEndIndex !== -1) {
1647
1627
  cleanedContent = cleanedContent.slice(firstLineEndIndex + 1, -3).trim();
1648
1628
  getLumicLogger().info(`Code block for type ${detectedType} detected and removed. Cleaned content: ${cleanedContent}`);
@@ -1675,7 +1655,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1675
1655
  },
1676
1656
  // Strategy 3: Remove leading/trailing text and try parsing
1677
1657
  () => {
1678
- const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, "");
1658
+ const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, '');
1679
1659
  try {
1680
1660
  return JSON.parse(jsonMatch);
1681
1661
  }
@@ -1697,7 +1677,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1697
1677
  }
1698
1678
  // Strategy 5: Use AI fixing (only if explicitly enabled)
1699
1679
  if (enableAiFix) {
1700
- getLumicLogger().warn("Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.");
1680
+ getLumicLogger().warn('Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.');
1701
1681
  try {
1702
1682
  const aiFixedJson = await fixJsonWithAI(cleanedContent);
1703
1683
  if (aiFixedJson !== null) {
@@ -1710,7 +1690,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1710
1690
  catch {
1711
1691
  // AI returned something that looks like JSON but isn't valid
1712
1692
  // Increment recursion depth and try parsing the AI response
1713
- getLumicLogger().warn("AI fixing returned invalid JSON, attempting recursive parse...");
1693
+ getLumicLogger().warn('AI fixing returned invalid JSON, attempting recursive parse...');
1714
1694
  return parseResponse(JSON.stringify(aiFixedJson), responseFormat, {
1715
1695
  ...options,
1716
1696
  _recursionDepth: recursionDepth + 1,
@@ -1719,14 +1699,14 @@ async function parseResponse(content, responseFormat, options = {}) {
1719
1699
  }
1720
1700
  }
1721
1701
  catch (error) {
1722
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1702
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1723
1703
  getLumicLogger().error(`AI JSON fixing failed: ${errorMessage}`);
1724
1704
  // Continue to final error below
1725
1705
  }
1726
1706
  }
1727
1707
  // If all strategies fail, throw an error
1728
1708
  getLumicLogger().error(`Failed to parse JSON from content: ${cleanedContent}. Original JSON: ${content}`);
1729
- throw new Error("Unable to parse JSON response");
1709
+ throw new Error('Unable to parse JSON response');
1730
1710
  }
1731
1711
  else {
1732
1712
  return content;
@@ -2003,11 +1983,19 @@ const DEFAULT_RETRY_CONFIG = {
2003
1983
  * @throws The last error encountered if all retry attempts fail or if a non-retryable error occurs
2004
1984
  */
2005
1985
  async function withRetry(fn, config = {}, label = 'unknown') {
2006
- const { maxRetries, baseDelayMs, maxDelayMs, retryableErrors, circuitBreaker } = {
1986
+ const { maxRetries, baseDelayMs, maxDelayMs, retryableErrors, circuitBreaker, signal } = {
2007
1987
  ...DEFAULT_RETRY_CONFIG,
2008
1988
  ...config,
2009
1989
  };
2010
1990
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
1991
+ // Short-circuit on cancellation BEFORE starting a fresh attempt so
1992
+ // the caller does not pay the cost of issuing a request whose result
1993
+ // they have already discarded.
1994
+ if (signal?.aborted) {
1995
+ throw signal.reason instanceof Error
1996
+ ? signal.reason
1997
+ : new Error('Aborted');
1998
+ }
2011
1999
  try {
2012
2000
  // If a circuit breaker is provided, execute through it
2013
2001
  const result = circuitBreaker
@@ -2020,6 +2008,13 @@ async function withRetry(fn, config = {}, label = 'unknown') {
2020
2008
  if (error instanceof CircuitBreakerOpenError) {
2021
2009
  throw error;
2022
2010
  }
2011
+ // Do not retry on cooperative cancellation — the caller has
2012
+ // explicitly asked us to stop.
2013
+ if (signal?.aborted) {
2014
+ throw signal.reason instanceof Error
2015
+ ? signal.reason
2016
+ : new Error('Aborted');
2017
+ }
2023
2018
  // Don't retry if we've exhausted all attempts
2024
2019
  if (attempt === maxRetries) {
2025
2020
  throw error;
@@ -2033,7 +2028,26 @@ async function withRetry(fn, config = {}, label = 'unknown') {
2033
2028
  const jitter = Math.random() * 1000;
2034
2029
  const delay = Math.min(exponentialDelay + jitter, maxDelayMs);
2035
2030
  getLumicLogger().warn(`[${label}] Attempt ${attempt + 1} failed, retrying in ${Math.round(delay)}ms`);
2036
- await new Promise((resolve) => setTimeout(resolve, delay));
2031
+ // Sleep but bail early if signal aborts mid-backoff.
2032
+ await new Promise((resolve, reject) => {
2033
+ const timer = setTimeout(() => {
2034
+ if (signal)
2035
+ signal.removeEventListener('abort', onAbort);
2036
+ resolve();
2037
+ }, delay);
2038
+ const onAbort = () => {
2039
+ clearTimeout(timer);
2040
+ reject(signal?.reason instanceof Error ? signal.reason : new Error('Aborted'));
2041
+ };
2042
+ if (signal) {
2043
+ if (signal.aborted) {
2044
+ clearTimeout(timer);
2045
+ reject(signal.reason instanceof Error ? signal.reason : new Error('Aborted'));
2046
+ return;
2047
+ }
2048
+ signal.addEventListener('abort', onAbort, { once: true });
2049
+ }
2050
+ });
2037
2051
  }
2038
2052
  }
2039
2053
  // This should never be reached, but TypeScript needs it for type safety
@@ -2320,15 +2334,13 @@ const isRetryableLLMError = (error) => {
2320
2334
  if (error instanceof Error) {
2321
2335
  const message = error.message;
2322
2336
  // Retry on rate limits (429)
2323
- if (message.includes("429") ||
2324
- message.includes("rate limit") ||
2325
- message.includes("Rate limit")) {
2337
+ if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
2326
2338
  return true;
2327
2339
  }
2328
2340
  // Retry on transient body-corruption 400s. Match the exact OpenAI error
2329
2341
  // string to avoid retrying genuine client-side validation 400s (which
2330
2342
  // would re-fail forever and waste retry budget).
2331
- if (message.includes("could not parse the JSON body of your request")) {
2343
+ if (message.includes('could not parse the JSON body of your request')) {
2332
2344
  return true;
2333
2345
  }
2334
2346
  }
@@ -2397,26 +2409,23 @@ function isReasoningModel(model) {
2397
2409
  * @throws Error if the response format is invalid or incompatible.
2398
2410
  */
2399
2411
  function getResponseFormatOption(responseFormat, normalizedModel) {
2400
- if (responseFormat === "text") {
2401
- return { type: "text" };
2412
+ if (responseFormat === 'text') {
2413
+ return { type: 'text' };
2402
2414
  }
2403
- if (responseFormat === "json") {
2404
- return supportsJsonMode(normalizedModel)
2405
- ? { type: "json_object" }
2406
- : { type: "text" };
2415
+ if (responseFormat === 'json') {
2416
+ return supportsJsonMode(normalizedModel) ? { type: 'json_object' } : { type: 'text' };
2407
2417
  }
2408
- if (typeof responseFormat === "object" &&
2409
- responseFormat.type === "json_schema") {
2418
+ if (typeof responseFormat === 'object' && responseFormat.type === 'json_schema') {
2410
2419
  if (!isStructuredOutputCompatibleModel(normalizedModel)) {
2411
2420
  throw new Error(`${normalizedModel} is not compatible with structured outputs / json object.`);
2412
2421
  }
2413
2422
  return {
2414
- type: "json_schema",
2423
+ type: 'json_schema',
2415
2424
  json_schema: {
2416
- type: "object",
2425
+ type: 'object',
2417
2426
  properties: responseFormat.schema.properties,
2418
2427
  required: responseFormat.schema.required || [],
2419
- name: "Response",
2428
+ name: 'Response',
2420
2429
  },
2421
2430
  };
2422
2431
  }
@@ -2475,7 +2484,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2475
2484
  const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
2476
2485
  const apiKey = options.apiKey || getSecrets().openai.apiKey;
2477
2486
  if (!apiKey) {
2478
- throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
2487
+ throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
2479
2488
  }
2480
2489
  const openai = new OpenAI({
2481
2490
  apiKey: apiKey,
@@ -2485,7 +2494,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2485
2494
  // Add developer prompt if present
2486
2495
  if (options.developerPrompt && supportsDeveloperPrompt(normalizedModel)) {
2487
2496
  messages.push({
2488
- role: "developer",
2497
+ role: 'developer',
2489
2498
  content: options.developerPrompt,
2490
2499
  });
2491
2500
  }
@@ -2494,15 +2503,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2494
2503
  messages.push(...options.context);
2495
2504
  }
2496
2505
  // Add user content
2497
- if (typeof content === "string") {
2506
+ if (typeof content === 'string') {
2498
2507
  messages.push({
2499
- role: "user",
2508
+ role: 'user',
2500
2509
  content,
2501
2510
  });
2502
2511
  }
2503
2512
  else {
2504
2513
  messages.push({
2505
- role: "user",
2514
+ role: 'user',
2506
2515
  content,
2507
2516
  });
2508
2517
  }
@@ -2516,8 +2525,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2516
2525
  queryOptions.tools = options.tools;
2517
2526
  }
2518
2527
  // Only include temperature if the model supports it
2519
- if (options.temperature !== undefined &&
2520
- supportsTemperature(normalizedModel)) {
2528
+ if (options.temperature !== undefined && supportsTemperature(normalizedModel)) {
2521
2529
  queryOptions.temperature = options.temperature;
2522
2530
  }
2523
2531
  // Only include max_completion_tokens when specified
@@ -2527,16 +2535,25 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2527
2535
  // Only set response_format when it's not the default 'text' type
2528
2536
  // (sending response_format: {type: 'text'} is redundant and may cause issues)
2529
2537
  const responseFormatOption = getResponseFormatOption(responseFormat, normalizedModel);
2530
- if (responseFormatOption.type !== "text") {
2538
+ if (responseFormatOption.type !== 'text') {
2531
2539
  queryOptions.response_format = responseFormatOption;
2532
2540
  }
2533
2541
  let completion;
2534
2542
  try {
2535
- completion = await withRetry(() => openai.chat.completions.create(queryOptions), {
2543
+ completion = await withRetry(
2544
+ // Pass the AbortSignal into the OpenAI SDK request-options object.
2545
+ // The OpenAI SDK honours `signal` to cancel the in-flight fetch
2546
+ // immediately when it fires — this is what makes caller-driven
2547
+ // cancel-and-restart (e.g. signal-monitoring's materiality
2548
+ // re-trigger path in the engine) actually release the HTTP
2549
+ // connection and response-body buffer rather than just discarding
2550
+ // the resolved value at the engine boundary.
2551
+ () => openai.chat.completions.create(queryOptions, options.signal ? { signal: options.signal } : undefined), {
2536
2552
  maxRetries: 3,
2537
2553
  baseDelayMs: 2000,
2538
2554
  maxDelayMs: 30000,
2539
2555
  retryableErrors: isRetryableLLMError,
2556
+ signal: options.signal,
2540
2557
  }, `OpenAI:${normalizedModel}`);
2541
2558
  }
2542
2559
  catch (error) {
@@ -2548,19 +2565,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2548
2565
  // for production prompts containing sensitive context.
2549
2566
  const errorMessage = error instanceof Error ? error.message : String(error);
2550
2567
  const totalContentChars = messages.reduce((sum, msg) => {
2551
- if (typeof msg.content === "string")
2568
+ if (typeof msg.content === 'string')
2552
2569
  return sum + msg.content.length;
2553
2570
  if (Array.isArray(msg.content)) {
2554
- return (sum +
2555
- msg.content.reduce((s, part) => {
2556
- if (typeof part === "object" &&
2557
- part !== null &&
2558
- "text" in part &&
2559
- typeof part.text === "string") {
2560
- return s + part.text.length;
2561
- }
2562
- return s;
2563
- }, 0));
2571
+ return sum + msg.content.reduce((s, part) => {
2572
+ if (typeof part === 'object' && part !== null && 'text' in part && typeof part.text === 'string') {
2573
+ return s + part.text.length;
2574
+ }
2575
+ return s;
2576
+ }, 0);
2564
2577
  }
2565
2578
  return sum;
2566
2579
  }, 0);
@@ -2587,7 +2600,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2587
2600
  const cachedTokens = completion.usage?.prompt_tokens_details?.cached_tokens ?? 0;
2588
2601
  const response = {
2589
2602
  id: completion.id,
2590
- content: completion.choices[0]?.message?.content || "",
2603
+ content: completion.choices[0]?.message?.content || '',
2591
2604
  tool_calls: completion.choices[0]?.message?.tool_calls,
2592
2605
  usage: {
2593
2606
  prompt_tokens: completion.usage?.prompt_tokens ?? 0,
@@ -2597,7 +2610,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2597
2610
  },
2598
2611
  system_fingerprint: completion.system_fingerprint,
2599
2612
  service_tier: options.service_tier,
2600
- provider: "openai",
2613
+ provider: 'openai',
2601
2614
  model: normalizedModel,
2602
2615
  };
2603
2616
  return response;
@@ -2610,7 +2623,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2610
2623
  * @param options The options for the LLM call. Defaults to an empty object.
2611
2624
  * @return A promise that resolves to the response from the LLM.
2612
2625
  */
2613
- const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", options = {}) => {
2626
+ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', options = {}) => {
2614
2627
  const mergedOptions = {
2615
2628
  ...DEFAULT_OPTIONS$1,
2616
2629
  ...options,
@@ -2619,7 +2632,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2619
2632
  // Track cost in the global cost tracker. Pass cached tokens through so the
2620
2633
  // tracker applies the discounted cached-input rate (typically ~50% of the
2621
2634
  // standard input rate) instead of billing every input token at full price.
2622
- getLLMCostTracker().trackUsage("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
2635
+ getLLMCostTracker().trackUsage('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
2623
2636
  // Handle tool calls differently
2624
2637
  if (completion.tool_calls && completion.tool_calls.length > 0) {
2625
2638
  const toolCallResponse = {
@@ -2635,10 +2648,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2635
2648
  prompt_tokens: completion.usage.prompt_tokens,
2636
2649
  completion_tokens: completion.usage.completion_tokens,
2637
2650
  reasoning_tokens: 0,
2638
- provider: "openai",
2651
+ provider: 'openai',
2639
2652
  model: completion.model,
2640
2653
  cached_tokens: completion.usage.cached_tokens,
2641
- cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2654
+ cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2642
2655
  },
2643
2656
  tool_calls: completion.tool_calls,
2644
2657
  };
@@ -2646,7 +2659,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2646
2659
  // Handle regular responses
2647
2660
  const parsedResponse = await parseResponse(completion.content, responseFormat);
2648
2661
  if (parsedResponse === null) {
2649
- throw new Error("Failed to parse response");
2662
+ throw new Error('Failed to parse response');
2650
2663
  }
2651
2664
  return {
2652
2665
  response: parsedResponse,
@@ -2654,10 +2667,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2654
2667
  prompt_tokens: completion.usage.prompt_tokens,
2655
2668
  completion_tokens: completion.usage.completion_tokens,
2656
2669
  reasoning_tokens: 0,
2657
- provider: "openai",
2670
+ provider: 'openai',
2658
2671
  model: completion.model,
2659
2672
  cached_tokens: completion.usage.cached_tokens,
2660
- cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2673
+ cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2661
2674
  },
2662
2675
  tool_calls: completion.tool_calls,
2663
2676
  };
@@ -2692,44 +2705,45 @@ const makeResponsesAPICall = async (input, options = {}) => {
2692
2705
  const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
2693
2706
  const apiKey = options.apiKey || getSecrets().openai.apiKey;
2694
2707
  if (!apiKey) {
2695
- throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
2708
+ throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
2696
2709
  }
2697
2710
  const openai = new OpenAI({
2698
2711
  apiKey: apiKey,
2699
2712
  timeout: options.timeout ?? LLM_TIMEOUT_MS,
2700
2713
  });
2701
- // Remove apiKey, model, and timeout from options before creating request body
2702
- const { apiKey: _, model: __, timeout: ___, ...cleanOptions } = options;
2714
+ // Remove apiKey, model, timeout, and signal from options before creating request body
2715
+ const { apiKey: _, model: __, timeout: ___, signal: ____, ...cleanOptions } = options;
2703
2716
  const requestBody = {
2704
2717
  model: normalizedModel,
2705
2718
  input,
2706
2719
  ...cleanOptions,
2707
2720
  };
2708
2721
  // Make the API call to the Responses endpoint
2709
- const response = await withRetry(() => openai.responses.create(requestBody), {
2722
+ const response = await withRetry(() => openai.responses.create(requestBody, options.signal ? { signal: options.signal } : undefined), {
2710
2723
  maxRetries: 3,
2711
2724
  baseDelayMs: 2000,
2712
2725
  maxDelayMs: 30000,
2713
2726
  retryableErrors: isRetryableLLMError,
2727
+ signal: options.signal,
2714
2728
  }, `OpenAI-Responses:${normalizedModel}`);
2715
2729
  // Responses API exposes cached input tokens under `input_tokens_details.cached_tokens`
2716
2730
  // (the equivalent of Chat Completions' `prompt_tokens_details.cached_tokens`).
2717
2731
  const responsesCachedTokens = response.usage?.input_tokens_details?.cached_tokens || 0;
2718
2732
  // Track cost in the global cost tracker
2719
- getLLMCostTracker().trackUsage("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
2733
+ getLLMCostTracker().trackUsage('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
2720
2734
  // Extract tool calls from the output
2721
2735
  const toolCalls = response.output
2722
- ?.filter((item) => item.type === "function_call")
2736
+ ?.filter((item) => item.type === 'function_call')
2723
2737
  .map((toolCall) => ({
2724
2738
  id: toolCall.call_id,
2725
- type: "function",
2739
+ type: 'function',
2726
2740
  function: {
2727
2741
  name: toolCall.name,
2728
2742
  arguments: toolCall.arguments,
2729
2743
  },
2730
2744
  }));
2731
2745
  // Extract code interpreter outputs if present
2732
- const codeInterpreterCalls = response.output?.filter((item) => item.type === "code_interpreter_call");
2746
+ const codeInterpreterCalls = response.output?.filter((item) => item.type === 'code_interpreter_call');
2733
2747
  let codeInterpreterOutputs = undefined;
2734
2748
  if (codeInterpreterCalls && codeInterpreterCalls.length > 0) {
2735
2749
  // Each code_interpreter_call has an 'outputs' array property
@@ -2755,34 +2769,32 @@ const makeResponsesAPICall = async (input, options = {}) => {
2755
2769
  prompt_tokens: response.usage?.input_tokens || 0,
2756
2770
  completion_tokens: response.usage?.output_tokens || 0,
2757
2771
  reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
2758
- provider: "openai",
2772
+ provider: 'openai',
2759
2773
  model: normalizedModel,
2760
2774
  cached_tokens: responsesCachedTokens,
2761
- cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2775
+ cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2762
2776
  },
2763
2777
  tool_calls: toolCalls,
2764
- ...(codeInterpreterOutputs
2765
- ? { code_interpreter_outputs: codeInterpreterOutputs }
2766
- : {}),
2778
+ ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
2767
2779
  };
2768
2780
  }
2769
2781
  // Extract text content from the response output
2770
2782
  const textContent = response.output
2771
- ?.filter((item) => item.type === "message")
2783
+ ?.filter((item) => item.type === 'message')
2772
2784
  .map((item) => item.content
2773
- .filter((content) => content.type === "output_text")
2785
+ .filter((content) => content.type === 'output_text')
2774
2786
  .map((content) => content.text)
2775
- .join(""))
2776
- .join("") || "";
2787
+ .join(''))
2788
+ .join('') || '';
2777
2789
  // Determine the format for parsing the response
2778
- let parsingFormat = "text";
2779
- if (requestBody.text?.format?.type === "json_object") {
2780
- parsingFormat = "json";
2790
+ let parsingFormat = 'text';
2791
+ if (requestBody.text?.format?.type === 'json_object') {
2792
+ parsingFormat = 'json';
2781
2793
  }
2782
2794
  // Handle regular responses
2783
2795
  const parsedResponse = await parseResponse(textContent, parsingFormat);
2784
2796
  if (parsedResponse === null) {
2785
- throw new Error("Failed to parse response from Responses API");
2797
+ throw new Error('Failed to parse response from Responses API');
2786
2798
  }
2787
2799
  return {
2788
2800
  response: parsedResponse,
@@ -2790,15 +2802,13 @@ const makeResponsesAPICall = async (input, options = {}) => {
2790
2802
  prompt_tokens: response.usage?.input_tokens || 0,
2791
2803
  completion_tokens: response.usage?.output_tokens || 0,
2792
2804
  reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
2793
- provider: "openai",
2805
+ provider: 'openai',
2794
2806
  model: normalizedModel,
2795
2807
  cached_tokens: responsesCachedTokens,
2796
- cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2808
+ cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2797
2809
  },
2798
2810
  tool_calls: toolCalls,
2799
- ...(codeInterpreterOutputs
2800
- ? { code_interpreter_outputs: codeInterpreterOutputs }
2801
- : {}),
2811
+ ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
2802
2812
  };
2803
2813
  };
2804
2814
 
@@ -8108,12 +8118,12 @@ function sanitizeObject(obj, sensitiveFields = ['apiKey', 'token', 'secret', 'pa
8108
8118
  const isRetryableAnthropicError = (error) => {
8109
8119
  if (error instanceof Error) {
8110
8120
  const message = error.message;
8111
- if (message.includes("429") ||
8112
- message.includes("500") ||
8113
- message.includes("503") ||
8114
- message.includes("529") ||
8115
- message.includes("rate limit") ||
8116
- message.includes("overloaded")) {
8121
+ if (message.includes('429') ||
8122
+ message.includes('500') ||
8123
+ message.includes('503') ||
8124
+ message.includes('529') ||
8125
+ message.includes('rate limit') ||
8126
+ message.includes('overloaded')) {
8117
8127
  return true;
8118
8128
  }
8119
8129
  }
@@ -8128,11 +8138,8 @@ const isRetryableAnthropicError = (error) => {
8128
8138
  function translateToolsToAnthropic(tools) {
8129
8139
  return tools.map((tool) => ({
8130
8140
  name: tool.function.name,
8131
- description: tool.function.description || "",
8132
- input_schema: (tool.function.parameters || {
8133
- type: "object",
8134
- properties: {},
8135
- }),
8141
+ description: tool.function.description || '',
8142
+ input_schema: (tool.function.parameters || { type: 'object', properties: {} }),
8136
8143
  }));
8137
8144
  }
8138
8145
  /**
@@ -8145,31 +8152,27 @@ function translateContextToAnthropic(context) {
8145
8152
  const systemParts = [];
8146
8153
  const messages = [];
8147
8154
  for (const msg of context) {
8148
- if (msg.role === "system" || msg.role === "developer") {
8149
- if (typeof msg.content === "string") {
8155
+ if (msg.role === 'system' || msg.role === 'developer') {
8156
+ if (typeof msg.content === 'string') {
8150
8157
  systemParts.push(msg.content);
8151
8158
  }
8152
8159
  continue;
8153
8160
  }
8154
- if (msg.role === "user") {
8155
- const content = typeof msg.content === "string"
8161
+ if (msg.role === 'user') {
8162
+ const content = typeof msg.content === 'string'
8156
8163
  ? msg.content
8157
8164
  : JSON.stringify(msg.content);
8158
- messages.push({ role: "user", content });
8165
+ messages.push({ role: 'user', content });
8159
8166
  continue;
8160
8167
  }
8161
- if (msg.role === "assistant") {
8168
+ if (msg.role === 'assistant') {
8162
8169
  const assistantMsg = msg;
8163
8170
  // If the assistant message has tool_calls, translate to Anthropic tool_use blocks
8164
8171
  if (assistantMsg.tool_calls && assistantMsg.tool_calls.length > 0) {
8165
8172
  const contentBlocks = [];
8166
8173
  // Include text content if present
8167
- if (typeof assistantMsg.content === "string" &&
8168
- assistantMsg.content.trim()) {
8169
- contentBlocks.push({
8170
- type: "text",
8171
- text: assistantMsg.content,
8172
- });
8174
+ if (typeof assistantMsg.content === 'string' && assistantMsg.content.trim()) {
8175
+ contentBlocks.push({ type: 'text', text: assistantMsg.content });
8173
8176
  }
8174
8177
  // Translate each tool_call to a tool_use block
8175
8178
  for (const tc of assistantMsg.tool_calls) {
@@ -8181,36 +8184,32 @@ function translateContextToAnthropic(context) {
8181
8184
  input = { raw: tc.function.arguments };
8182
8185
  }
8183
8186
  contentBlocks.push({
8184
- type: "tool_use",
8187
+ type: 'tool_use',
8185
8188
  id: tc.id,
8186
8189
  name: tc.function.name,
8187
8190
  input,
8188
8191
  });
8189
8192
  }
8190
- messages.push({ role: "assistant", content: contentBlocks });
8193
+ messages.push({ role: 'assistant', content: contentBlocks });
8191
8194
  }
8192
8195
  else {
8193
- const content = typeof assistantMsg.content === "string"
8196
+ const content = typeof assistantMsg.content === 'string'
8194
8197
  ? assistantMsg.content
8195
8198
  : JSON.stringify(assistantMsg.content);
8196
- messages.push({ role: "assistant", content });
8199
+ messages.push({ role: 'assistant', content });
8197
8200
  }
8198
8201
  continue;
8199
8202
  }
8200
- if (msg.role === "tool") {
8203
+ if (msg.role === 'tool') {
8201
8204
  // Anthropic expects tool results as user messages with tool_result content blocks
8202
8205
  const toolMsg = msg;
8203
8206
  messages.push({
8204
- role: "user",
8205
- content: [
8206
- {
8207
- type: "tool_result",
8207
+ role: 'user',
8208
+ content: [{
8209
+ type: 'tool_result',
8208
8210
  tool_use_id: toolMsg.tool_call_id,
8209
- content: typeof toolMsg.content === "string"
8210
- ? toolMsg.content
8211
- : JSON.stringify(toolMsg.content),
8212
- },
8213
- ],
8211
+ content: typeof toolMsg.content === 'string' ? toolMsg.content : JSON.stringify(toolMsg.content),
8212
+ }],
8214
8213
  });
8215
8214
  continue;
8216
8215
  }
@@ -8231,13 +8230,13 @@ function translateContextToAnthropic(context) {
8231
8230
  merged.push({ role: msg.role, content: toContentBlocks(msg.content) });
8232
8231
  }
8233
8232
  }
8234
- return { messages: merged, systemText: systemParts.join("\n\n") };
8233
+ return { messages: merged, systemText: systemParts.join('\n\n') };
8235
8234
  }
8236
8235
  /** Convert string or content block array to a uniform content block array. */
8237
8236
  function toContentBlocks(content) {
8238
- if (typeof content === "string") {
8237
+ if (typeof content === 'string') {
8239
8238
  const textBlock = {
8240
- type: "text",
8239
+ type: 'text',
8241
8240
  text: content,
8242
8241
  citations: null,
8243
8242
  };
@@ -8257,11 +8256,11 @@ function toContentBlocks(content) {
8257
8256
  * @param options The options for the LLM call.
8258
8257
  * @return A promise that resolves to the response from the Anthropic API.
8259
8258
  */
8260
- async function makeAnthropicCall(content, responseFormat = "text", options = {}) {
8261
- const model = (options.model || "claude-sonnet-4-6");
8259
+ async function makeAnthropicCall(content, responseFormat = 'text', options = {}) {
8260
+ const model = (options.model || 'claude-sonnet-4-6');
8262
8261
  const apiKey = options.apiKey || getSecrets().anthropic.apiKey;
8263
8262
  if (!apiKey) {
8264
- throw new Error("Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set");
8263
+ throw new Error('Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set');
8265
8264
  }
8266
8265
  const client = new Anthropic({
8267
8266
  apiKey,
@@ -8273,10 +8272,8 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8273
8272
  systemParts.push(options.developerPrompt);
8274
8273
  }
8275
8274
  // If JSON response is requested, instruct the model via system prompt
8276
- if (responseFormat === "json" ||
8277
- (typeof responseFormat === "object" &&
8278
- responseFormat.type === "json_schema")) {
8279
- systemParts.push("You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.");
8275
+ if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
8276
+ systemParts.push('You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.');
8280
8277
  }
8281
8278
  // Build messages array
8282
8279
  const messages = [];
@@ -8289,7 +8286,7 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8289
8286
  messages.push(...translated.messages);
8290
8287
  }
8291
8288
  // Add user content
8292
- messages.push({ role: "user", content });
8289
+ messages.push({ role: 'user', content });
8293
8290
  // Build request params
8294
8291
  const requestParams = {
8295
8292
  model,
@@ -8298,37 +8295,46 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8298
8295
  };
8299
8296
  // Add system prompt if present
8300
8297
  if (systemParts.length > 0) {
8301
- requestParams.system = systemParts.join("\n\n");
8302
- }
8303
- // Add temperature if specified
8304
- if (options.temperature !== undefined) {
8305
- requestParams.temperature = options.temperature;
8306
- }
8307
- if (options.top_p !== undefined) {
8308
- requestParams.top_p = options.top_p;
8309
- }
8298
+ requestParams.system = systemParts.join('\n\n');
8299
+ }
8300
+ // Sampling controls are intentionally disabled for the Anthropic provider.
8301
+ //
8302
+ // The claude-*-4.x model family rejects any request that specifies BOTH
8303
+ // `temperature` and `top_p` ("`temperature` and `top_p` cannot both be
8304
+ // specified for this model. Please use only one." — HTTP 400), which aborts
8305
+ // the entire caller flow (e.g. the engine's account decision session).
8306
+ // Callers across the org pass both knobs generically, so rather than make
8307
+ // each one provider-aware we drop both here and let Anthropic use its model
8308
+ // defaults. `options.temperature` / `options.top_p` are intentionally not
8309
+ // forwarded. This is the Anthropic adapter only — the OpenAI, DeepSeek, and
8310
+ // OpenAI-compatible adapters still honor `temperature` and `top_p`.
8310
8311
  // Translate and add tools if present
8311
8312
  if (options.tools && options.tools.length > 0) {
8312
8313
  requestParams.tools = translateToolsToAnthropic(options.tools);
8313
8314
  }
8314
8315
  try {
8315
- const response = await withRetry(() => client.messages.create(requestParams), {
8316
+ const response = await withRetry(
8317
+ // Pass AbortSignal into the Anthropic SDK request-options object
8318
+ // so caller-driven cancellation interrupts the in-flight HTTP
8319
+ // request immediately. See LLMOptions.signal JSDoc for context.
8320
+ () => client.messages.create(requestParams, options.signal ? { signal: options.signal } : undefined), {
8316
8321
  maxRetries: 3,
8317
8322
  baseDelayMs: 2000,
8318
8323
  maxDelayMs: 30000,
8319
8324
  retryableErrors: isRetryableAnthropicError,
8325
+ signal: options.signal,
8320
8326
  }, `Anthropic:${model}`);
8321
8327
  const inputTokens = response.usage.input_tokens;
8322
8328
  const outputTokens = response.usage.output_tokens;
8323
8329
  // Track cost
8324
- getLLMCostTracker().trackUsage("anthropic", model, inputTokens, outputTokens);
8330
+ getLLMCostTracker().trackUsage('anthropic', model, inputTokens, outputTokens);
8325
8331
  // Extract tool use blocks
8326
- const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
8332
+ const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
8327
8333
  if (toolUseBlocks.length > 0) {
8328
8334
  // Translate Anthropic tool_use to OpenAI tool_calls format
8329
8335
  const toolCalls = toolUseBlocks.map((block) => ({
8330
8336
  id: block.id,
8331
- type: "function",
8337
+ type: 'function',
8332
8338
  function: {
8333
8339
  name: block.name,
8334
8340
  arguments: JSON.stringify(block.input),
@@ -8347,22 +8353,22 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8347
8353
  prompt_tokens: inputTokens,
8348
8354
  completion_tokens: outputTokens,
8349
8355
  reasoning_tokens: 0,
8350
- provider: "anthropic",
8356
+ provider: 'anthropic',
8351
8357
  model,
8352
- cost: calculateCost("anthropic", model, inputTokens, outputTokens),
8358
+ cost: calculateCost('anthropic', model, inputTokens, outputTokens),
8353
8359
  },
8354
8360
  tool_calls: toolCalls,
8355
8361
  };
8356
8362
  }
8357
8363
  // Extract text content
8358
8364
  const textContent = response.content
8359
- .filter((block) => block.type === "text")
8365
+ .filter((block) => block.type === 'text')
8360
8366
  .map((block) => block.text)
8361
- .join("");
8367
+ .join('');
8362
8368
  // Parse response
8363
8369
  const parsedResponse = await parseResponse(textContent, responseFormat);
8364
8370
  if (parsedResponse === null) {
8365
- throw new Error("Failed to parse Anthropic response");
8371
+ throw new Error('Failed to parse Anthropic response');
8366
8372
  }
8367
8373
  return {
8368
8374
  response: parsedResponse,
@@ -8370,9 +8376,9 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8370
8376
  prompt_tokens: inputTokens,
8371
8377
  completion_tokens: outputTokens,
8372
8378
  reasoning_tokens: 0,
8373
- provider: "anthropic",
8379
+ provider: 'anthropic',
8374
8380
  model,
8375
- cost: calculateCost("anthropic", model, inputTokens, outputTokens),
8381
+ cost: calculateCost('anthropic', model, inputTokens, outputTokens),
8376
8382
  },
8377
8383
  };
8378
8384
  }
@@ -8394,9 +8400,7 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8394
8400
  const isRetryableError = (error) => {
8395
8401
  if (error instanceof Error) {
8396
8402
  const message = error.message;
8397
- if (message.includes("429") ||
8398
- message.includes("rate limit") ||
8399
- message.includes("Rate limit")) {
8403
+ if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
8400
8404
  return true;
8401
8405
  }
8402
8406
  }
@@ -8410,16 +8414,14 @@ function resolveApiKey(provider, optionsApiKey) {
8410
8414
  if (optionsApiKey)
8411
8415
  return optionsApiKey;
8412
8416
  const secrets = getSecrets();
8413
- const pathParts = provider.apiKeySecretPath.split(".");
8417
+ const pathParts = provider.apiKeySecretPath.split('.');
8414
8418
  let current = secrets;
8415
8419
  for (const part of pathParts) {
8416
- if (current === null ||
8417
- current === undefined ||
8418
- typeof current !== "object")
8419
- return "";
8420
+ if (current === null || current === undefined || typeof current !== 'object')
8421
+ return '';
8420
8422
  current = current[part];
8421
8423
  }
8422
- return (typeof current === "string" ? current : "") || "";
8424
+ return (typeof current === 'string' ? current : '') || '';
8423
8425
  }
8424
8426
  /**
8425
8427
  * Makes a call to an OpenAI-compatible provider (Kimi, Qwen, or any future provider).
@@ -8433,12 +8435,12 @@ function resolveApiKey(provider, optionsApiKey) {
8433
8435
  * @param providerName The provider key in OPENAI_COMPATIBLE_PROVIDERS.
8434
8436
  * @return A promise that resolves to the response from the provider.
8435
8437
  */
8436
- async function makeOpenAICompatibleCall(content, responseFormat = "text", options = {}, providerName) {
8438
+ async function makeOpenAICompatibleCall(content, responseFormat = 'text', options = {}, providerName) {
8437
8439
  const providerConfig = OPENAI_COMPATIBLE_PROVIDERS[providerName];
8438
8440
  if (!providerConfig) {
8439
8441
  throw new Error(`Unknown OpenAI-compatible provider: ${providerName}`);
8440
8442
  }
8441
- const model = normalizeModelName(options.model || "");
8443
+ const model = normalizeModelName(options.model || '');
8442
8444
  const apiKey = resolveApiKey(providerConfig, options.apiKey);
8443
8445
  if (!apiKey) {
8444
8446
  throw new Error(`${providerConfig.name} API key is not provided and ${providerConfig.apiKeyEnvVar} environment variable is not set`);
@@ -8451,36 +8453,31 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8451
8453
  const messages = [];
8452
8454
  // Add system message with developer prompt if present
8453
8455
  if (options.developerPrompt) {
8454
- messages.push({ role: "system", content: options.developerPrompt });
8456
+ messages.push({ role: 'system', content: options.developerPrompt });
8455
8457
  }
8456
8458
  // Add context if present
8457
8459
  if (options.context) {
8458
8460
  messages.push(...options.context);
8459
8461
  }
8460
8462
  // Add user content
8461
- if (typeof content === "string") {
8462
- messages.push({ role: "user", content });
8463
+ if (typeof content === 'string') {
8464
+ messages.push({ role: 'user', content });
8463
8465
  }
8464
8466
  else {
8465
- messages.push({ role: "user", content });
8467
+ messages.push({ role: 'user', content });
8466
8468
  }
8467
8469
  // Determine if the model supports JSON mode
8468
8470
  const supportsJson = isValidModel(model) && getModelCapabilities(model).supportsJson;
8469
8471
  // If JSON response format, add hint to system prompt
8470
- if ((responseFormat === "json" ||
8471
- (typeof responseFormat === "object" &&
8472
- responseFormat.type === "json_schema")) &&
8472
+ if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) &&
8473
8473
  supportsJson) {
8474
- if (!messages.some((m) => m.role === "system")) {
8475
- messages.unshift({
8476
- role: "system",
8477
- content: "Please respond in valid JSON format.",
8478
- });
8474
+ if (!messages.some((m) => m.role === 'system')) {
8475
+ messages.unshift({ role: 'system', content: 'Please respond in valid JSON format.' });
8479
8476
  }
8480
8477
  else {
8481
- const systemMsgIndex = messages.findIndex((m) => m.role === "system");
8478
+ const systemMsgIndex = messages.findIndex((m) => m.role === 'system');
8482
8479
  const systemMsg = messages[systemMsgIndex];
8483
- if (typeof systemMsg.content === "string") {
8480
+ if (typeof systemMsg.content === 'string') {
8484
8481
  messages[systemMsgIndex] = {
8485
8482
  ...systemMsg,
8486
8483
  content: `${systemMsg.content} Please respond in valid JSON format.`,
@@ -8493,8 +8490,8 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8493
8490
  messages,
8494
8491
  };
8495
8492
  // Add response format if JSON is supported
8496
- if (responseFormat !== "text" && supportsJson) {
8497
- queryOptions.response_format = { type: "json_object" };
8493
+ if (responseFormat !== 'text' && supportsJson) {
8494
+ queryOptions.response_format = { type: 'json_object' };
8498
8495
  }
8499
8496
  // Temperature
8500
8497
  if (options.temperature !== undefined) {
@@ -8518,11 +8515,16 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8518
8515
  queryOptions.tools = options.tools;
8519
8516
  }
8520
8517
  try {
8521
- const completion = await withRetry(() => openai.chat.completions.create(queryOptions), {
8518
+ const completion = await withRetry(
8519
+ // Pass AbortSignal into the SDK request-options object so
8520
+ // caller-driven cancellation interrupts the in-flight HTTP
8521
+ // request immediately. See LLMOptions.signal JSDoc for context.
8522
+ () => openai.chat.completions.create(queryOptions, options.signal ? { signal: options.signal } : undefined), {
8522
8523
  maxRetries: 3,
8523
8524
  baseDelayMs: 2000,
8524
8525
  maxDelayMs: 30000,
8525
8526
  retryableErrors: isRetryableError,
8527
+ signal: options.signal,
8526
8528
  }, `${providerConfig.name}:${model}`);
8527
8529
  const promptTokens = completion.usage?.prompt_tokens || 0;
8528
8530
  const completionTokens = completion.usage?.completion_tokens || 0;
@@ -8552,7 +8554,7 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8552
8554
  };
8553
8555
  }
8554
8556
  // Handle regular responses
8555
- const textContent = completion.choices[0]?.message?.content || "";
8557
+ const textContent = completion.choices[0]?.message?.content || '';
8556
8558
  const parsedResponse = await parseResponse(textContent, responseFormat);
8557
8559
  if (parsedResponse === null) {
8558
8560
  throw new Error(`Failed to parse ${providerConfig.name} response`);
@@ -8844,9 +8846,7 @@ const isRetryableDeepseekError = (error) => {
8844
8846
  if (error instanceof Error) {
8845
8847
  const message = error.message;
8846
8848
  // Retry only on rate limits (429)
8847
- if (message.includes("429") ||
8848
- message.includes("rate limit") ||
8849
- message.includes("Rate limit")) {
8849
+ if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
8850
8850
  return true;
8851
8851
  }
8852
8852
  }
@@ -8856,7 +8856,7 @@ const isRetryableDeepseekError = (error) => {
8856
8856
  * Default options for Deepseek API calls
8857
8857
  */
8858
8858
  const DEFAULT_DEEPSEEK_OPTIONS = {
8859
- defaultModel: "deepseek-chat",
8859
+ defaultModel: 'deepseek-chat',
8860
8860
  };
8861
8861
  /**
8862
8862
  * Checks if the given model is a supported Deepseek model
@@ -8868,7 +8868,7 @@ const isSupportedDeepseekModel = (model) => {
8868
8868
  return false;
8869
8869
  }
8870
8870
  const capabilities = getModelCapabilities(model);
8871
- return capabilities.provider === "deepseek";
8871
+ return capabilities.provider === 'deepseek';
8872
8872
  };
8873
8873
  /**
8874
8874
  * Checks if the given Deepseek model supports JSON output format
@@ -8901,19 +8901,17 @@ const supportsToolCalling = (model) => {
8901
8901
  */
8902
8902
  function getDeepseekResponseFormatOption(responseFormat, model) {
8903
8903
  // Check if the model supports JSON output
8904
- if (responseFormat !== "text" && !supportsJsonOutput(model)) {
8904
+ if (responseFormat !== 'text' && !supportsJsonOutput(model)) {
8905
8905
  getLumicLogger().warn(`Model ${model} does not support JSON output. Using text format instead.`);
8906
- return { type: "text" };
8906
+ return { type: 'text' };
8907
8907
  }
8908
- if (responseFormat === "text") {
8909
- return { type: "text" };
8908
+ if (responseFormat === 'text') {
8909
+ return { type: 'text' };
8910
8910
  }
8911
- if (responseFormat === "json" ||
8912
- (typeof responseFormat === "object" &&
8913
- responseFormat.type === "json_schema")) {
8914
- return { type: "json_object" };
8911
+ if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
8912
+ return { type: 'json_object' };
8915
8913
  }
8916
- return { type: "text" };
8914
+ return { type: 'text' };
8917
8915
  }
8918
8916
  /**
8919
8917
  * Creates a completion using the Deepseek API
@@ -8932,26 +8930,24 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8932
8930
  throw new Error(`Unsupported Deepseek model: ${normalizedModel}. Please use 'deepseek-chat' or 'deepseek-reasoner'.`);
8933
8931
  }
8934
8932
  // Check if tools are requested with a model that doesn't support them
8935
- if (options.tools &&
8936
- options.tools.length > 0 &&
8937
- !supportsToolCalling(normalizedModel)) {
8933
+ if (options.tools && options.tools.length > 0 && !supportsToolCalling(normalizedModel)) {
8938
8934
  throw new Error(`Model ${normalizedModel} does not support tool calling.`);
8939
8935
  }
8940
8936
  const apiKey = options.apiKey || getSecrets().deepseek.apiKey;
8941
8937
  if (!apiKey) {
8942
- throw new Error("Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set");
8938
+ throw new Error('Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set');
8943
8939
  }
8944
8940
  // Initialize OpenAI client with Deepseek API URL
8945
8941
  const openai = new OpenAI({
8946
8942
  apiKey,
8947
- baseURL: "https://api.deepseek.com",
8943
+ baseURL: 'https://api.deepseek.com',
8948
8944
  timeout: options.timeout ?? LLM_TIMEOUT_MS,
8949
8945
  });
8950
8946
  const messages = [];
8951
8947
  // Add system message with developer prompt if present
8952
8948
  if (options.developerPrompt) {
8953
8949
  messages.push({
8954
- role: "system",
8950
+ role: 'system',
8955
8951
  content: options.developerPrompt,
8956
8952
  });
8957
8953
  }
@@ -8960,35 +8956,33 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8960
8956
  messages.push(...options.context);
8961
8957
  }
8962
8958
  // Add user content
8963
- if (typeof content === "string") {
8959
+ if (typeof content === 'string') {
8964
8960
  messages.push({
8965
- role: "user",
8961
+ role: 'user',
8966
8962
  content,
8967
8963
  });
8968
8964
  }
8969
8965
  else {
8970
8966
  messages.push({
8971
- role: "user",
8967
+ role: 'user',
8972
8968
  content,
8973
8969
  });
8974
8970
  }
8975
8971
  // If JSON response format, include a hint in the system prompt
8976
- if ((responseFormat === "json" ||
8977
- (typeof responseFormat === "object" &&
8978
- responseFormat.type === "json_schema")) &&
8979
- supportsJsonOutput(normalizedModel)) {
8972
+ if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema'))
8973
+ && supportsJsonOutput(normalizedModel)) {
8980
8974
  // If there's no system message yet, add one
8981
- if (!messages.some((m) => m.role === "system")) {
8975
+ if (!messages.some(m => m.role === 'system')) {
8982
8976
  messages.unshift({
8983
- role: "system",
8984
- content: "Please respond in valid JSON format.",
8977
+ role: 'system',
8978
+ content: 'Please respond in valid JSON format.',
8985
8979
  });
8986
8980
  }
8987
8981
  else {
8988
8982
  // Append to existing system message
8989
- const systemMsgIndex = messages.findIndex((m) => m.role === "system");
8983
+ const systemMsgIndex = messages.findIndex(m => m.role === 'system');
8990
8984
  const systemMsg = messages[systemMsgIndex];
8991
- if (typeof systemMsg.content === "string") {
8985
+ if (typeof systemMsg.content === 'string') {
8992
8986
  messages[systemMsgIndex] = {
8993
8987
  ...systemMsg,
8994
8988
  content: `${systemMsg.content} Please respond in valid JSON format.`,
@@ -9029,7 +9023,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
9029
9023
  0;
9030
9024
  return {
9031
9025
  id: completion.id,
9032
- content: completion.choices[0]?.message?.content || "",
9026
+ content: completion.choices[0]?.message?.content || '',
9033
9027
  tool_calls: completion.choices[0]?.message?.tool_calls,
9034
9028
  usage: {
9035
9029
  prompt_tokens: completion.usage?.prompt_tokens ?? 0,
@@ -9038,7 +9032,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
9038
9032
  cached_tokens: cachedTokens,
9039
9033
  },
9040
9034
  system_fingerprint: completion.system_fingerprint,
9041
- provider: "deepseek",
9035
+ provider: 'deepseek',
9042
9036
  model: normalizedModel,
9043
9037
  };
9044
9038
  }
@@ -9055,7 +9049,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
9055
9049
  * @param options Configuration options including model ('deepseek-chat' or 'deepseek-reasoner'), tools, and apiKey.
9056
9050
  * @return A promise that resolves to the response from the Deepseek API.
9057
9051
  */
9058
- const makeDeepseekCall = async (content, responseFormat = "json", options = {}) => {
9052
+ const makeDeepseekCall = async (content, responseFormat = 'json', options = {}) => {
9059
9053
  // Set default model if not provided
9060
9054
  const mergedOptions = {
9061
9055
  ...options,
@@ -9065,17 +9059,17 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9065
9059
  }
9066
9060
  const modelName = normalizeModelName(mergedOptions.model);
9067
9061
  // Check if the requested response format is compatible with the model
9068
- if (responseFormat !== "text" && !supportsJsonOutput(modelName)) {
9062
+ if (responseFormat !== 'text' && !supportsJsonOutput(modelName)) {
9069
9063
  getLumicLogger().warn(`Model ${modelName} does not support JSON output. Will return error in the response.`);
9070
9064
  return {
9071
9065
  response: {
9072
- error: `Model ${modelName} does not support JSON output format.`,
9066
+ error: `Model ${modelName} does not support JSON output format.`
9073
9067
  },
9074
9068
  usage: {
9075
9069
  prompt_tokens: 0,
9076
9070
  completion_tokens: 0,
9077
9071
  reasoning_tokens: 0,
9078
- provider: "deepseek",
9072
+ provider: 'deepseek',
9079
9073
  model: modelName,
9080
9074
  cached_tokens: 0,
9081
9075
  cost: 0,
@@ -9084,19 +9078,17 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9084
9078
  };
9085
9079
  }
9086
9080
  // Check if tools are requested with a model that doesn't support them
9087
- if (mergedOptions.tools &&
9088
- mergedOptions.tools.length > 0 &&
9089
- !supportsToolCalling(modelName)) {
9081
+ if (mergedOptions.tools && mergedOptions.tools.length > 0 && !supportsToolCalling(modelName)) {
9090
9082
  getLumicLogger().warn(`Model ${modelName} does not support tool calling. Will return error in the response.`);
9091
9083
  return {
9092
9084
  response: {
9093
- error: `Model ${modelName} does not support tool calling.`,
9085
+ error: `Model ${modelName} does not support tool calling.`
9094
9086
  },
9095
9087
  usage: {
9096
9088
  prompt_tokens: 0,
9097
9089
  completion_tokens: 0,
9098
9090
  reasoning_tokens: 0,
9099
- provider: "deepseek",
9091
+ provider: 'deepseek',
9100
9092
  model: modelName,
9101
9093
  cached_tokens: 0,
9102
9094
  cost: 0,
@@ -9108,7 +9100,7 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9108
9100
  const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
9109
9101
  // Track cost in the global cost tracker. Pass cached tokens through so the
9110
9102
  // discounted cached-input pricing tier is applied.
9111
- getLLMCostTracker().trackUsage("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
9103
+ getLLMCostTracker().trackUsage('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
9112
9104
  // Handle tool calls similarly to OpenAI
9113
9105
  if (completion.tool_calls && completion.tool_calls.length > 0) {
9114
9106
  const toolCallResponse = {
@@ -9124,10 +9116,10 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9124
9116
  prompt_tokens: completion.usage.prompt_tokens,
9125
9117
  completion_tokens: completion.usage.completion_tokens,
9126
9118
  reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
9127
- provider: "deepseek",
9119
+ provider: 'deepseek',
9128
9120
  model: completion.model,
9129
9121
  cached_tokens: completion.usage.cached_tokens,
9130
- cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9122
+ cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9131
9123
  },
9132
9124
  tool_calls: completion.tool_calls,
9133
9125
  };
@@ -9135,7 +9127,7 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9135
9127
  // Handle regular responses
9136
9128
  const parsedResponse = await parseResponse(completion.content, responseFormat);
9137
9129
  if (parsedResponse === null) {
9138
- throw new Error("Failed to parse Deepseek response");
9130
+ throw new Error('Failed to parse Deepseek response');
9139
9131
  }
9140
9132
  return {
9141
9133
  response: parsedResponse,
@@ -9143,10 +9135,10 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9143
9135
  prompt_tokens: completion.usage.prompt_tokens,
9144
9136
  completion_tokens: completion.usage.completion_tokens,
9145
9137
  reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
9146
- provider: "deepseek",
9138
+ provider: 'deepseek',
9147
9139
  model: completion.model,
9148
9140
  cached_tokens: completion.usage.cached_tokens,
9149
- cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9141
+ cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9150
9142
  },
9151
9143
  tool_calls: completion.tool_calls,
9152
9144
  };
@@ -9156,13 +9148,13 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9156
9148
  getLumicLogger().error(`Error in Deepseek API call: ${sanitizeError(error)}`);
9157
9149
  return {
9158
9150
  response: {
9159
- error: sanitizeError(error),
9151
+ error: sanitizeError(error)
9160
9152
  },
9161
9153
  usage: {
9162
9154
  prompt_tokens: 0,
9163
9155
  completion_tokens: 0,
9164
9156
  reasoning_tokens: 0,
9165
- provider: "deepseek",
9157
+ provider: 'deepseek',
9166
9158
  model: modelName,
9167
9159
  cached_tokens: 0,
9168
9160
  cost: 0,
@@ -9271,15 +9263,15 @@ const generateCacheKey = (auth, envVarsCheck = null) => {
9271
9263
  if (auth) {
9272
9264
  return `${auth.AWS_ACCESS_KEY_ID}:${auth.AWS_SECRET_ACCESS_KEY}:${auth.AWS_REGION}`;
9273
9265
  }
9274
- if (envVarsCheck?.source === "lambda") {
9266
+ if (envVarsCheck?.source === 'lambda') {
9275
9267
  // In Lambda, use function name as cache key since it uses IAM role
9276
9268
  const secrets = getSecrets();
9277
- return `lambda:${secrets.aws.lambdaFunctionName || "default"}`;
9269
+ return `lambda:${secrets.aws.lambdaFunctionName || 'default'}`;
9278
9270
  }
9279
9271
  if (envVarsCheck?.vars) {
9280
9272
  return `${envVarsCheck.vars.accessKeyId}:${envVarsCheck.vars.secretAccessKey}:${envVarsCheck.vars.region}`;
9281
9273
  }
9282
- return "default";
9274
+ return 'default';
9283
9275
  };
9284
9276
  const validateEnvironmentVars = () => {
9285
9277
  const secrets = getSecrets();
@@ -9292,37 +9284,37 @@ const validateEnvironmentVars = () => {
9292
9284
  // We're in a Lambda environment
9293
9285
  return {
9294
9286
  isValid: true,
9295
- source: "lambda",
9296
- vars: { accessKeyId: "", secretAccessKey: "", region: "" }, // No need for explicit credentials in Lambda
9287
+ source: 'lambda',
9288
+ vars: { accessKeyId: '', secretAccessKey: '', region: '' } // No need for explicit credentials in Lambda
9297
9289
  };
9298
9290
  }
9299
9291
  else if (areVarsComplete(secrets.aws.myAccessKeyId, secrets.aws.mySecretAccessKey, secrets.aws.myRegion)) {
9300
9292
  return {
9301
9293
  isValid: true,
9302
- source: "my_prefix",
9294
+ source: 'my_prefix',
9303
9295
  vars: {
9304
- accessKeyId: secrets.aws.myAccessKeyId ?? "",
9305
- secretAccessKey: secrets.aws.mySecretAccessKey ?? "",
9306
- region: secrets.aws.myRegion ?? "",
9307
- },
9296
+ accessKeyId: secrets.aws.myAccessKeyId,
9297
+ secretAccessKey: secrets.aws.mySecretAccessKey,
9298
+ region: secrets.aws.myRegion
9299
+ }
9308
9300
  };
9309
9301
  }
9310
9302
  else if (areVarsComplete(secrets.aws.accessKeyId, secrets.aws.secretAccessKey, secrets.aws.region)) {
9311
9303
  return {
9312
9304
  isValid: true,
9313
- source: "standard",
9305
+ source: 'standard',
9314
9306
  vars: {
9315
- accessKeyId: secrets.aws.accessKeyId ?? "",
9316
- secretAccessKey: secrets.aws.secretAccessKey ?? "",
9317
- region: secrets.aws.region ?? "",
9318
- },
9307
+ accessKeyId: secrets.aws.accessKeyId,
9308
+ secretAccessKey: secrets.aws.secretAccessKey,
9309
+ region: secrets.aws.region
9310
+ }
9319
9311
  };
9320
9312
  }
9321
9313
  // If we get here, no complete set of variables was found
9322
9314
  return {
9323
9315
  isValid: false,
9324
9316
  source: null,
9325
- missingVars: [],
9317
+ missingVars: []
9326
9318
  };
9327
9319
  };
9328
9320
  const initialiseAWSClient = (ClientClass, auth = null) => {
@@ -9331,52 +9323,48 @@ const initialiseAWSClient = (ClientClass, auth = null) => {
9331
9323
  const requestTimeout = ClientClass === S3Client ? AWS_S3_TIMEOUT_MS : AWS_LAMBDA_TIMEOUT_MS;
9332
9324
  // Case 1: Explicit auth provided
9333
9325
  if (auth) {
9334
- if (typeof auth !== "object" || auth === null) {
9335
- throw new Error("Auth parameter must be an object");
9326
+ if (typeof auth !== 'object' || auth === null) {
9327
+ throw new Error('Auth parameter must be an object');
9336
9328
  }
9337
- const requiredAuthFields = [
9338
- "AWS_ACCESS_KEY_ID",
9339
- "AWS_SECRET_ACCESS_KEY",
9340
- "AWS_REGION",
9341
- ];
9342
- const missingFields = requiredAuthFields.filter((field) => !auth[field]);
9329
+ const requiredAuthFields = ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION'];
9330
+ const missingFields = requiredAuthFields.filter(field => !auth[field]);
9343
9331
  if (missingFields.length > 0) {
9344
- throw new Error(`Auth object is missing required fields: ${missingFields.join(", ")}`);
9332
+ throw new Error(`Auth object is missing required fields: ${missingFields.join(', ')}`);
9345
9333
  }
9346
9334
  return new ClientClass({
9347
9335
  region: auth.AWS_REGION,
9348
9336
  credentials: {
9349
9337
  accessKeyId: auth.AWS_ACCESS_KEY_ID,
9350
- secretAccessKey: auth.AWS_SECRET_ACCESS_KEY,
9338
+ secretAccessKey: auth.AWS_SECRET_ACCESS_KEY
9351
9339
  },
9352
9340
  requestHandler: {
9353
- requestTimeout,
9354
- },
9341
+ requestTimeout
9342
+ }
9355
9343
  });
9356
9344
  }
9357
9345
  // Case 2: Check environment variables
9358
9346
  const envVarsCheck = validateEnvironmentVars();
9359
9347
  if (!envVarsCheck.isValid) {
9360
- throw new Error("No valid AWS credentials found in environment variables");
9348
+ throw new Error('No valid AWS credentials found in environment variables');
9361
9349
  }
9362
9350
  // Case 2a: Lambda execution environment
9363
- if (envVarsCheck.source === "lambda") {
9351
+ if (envVarsCheck.source === 'lambda') {
9364
9352
  return new ClientClass({
9365
9353
  requestHandler: {
9366
- requestTimeout,
9367
- },
9354
+ requestTimeout
9355
+ }
9368
9356
  }); // AWS SDK will automatically use Lambda role credentials
9369
9357
  }
9370
9358
  // Case 2b: MY_ prefixed vars or standard AWS vars
9371
9359
  return new ClientClass({
9372
- region: envVarsCheck.vars?.region ?? "",
9360
+ region: envVarsCheck.vars.region,
9373
9361
  credentials: {
9374
- accessKeyId: envVarsCheck.vars?.accessKeyId ?? "",
9375
- secretAccessKey: envVarsCheck.vars?.secretAccessKey ?? "",
9362
+ accessKeyId: envVarsCheck.vars.accessKeyId,
9363
+ secretAccessKey: envVarsCheck.vars.secretAccessKey
9376
9364
  },
9377
9365
  requestHandler: {
9378
- requestTimeout,
9379
- },
9366
+ requestTimeout
9367
+ }
9380
9368
  });
9381
9369
  }
9382
9370
  catch (error) {
@@ -9389,9 +9377,7 @@ const initialiseS3Client = (auth = null) => {
9389
9377
  const envVarsCheck = auth ? null : validateEnvironmentVars();
9390
9378
  const cacheKey = generateCacheKey(auth, envVarsCheck);
9391
9379
  if (s3ClientCache.has(cacheKey)) {
9392
- const cached = s3ClientCache.get(cacheKey);
9393
- if (cached)
9394
- return cached;
9380
+ return s3ClientCache.get(cacheKey);
9395
9381
  }
9396
9382
  // Create new client and cache it
9397
9383
  const client = initialiseAWSClient(S3Client, auth);
@@ -9403,9 +9389,7 @@ const initialiseLambdaClient = (auth = null) => {
9403
9389
  const envVarsCheck = auth ? null : validateEnvironmentVars();
9404
9390
  const cacheKey = generateCacheKey(auth, envVarsCheck);
9405
9391
  if (lambdaClientCache.has(cacheKey)) {
9406
- const cached = lambdaClientCache.get(cacheKey);
9407
- if (cached)
9408
- return cached;
9392
+ return lambdaClientCache.get(cacheKey);
9409
9393
  }
9410
9394
  // Create new client and cache it
9411
9395
  const client = initialiseAWSClient(Lambda, auth);
@@ -9905,17 +9889,15 @@ const isRetryableS3Error = (error) => {
9905
9889
  if (error instanceof Error) {
9906
9890
  const message = error.message;
9907
9891
  // Retry on throttling
9908
- if (message.includes("SlowDown") || message.includes("RequestTimeout")) {
9892
+ if (message.includes('SlowDown') || message.includes('RequestTimeout')) {
9909
9893
  return true;
9910
9894
  }
9911
9895
  // Retry on 5xx server errors
9912
- if (message.includes("InternalError") ||
9913
- message.includes("ServiceUnavailable")) {
9896
+ if (message.includes('InternalError') || message.includes('ServiceUnavailable')) {
9914
9897
  return true;
9915
9898
  }
9916
9899
  // Retry on connection errors
9917
- if (message.includes("RequestTimeTooSkewed") ||
9918
- message.includes("NetworkingError")) {
9900
+ if (message.includes('RequestTimeTooSkewed') || message.includes('NetworkingError')) {
9919
9901
  return true;
9920
9902
  }
9921
9903
  }
@@ -9929,11 +9911,10 @@ const generateRandomHash = (length) => {
9929
9911
  };
9930
9912
  function generateDateTimeStamp() {
9931
9913
  const now = new Date();
9932
- return now
9933
- .toISOString()
9934
- .replace(/T/, "-")
9935
- .replace(/\..+/, "")
9936
- .replace(/:/g, "-");
9914
+ return now.toISOString()
9915
+ .replace(/T/, '-')
9916
+ .replace(/\..+/, '')
9917
+ .replace(/:/g, '-');
9937
9918
  }
9938
9919
  const isValidBucketName = (name) => {
9939
9920
  const regex = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
@@ -9964,7 +9945,7 @@ async function uploadFile(s3Client, bucketName, filePath, destinationPath, isPub
9964
9945
  Bucket: bucketName,
9965
9946
  Key: s3Key,
9966
9947
  Body: fileContent,
9967
- ACL: isPublic ? "public-read" : undefined,
9948
+ ACL: isPublic ? 'public-read' : undefined
9968
9949
  });
9969
9950
  await withRetry(() => s3Client.send(command), {
9970
9951
  maxRetries: 3,
@@ -9984,7 +9965,7 @@ async function uploadDirectory(s3Client, bucketName, dirPath, destinationPath, i
9984
9965
  Bucket: bucketName,
9985
9966
  Key: s3Key,
9986
9967
  Body: fileContent,
9987
- ACL: isPublic ? "public-read" : undefined,
9968
+ ACL: isPublic ? 'public-read' : undefined
9988
9969
  });
9989
9970
  await withRetry(() => s3Client.send(command), {
9990
9971
  maxRetries: 3,
@@ -10000,7 +9981,7 @@ async function zipDirectory(sourceDir, outPath) {
10000
9981
  const zip = new AdmZip();
10001
9982
  let fileCount = 0;
10002
9983
  try {
10003
- async function addFilesToZip(currentPath, relativePath = "") {
9984
+ async function addFilesToZip(currentPath, relativePath = '') {
10004
9985
  const files = await fs$1.readdir(currentPath, { withFileTypes: true });
10005
9986
  for (const file of files) {
10006
9987
  const filePath = path__default.join(currentPath, file.name);
@@ -10043,33 +10024,27 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
10043
10024
  Prefix: s3Path,
10044
10025
  ContinuationToken: continuationToken,
10045
10026
  });
10046
- const listResponse = (await withRetry(() => s3Client.send(listCommand), {
10027
+ const listResponse = await withRetry(() => s3Client.send(listCommand), {
10047
10028
  maxRetries: 3,
10048
10029
  baseDelayMs: 1000,
10049
10030
  maxDelayMs: 15000,
10050
10031
  retryableErrors: isRetryableS3Error,
10051
- }, `S3:ListObjects:${bucketName}`));
10032
+ }, `S3:ListObjects:${bucketName}`);
10052
10033
  const objects = listResponse.Contents || [];
10053
10034
  if (objects.length === 0) {
10054
10035
  return { fileCount: 0, folderCount: 0, totalSize: 0, fileList: [] };
10055
10036
  }
10056
- if (objects.length === 1 && objects[0].Key?.endsWith(".zip")) {
10037
+ if (objects.length === 1 && objects[0].Key?.endsWith('.zip')) {
10057
10038
  // It's a zip file, download and extract it
10058
10039
  const zipPath = path__default.join(localPath, path__default.basename(objects[0].Key));
10059
10040
  await downloadFile(s3Client, bucketName, objects[0].Key, zipPath);
10060
- const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList, } = await unzipFile(zipPath, localPath);
10041
+ const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList } = await unzipFile(zipPath, localPath);
10061
10042
  await fs$1.unlink(zipPath);
10062
- return {
10063
- fileCount: totalFiles,
10064
- folderCount: totalFolders,
10065
- totalSize: extractedSize,
10066
- fileList: extractedFileList,
10067
- };
10043
+ return { fileCount: totalFiles, folderCount: totalFolders, totalSize: extractedSize, fileList: extractedFileList };
10068
10044
  }
10069
10045
  else {
10070
10046
  // Non-zip files. Download files in batches.
10071
- for (let i = 0; i < objects.length; i += 1000) {
10072
- // AWS allows up to 1000 per request
10047
+ for (let i = 0; i < objects.length; i += 1000) { // AWS allows up to 1000 per request
10073
10048
  const batch = objects.slice(i, i + 1000);
10074
10049
  await Promise.all(batch.map(async (obj) => {
10075
10050
  if (!obj.Key)
@@ -10085,9 +10060,7 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
10085
10060
  }
10086
10061
  // Count folders
10087
10062
  const relativePath = path__default.relative(localPath, path__default.dirname(localFilePath));
10088
- if (relativePath &&
10089
- !relativePath.startsWith("..") &&
10090
- relativePath !== ".") {
10063
+ if (relativePath && !relativePath.startsWith('..') && relativePath !== '.') {
10091
10064
  folderCount++;
10092
10065
  }
10093
10066
  }));
@@ -10108,7 +10081,7 @@ async function downloadFile(s3Client, bucketName, s3Key, localFilePath) {
10108
10081
  await withRetry(async () => {
10109
10082
  const { Body } = await s3Client.send(getCommand);
10110
10083
  if (!Body)
10111
- throw new Error("No body returned from S3");
10084
+ throw new Error('No body returned from S3');
10112
10085
  const writeStream = createWriteStream(localFilePath);
10113
10086
  await pipeline(Body, writeStream);
10114
10087
  }, {
@@ -10164,19 +10137,17 @@ async function emptyBucket(s3Client, bucketName) {
10164
10137
  Bucket: bucketName,
10165
10138
  ContinuationToken: continuationToken,
10166
10139
  });
10167
- const response = (await withRetry(() => s3Client.send(listCommand), {
10140
+ const response = await withRetry(() => s3Client.send(listCommand), {
10168
10141
  maxRetries: 3,
10169
10142
  baseDelayMs: 1000,
10170
10143
  maxDelayMs: 15000,
10171
10144
  retryableErrors: isRetryableS3Error,
10172
- }, `S3:ListObjects:${bucketName}`));
10145
+ }, `S3:ListObjects:${bucketName}`);
10173
10146
  const { Contents, IsTruncated, NextContinuationToken } = response;
10174
10147
  if (Contents && Contents.length > 0) {
10175
10148
  const deleteCommand = new DeleteObjectsCommand({
10176
10149
  Bucket: bucketName,
10177
- Delete: {
10178
- Objects: Contents.filter((c) => c.Key).map((c) => ({ Key: c.Key })),
10179
- },
10150
+ Delete: { Objects: Contents.map(({ Key }) => ({ Key: Key })) },
10180
10151
  });
10181
10152
  await withRetry(() => s3Client.send(deleteCommand), {
10182
10153
  maxRetries: 3,
@@ -23084,11 +23055,11 @@ let poolConfig = DEFAULT_POOL_CONFIG;
23084
23055
  async function loadApolloModules() {
23085
23056
  if (typeof window === "undefined" || process.env.AWS_EXECUTION_ENV) {
23086
23057
  // Server-side (or Lambda): load the CommonJS‑based implementation.
23087
- return (await import('./apollo-client.server-CbagxkmK.js'));
23058
+ return (await import('./apollo-client.server-CXLiLmSz.js'));
23088
23059
  }
23089
23060
  else {
23090
23061
  // Client-side: load the ESM‑based implementation.
23091
- return (await import('./apollo-client.client-9wJcufhf.js'));
23062
+ return (await import('./apollo-client.client-DLW-p46Y.js'));
23092
23063
  }
23093
23064
  }
23094
23065
  /**
@@ -79115,7 +79086,7 @@ const RETRY_CONFIG = {
79115
79086
  BASE_DELAY: 2000, // Increased base delay to 2 seconds
79116
79087
  MAX_DELAY: 64000,
79117
79088
  MAX_RETRIES: 5,
79118
- MAX_RANDOM_DELAY: 1000,
79089
+ MAX_RANDOM_DELAY: 1000
79119
79090
  };
79120
79091
  /**
79121
79092
  * Determines if an error is related to Google Sheets API quotas or rate limits.
@@ -79141,10 +79112,10 @@ const RETRY_CONFIG = {
79141
79112
  */
79142
79113
  function isQuotaError(error) {
79143
79114
  const message = error.message.toLowerCase();
79144
- return (message.includes("quota exceeded") ||
79145
- message.includes("rate limit") ||
79146
- message.includes("429") ||
79147
- message.includes("too many requests"));
79115
+ return message.includes('quota exceeded') ||
79116
+ message.includes('rate limit') ||
79117
+ message.includes('429') ||
79118
+ message.includes('too many requests');
79148
79119
  }
79149
79120
  /**
79150
79121
  * Implements exponential backoff retry mechanism for Google Sheets API calls.
@@ -79203,14 +79174,14 @@ async function withExponentialBackoff(operation) {
79203
79174
  const exponentialDelay = Math.min(Math.pow(2, retries) * RETRY_CONFIG.BASE_DELAY + randomDelay, RETRY_CONFIG.MAX_DELAY);
79204
79175
  logIfDebug(`Quota/Rate limit exceeded. Error: ${error.message}`);
79205
79176
  logIfDebug(`Retrying in ${exponentialDelay}ms (attempt ${retries}/${RETRY_CONFIG.MAX_RETRIES})`);
79206
- await new Promise((resolve) => setTimeout(resolve, exponentialDelay));
79177
+ await new Promise(resolve => setTimeout(resolve, exponentialDelay));
79207
79178
  }
79208
79179
  }
79209
79180
  }
79210
79181
  function checkEnvVars() {
79211
79182
  const secrets = getSecrets();
79212
79183
  if (!secrets.googleSheets.clientEmail || !secrets.googleSheets.privateKey) {
79213
- throw new Error("GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.");
79184
+ throw new Error('GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.');
79214
79185
  }
79215
79186
  }
79216
79187
  /**
@@ -79225,16 +79196,16 @@ async function getAuthClient() {
79225
79196
  const secrets = getSecrets();
79226
79197
  const credentials = {
79227
79198
  client_email: secrets.googleSheets.clientEmail,
79228
- private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, "\n"),
79199
+ private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, '\n'),
79229
79200
  };
79230
79201
  const auth = new GoogleAuth({
79231
79202
  credentials,
79232
- scopes: ["https://www.googleapis.com/auth/spreadsheets"],
79203
+ scopes: ['https://www.googleapis.com/auth/spreadsheets'],
79233
79204
  });
79234
79205
  return auth.getClient();
79235
79206
  }
79236
79207
  catch (error) {
79237
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79208
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79238
79209
  logIfDebug(`Error initializing Google Sheets auth: ${errorMessage}`);
79239
79210
  throw new Error(`Failed to initialize Google Sheets auth: ${errorMessage}`);
79240
79211
  }
@@ -79246,7 +79217,7 @@ async function getSheetsClient() {
79246
79217
  const auth = await getAuthClient();
79247
79218
  return new buildExports.sheets_v4.Sheets({
79248
79219
  auth,
79249
- timeout: GOOGLE_SHEETS_TIMEOUT_MS,
79220
+ timeout: GOOGLE_SHEETS_TIMEOUT_MS
79250
79221
  });
79251
79222
  }
79252
79223
  /**
@@ -79279,7 +79250,7 @@ function escapeSheetName(sheetName) {
79279
79250
  * @param startColumn Optional starting column (defaults to 'A')
79280
79251
  * @returns Promise resolving to response with success status and row details
79281
79252
  */
79282
- async function addRow(config, values, startColumn = "A") {
79253
+ async function addRow(config, values, startColumn = 'A') {
79283
79254
  try {
79284
79255
  const sheets = await getSheetsClient();
79285
79256
  const escapedSheetName = escapeSheetName(config.sheetName);
@@ -79287,7 +79258,7 @@ async function addRow(config, values, startColumn = "A") {
79287
79258
  const response = await withExponentialBackoff(() => sheets.spreadsheets.values.append({
79288
79259
  spreadsheetId: config.spreadsheetId,
79289
79260
  range,
79290
- valueInputOption: "USER_ENTERED",
79261
+ valueInputOption: 'USER_ENTERED',
79291
79262
  requestBody: {
79292
79263
  values: [values],
79293
79264
  },
@@ -79295,9 +79266,9 @@ async function addRow(config, values, startColumn = "A") {
79295
79266
  //logIfDebug(`Added row to sheet ${config.sheetName}: ${values.join(', ')}`);
79296
79267
  const rowNumber = response.data.updates?.updatedRange
79297
79268
  ? parseInt(response.data.updates.updatedRange
79298
- .split("!")[1]
79299
- .split(":")[0]
79300
- .replace(/[^0-9]/g, ""))
79269
+ .split('!')[1]
79270
+ .split(':')[0]
79271
+ .replace(/[^0-9]/g, ''))
79301
79272
  : 0;
79302
79273
  return {
79303
79274
  success: true,
@@ -79308,7 +79279,7 @@ async function addRow(config, values, startColumn = "A") {
79308
79279
  };
79309
79280
  }
79310
79281
  catch (error) {
79311
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79282
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79312
79283
  logIfDebug(`Error adding row to sheet: ${errorMessage}`);
79313
79284
  return {
79314
79285
  success: false,
@@ -79350,7 +79321,7 @@ async function addSheet(spreadsheetId, sheetTitle) {
79350
79321
  };
79351
79322
  }
79352
79323
  catch (error) {
79353
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79324
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79354
79325
  logIfDebug(`Error adding sheet: ${errorMessage}`);
79355
79326
  return {
79356
79327
  success: false,
@@ -79374,7 +79345,7 @@ async function writeCell(config, cell, value) {
79374
79345
  await withExponentialBackoff(() => sheets.spreadsheets.values.update({
79375
79346
  spreadsheetId: config.spreadsheetId,
79376
79347
  range,
79377
- valueInputOption: "USER_ENTERED",
79348
+ valueInputOption: 'USER_ENTERED',
79378
79349
  requestBody: {
79379
79350
  values: [[value]],
79380
79351
  },
@@ -79388,7 +79359,7 @@ async function writeCell(config, cell, value) {
79388
79359
  };
79389
79360
  }
79390
79361
  catch (error) {
79391
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79362
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79392
79363
  logIfDebug(`Error writing to cell: ${errorMessage}`);
79393
79364
  return {
79394
79365
  success: false,
@@ -79412,11 +79383,8 @@ async function getCell(config, cell) {
79412
79383
  spreadsheetId: config.spreadsheetId,
79413
79384
  range,
79414
79385
  }));
79415
- const rawValue = response.data.values?.[0]?.[0];
79416
- const value = rawValue === undefined || rawValue === null
79417
- ? null
79418
- : rawValue;
79419
- logIfDebug(`Read value from cell ${range}: ${String(value)}`);
79386
+ const value = response.data.values?.[0]?.[0];
79387
+ logIfDebug(`Read value from cell ${range}: ${value}`);
79420
79388
  return {
79421
79389
  success: true,
79422
79390
  data: {
@@ -79425,7 +79393,7 @@ async function getCell(config, cell) {
79425
79393
  };
79426
79394
  }
79427
79395
  catch (error) {
79428
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79396
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79429
79397
  logIfDebug(`Error reading from cell: ${errorMessage}`);
79430
79398
  return {
79431
79399
  success: false,
@@ -79458,11 +79426,10 @@ function convertA1ToRowCol(a1Notation) {
79458
79426
  * @returns Padded array of arrays
79459
79427
  */
79460
79428
  function padArrays(arrays) {
79461
- const maxLength = Math.max(...arrays.map((arr) => arr.length));
79462
- return arrays.map((arr) => {
79429
+ const maxLength = Math.max(...arrays.map(arr => arr.length));
79430
+ return arrays.map(arr => {
79463
79431
  if (arr.length < maxLength) {
79464
- const padding = Array(maxLength - arr.length).fill("");
79465
- return [...arr, ...padding];
79432
+ return [...arr, ...Array(maxLength - arr.length).fill('')];
79466
79433
  }
79467
79434
  return arr;
79468
79435
  });
@@ -79474,10 +79441,10 @@ function padArrays(arrays) {
79474
79441
  * @param startCell Optional starting cell in A1 notation (defaults to 'A1')
79475
79442
  * @returns Promise resolving to response with success status and update details
79476
79443
  */
79477
- async function writeArray(config, data, startCell = "A1") {
79444
+ async function writeArray(config, data, startCell = 'A1') {
79478
79445
  try {
79479
79446
  if (!data.length) {
79480
- throw new Error("Data array is empty");
79447
+ throw new Error('Data array is empty');
79481
79448
  }
79482
79449
  const sheets = await getSheetsClient();
79483
79450
  const { row: startRow, column: startCol } = convertA1ToRowCol(startCell);
@@ -79493,7 +79460,7 @@ async function writeArray(config, data, startCell = "A1") {
79493
79460
  const response = await withExponentialBackoff(() => sheets.spreadsheets.values.update({
79494
79461
  spreadsheetId: config.spreadsheetId,
79495
79462
  range,
79496
- valueInputOption: "USER_ENTERED",
79463
+ valueInputOption: 'USER_ENTERED',
79497
79464
  requestBody: {
79498
79465
  values: paddedData,
79499
79466
  },
@@ -79509,7 +79476,7 @@ async function writeArray(config, data, startCell = "A1") {
79509
79476
  };
79510
79477
  }
79511
79478
  catch (error) {
79512
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79479
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79513
79480
  logIfDebug(`Error writing array to sheet: ${errorMessage}`);
79514
79481
  return {
79515
79482
  success: false,
@@ -81616,4 +81583,4 @@ const lumic = {
81616
81583
  };
81617
81584
 
81618
81585
  export { GraphQLInterfaceType as $, print as A, getNamedType as B, isInputType as C, isRequiredArgument as D, isNamedType as E, GraphQLError as F, GraphQLNonNull as G, isOutputType as H, isRequiredInputField as I, isCompositeType as J, Kind as K, getNullableType as L, getEnterLeaveForKind as M, isNode as N, OperationTypeNode as O, didYouMean as P, naturalCompare as Q, suggestionList as R, specifiedScalarTypes as S, keyMap as T, isType as U, isNullableType as V, visit as W, visitInParallel as X, keyValMap as Y, assertObjectType as Z, GraphQLScalarType as _, isListType as a, validateGoogleSheetsRange as a$, GraphQLUnionType as a0, GraphQLInputObjectType as a1, assertNullableType as a2, assertInterfaceType as a3, mapValue as a4, isSpecifiedScalarType as a5, isPrintableAsBlockString as a6, printBlockString as a7, BREAK as a8, GRAPHQL_MAX_INT as a9, printSourceLocation as aA, resolveObjMapThunk as aB, resolveReadonlyArrayThunk as aC, valueFromASTUntyped as aD, version$4 as aE, versionInfo as aF, getAugmentedNamespace as aG, isDigit$1 as aH, isNameStart as aI, dedentBlockStringLines as aJ, isNameContinue as aK, setLumicLogger as aL, getLumicLogger as aM, sanitizeForLog as aN, sanitizeError as aO, sanitizeAWSAuth as aP, sanitizeObject as aQ, getSecrets as aR, resetSecrets as aS, requireSecret as aT, withRetry as aU, CircuitBreaker as aV, CircuitBreakerState as aW, CircuitBreakerOpenError as aX, DEFAULT_CIRCUIT_BREAKER_CONFIG as aY, validateSlackChannel as aZ, validateS3Key as a_, GRAPHQL_MIN_INT as aa, GraphQLFloat as ab, GraphQLInt as ac, Location as ad, Token as ae, assertAbstractType as af, assertCompositeType as ag, assertEnumType as ah, assertEnumValueName as ai, assertInputObjectType as aj, assertInputType as ak, assertLeafType as al, assertListType as am, assertNamedType as an, assertNonNullType as ao, assertOutputType as ap, assertScalarType as aq, assertType as ar, assertUnionType as as, assertWrappingType as at, formatError as au, getLocation as av, getVisitFn as aw, isWrappingType as ax, printError as ay, printLocation as az, isAbstractType as b, PDFError as b$, LLMCostTracker as b0, getLLMCostTracker as b1, setLLMCostTracker as b2, resetLLMCostTracker as b3, setMetricsCollector as b4, getMetricsCollector as b5, resetMetricsCollector as b6, withMetrics as b7, generateCorrelationId as b8, getCorrelationId as b9, openAIChatCompletionSchema as bA, openAIImageResponseSchema as bB, validateOpenAIChatCompletion as bC, safeValidateOpenAIChatCompletion as bD, perplexityResponseSchema as bE, validatePerplexityResponse as bF, safeValidatePerplexityResponse as bG, googleSheetsValueRangeSchema as bH, validateGoogleSheetsResponse as bI, safeValidateGoogleSheetsResponse as bJ, s3ListObjectsSchema as bK, s3GetObjectSchema as bL, lambdaInvokeResponseSchema as bM, validateS3ListObjects as bN, safeValidateS3ListObjects as bO, validateLambdaResponse as bP, safeValidateLambdaResponse as bQ, createValidator as bR, createSafeValidator as bS, LumicError as bT, SlackError as bU, LLMError as bV, AWSLambdaError as bW, AWSS3Error as bX, GoogleSheetsError as bY, PerplexityError as bZ, JsonParseError as b_, getCorrelationContext as ba, withCorrelationId as bb, getCorrelationHeaders as bc, TokenBucketRateLimiter as bd, RATE_LIMIT_PROFILES as be, getRateLimiter as bf, resetAllRateLimiters as bg, withRateLimit as bh, checkIntegrationHealth as bi, SUPPORTED_MODELS as bj, isValidModel as bk, getModelCapabilities as bl, getModelProvider as bm, MODEL_ALIASES as bn, OPENAI_COMPATIBLE_PROVIDERS as bo, PROVIDER_DEFAULT_MODELS as bp, LLM_DEFAULT_PROVIDER as bq, LLM_MINI_PROVIDER as br, LLM_NORMAL_PROVIDER as bs, LLM_ADVANCED_PROVIDER as bt, LLM_PROVIDER as bu, LLM_MODEL_MINI as bv, LLM_MODEL_NORMAL as bw, LLM_MODEL_ADVANCED as bx, makeAnthropicCall as by, makeOpenAICompatibleCall as bz, isInterfaceType as c, ZipError as c0, SLACK_TIMEOUT_MS as c1, PERPLEXITY_TIMEOUT_MS as c2, GOOGLE_SHEETS_TIMEOUT_MS as c3, LLM_TIMEOUT_MS as c4, AWS_LAMBDA_TIMEOUT_MS as c5, AWS_S3_TIMEOUT_MS as c6, OPENWEATHER_TIMEOUT_MS as c7, GENERIC_FETCH_TIMEOUT_MS as c8, isObjectType as d, assertName as e, devAssert as f, isObjectLike as g, defineArguments as h, isNonNullType as i, argsToArgsConfig as j, GraphQLBoolean as k, lumic as l, GraphQLString as m, instanceOf as n, inspect as o, isInputObjectType as p, isLeafType as q, isEnumType as r, GraphQLID as s, toObjMap as t, invariant as u, GraphQLObjectType as v, GraphQLEnumType as w, GraphQLList as x, isScalarType as y, isUnionType as z };
81619
- //# sourceMappingURL=index-EMQ1uJMh.js.map
81586
+ //# sourceMappingURL=index-Bvc61lYS.js.map