@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
@@ -571,19 +571,10 @@ function getModelProvider(model) {
571
571
  return SUPPORTED_MODELS[model].provider;
572
572
  }
573
573
  /**
574
- * Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars.
575
- *
576
- * OpenAI `advanced` was reverted from `gpt-5.5` back to `gpt-5.4` on 2026-05-30:
577
- * the heavy tool-call prompt in adaptic-engine's trading-decision path was
578
- * timing out even after the engine raised `LLM_CALL_TIMEOUT_MS` to 90s (5+
579
- * tickers per loop still exceeded the budget). `gpt-5.5`'s 1M-context window
580
- * is not required on that path — the prompt fits comfortably inside `gpt-5.4`'s
581
- * envelope and `gpt-5.4` returns inside the original 25-30s p95. `gpt-5.5`
582
- * remains registered in OPENAI_MODELS and reachable by explicit model id;
583
- * only the `advanced` tier default is rolled back.
574
+ * Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars
584
575
  */
585
576
  const PROVIDER_DEFAULT_MODELS = {
586
- openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.4' },
577
+ openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.5' },
587
578
  anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7' },
588
579
  deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro' },
589
580
  kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6' },
@@ -1537,13 +1528,11 @@ let openai;
1537
1528
  function initializeOpenAI(apiKey) {
1538
1529
  const key = apiKey || getSecrets().openai.apiKey;
1539
1530
  if (!key) {
1540
- throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
1531
+ throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
1541
1532
  }
1542
1533
  return new OpenAI({
1543
1534
  apiKey: key,
1544
- defaultHeaders: {
1545
- "User-Agent": "My Server-side Application (compatible; OpenAI API Client)",
1546
- },
1535
+ defaultHeaders: { 'User-Agent': 'My Server-side Application (compatible; OpenAI API Client)' },
1547
1536
  });
1548
1537
  }
1549
1538
  /**
@@ -1561,7 +1550,9 @@ function initializeOpenAI(apiKey) {
1561
1550
  */
1562
1551
  async function fixJsonWithAI(jsonStr, options) {
1563
1552
  // Backward compatibility: handle string apiKey as first positional param
1564
- const opts = typeof options === "string" ? { apiKey: options } : (options ?? {});
1553
+ const opts = typeof options === 'string'
1554
+ ? { apiKey: options }
1555
+ : (options ?? {});
1565
1556
  const apiKey = opts.apiKey;
1566
1557
  const maxDepth = opts.maxDepth ?? 2;
1567
1558
  const depth = opts._depth ?? 0;
@@ -1574,18 +1565,18 @@ async function fixJsonWithAI(jsonStr, options) {
1574
1565
  openai = initializeOpenAI(apiKey);
1575
1566
  }
1576
1567
  const completion = await openai.chat.completions.create({
1577
- model: "gpt-5-mini",
1568
+ model: 'gpt-5-mini',
1578
1569
  messages: [
1579
1570
  {
1580
- role: "system",
1581
- content: "You are a JSON fixer. Return only valid JSON without any additional text or explanation.",
1571
+ role: 'system',
1572
+ content: 'You are a JSON fixer. Return only valid JSON without any additional text or explanation.'
1582
1573
  },
1583
1574
  {
1584
- role: "user",
1585
- content: `Fix this broken JSON:\n${jsonStr}`,
1586
- },
1575
+ role: 'user',
1576
+ content: `Fix this broken JSON:\n${jsonStr}`
1577
+ }
1587
1578
  ],
1588
- response_format: { type: "json_object" },
1579
+ response_format: { type: 'json_object' }
1589
1580
  });
1590
1581
  const fixedJson = completion.choices[0]?.message?.content;
1591
1582
  if (fixedJson && isValidJson(fixedJson)) {
@@ -1599,32 +1590,23 @@ async function fixJsonWithAI(jsonStr, options) {
1599
1590
  _depth: depth + 1,
1600
1591
  });
1601
1592
  }
1602
- throw new JsonParseError("Failed to fix JSON with AI: returned invalid JSON");
1593
+ throw new JsonParseError('Failed to fix JSON with AI: returned invalid JSON');
1603
1594
  }
1604
1595
  catch (err) {
1605
1596
  // Re-throw JsonParseError as-is (e.g. max depth exceeded)
1606
1597
  if (err instanceof JsonParseError) {
1607
1598
  throw err;
1608
1599
  }
1609
- const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
1600
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
1610
1601
  throw new JsonParseError(`Error fixing JSON with AI: ${errorMessage}`);
1611
1602
  }
1612
1603
  }
1613
1604
 
1614
1605
  // llm-utils.ts
1615
1606
  function normalizeModelName(model) {
1616
- return model.replace(/_/g, "-").toLowerCase();
1617
- }
1618
- const CODE_BLOCK_TYPES = [
1619
- "javascript",
1620
- "js",
1621
- "graphql",
1622
- "json",
1623
- "typescript",
1624
- "python",
1625
- "markdown",
1626
- "yaml",
1627
- ];
1607
+ return model.replace(/_/g, '-').toLowerCase();
1608
+ }
1609
+ const CODE_BLOCK_TYPES = ['javascript', 'js', 'graphql', 'json', 'typescript', 'python', 'markdown', 'yaml'];
1628
1610
  /**
1629
1611
  * Tries to parse JSON from the given content string according to the specified
1630
1612
  * response format. If the response format is 'json' or a JSON schema object, it
@@ -1648,9 +1630,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1648
1630
  if (recursionDepth > maxRetries) {
1649
1631
  throw new Error(`Maximum recursion depth (${maxRetries}) exceeded while parsing JSON. AI fixing may have returned broken JSON.`);
1650
1632
  }
1651
- if (responseFormat === "json" ||
1652
- (typeof responseFormat === "object" &&
1653
- responseFormat?.type === "json_schema")) {
1633
+ if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat?.type === 'json_schema')) {
1654
1634
  let cleanedContent = content.trim();
1655
1635
  let detectedType = null;
1656
1636
  // Remove code block markers if present
@@ -1661,8 +1641,8 @@ async function parseResponse(content, responseFormat, options = {}) {
1661
1641
  }
1662
1642
  return false;
1663
1643
  });
1664
- if (startsWithCodeBlock && cleanedContent.endsWith("```")) {
1665
- const firstLineEndIndex = cleanedContent.indexOf("\n");
1644
+ if (startsWithCodeBlock && cleanedContent.endsWith('```')) {
1645
+ const firstLineEndIndex = cleanedContent.indexOf('\n');
1666
1646
  if (firstLineEndIndex !== -1) {
1667
1647
  cleanedContent = cleanedContent.slice(firstLineEndIndex + 1, -3).trim();
1668
1648
  getLumicLogger().info(`Code block for type ${detectedType} detected and removed. Cleaned content: ${cleanedContent}`);
@@ -1695,7 +1675,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1695
1675
  },
1696
1676
  // Strategy 3: Remove leading/trailing text and try parsing
1697
1677
  () => {
1698
- const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, "");
1678
+ const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, '');
1699
1679
  try {
1700
1680
  return JSON.parse(jsonMatch);
1701
1681
  }
@@ -1717,7 +1697,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1717
1697
  }
1718
1698
  // Strategy 5: Use AI fixing (only if explicitly enabled)
1719
1699
  if (enableAiFix) {
1720
- getLumicLogger().warn("Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.");
1700
+ getLumicLogger().warn('Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.');
1721
1701
  try {
1722
1702
  const aiFixedJson = await fixJsonWithAI(cleanedContent);
1723
1703
  if (aiFixedJson !== null) {
@@ -1730,7 +1710,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1730
1710
  catch {
1731
1711
  // AI returned something that looks like JSON but isn't valid
1732
1712
  // Increment recursion depth and try parsing the AI response
1733
- getLumicLogger().warn("AI fixing returned invalid JSON, attempting recursive parse...");
1713
+ getLumicLogger().warn('AI fixing returned invalid JSON, attempting recursive parse...');
1734
1714
  return parseResponse(JSON.stringify(aiFixedJson), responseFormat, {
1735
1715
  ...options,
1736
1716
  _recursionDepth: recursionDepth + 1,
@@ -1739,14 +1719,14 @@ async function parseResponse(content, responseFormat, options = {}) {
1739
1719
  }
1740
1720
  }
1741
1721
  catch (error) {
1742
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1722
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1743
1723
  getLumicLogger().error(`AI JSON fixing failed: ${errorMessage}`);
1744
1724
  // Continue to final error below
1745
1725
  }
1746
1726
  }
1747
1727
  // If all strategies fail, throw an error
1748
1728
  getLumicLogger().error(`Failed to parse JSON from content: ${cleanedContent}. Original JSON: ${content}`);
1749
- throw new Error("Unable to parse JSON response");
1729
+ throw new Error('Unable to parse JSON response');
1750
1730
  }
1751
1731
  else {
1752
1732
  return content;
@@ -2023,11 +2003,19 @@ const DEFAULT_RETRY_CONFIG = {
2023
2003
  * @throws The last error encountered if all retry attempts fail or if a non-retryable error occurs
2024
2004
  */
2025
2005
  async function withRetry(fn, config = {}, label = 'unknown') {
2026
- const { maxRetries, baseDelayMs, maxDelayMs, retryableErrors, circuitBreaker } = {
2006
+ const { maxRetries, baseDelayMs, maxDelayMs, retryableErrors, circuitBreaker, signal } = {
2027
2007
  ...DEFAULT_RETRY_CONFIG,
2028
2008
  ...config,
2029
2009
  };
2030
2010
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
2011
+ // Short-circuit on cancellation BEFORE starting a fresh attempt so
2012
+ // the caller does not pay the cost of issuing a request whose result
2013
+ // they have already discarded.
2014
+ if (signal?.aborted) {
2015
+ throw signal.reason instanceof Error
2016
+ ? signal.reason
2017
+ : new Error('Aborted');
2018
+ }
2031
2019
  try {
2032
2020
  // If a circuit breaker is provided, execute through it
2033
2021
  const result = circuitBreaker
@@ -2040,6 +2028,13 @@ async function withRetry(fn, config = {}, label = 'unknown') {
2040
2028
  if (error instanceof CircuitBreakerOpenError) {
2041
2029
  throw error;
2042
2030
  }
2031
+ // Do not retry on cooperative cancellation — the caller has
2032
+ // explicitly asked us to stop.
2033
+ if (signal?.aborted) {
2034
+ throw signal.reason instanceof Error
2035
+ ? signal.reason
2036
+ : new Error('Aborted');
2037
+ }
2043
2038
  // Don't retry if we've exhausted all attempts
2044
2039
  if (attempt === maxRetries) {
2045
2040
  throw error;
@@ -2053,7 +2048,26 @@ async function withRetry(fn, config = {}, label = 'unknown') {
2053
2048
  const jitter = Math.random() * 1000;
2054
2049
  const delay = Math.min(exponentialDelay + jitter, maxDelayMs);
2055
2050
  getLumicLogger().warn(`[${label}] Attempt ${attempt + 1} failed, retrying in ${Math.round(delay)}ms`);
2056
- await new Promise((resolve) => setTimeout(resolve, delay));
2051
+ // Sleep but bail early if signal aborts mid-backoff.
2052
+ await new Promise((resolve, reject) => {
2053
+ const timer = setTimeout(() => {
2054
+ if (signal)
2055
+ signal.removeEventListener('abort', onAbort);
2056
+ resolve();
2057
+ }, delay);
2058
+ const onAbort = () => {
2059
+ clearTimeout(timer);
2060
+ reject(signal?.reason instanceof Error ? signal.reason : new Error('Aborted'));
2061
+ };
2062
+ if (signal) {
2063
+ if (signal.aborted) {
2064
+ clearTimeout(timer);
2065
+ reject(signal.reason instanceof Error ? signal.reason : new Error('Aborted'));
2066
+ return;
2067
+ }
2068
+ signal.addEventListener('abort', onAbort, { once: true });
2069
+ }
2070
+ });
2057
2071
  }
2058
2072
  }
2059
2073
  // This should never be reached, but TypeScript needs it for type safety
@@ -2340,15 +2354,13 @@ const isRetryableLLMError = (error) => {
2340
2354
  if (error instanceof Error) {
2341
2355
  const message = error.message;
2342
2356
  // Retry on rate limits (429)
2343
- if (message.includes("429") ||
2344
- message.includes("rate limit") ||
2345
- message.includes("Rate limit")) {
2357
+ if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
2346
2358
  return true;
2347
2359
  }
2348
2360
  // Retry on transient body-corruption 400s. Match the exact OpenAI error
2349
2361
  // string to avoid retrying genuine client-side validation 400s (which
2350
2362
  // would re-fail forever and waste retry budget).
2351
- if (message.includes("could not parse the JSON body of your request")) {
2363
+ if (message.includes('could not parse the JSON body of your request')) {
2352
2364
  return true;
2353
2365
  }
2354
2366
  }
@@ -2417,26 +2429,23 @@ function isReasoningModel(model) {
2417
2429
  * @throws Error if the response format is invalid or incompatible.
2418
2430
  */
2419
2431
  function getResponseFormatOption(responseFormat, normalizedModel) {
2420
- if (responseFormat === "text") {
2421
- return { type: "text" };
2432
+ if (responseFormat === 'text') {
2433
+ return { type: 'text' };
2422
2434
  }
2423
- if (responseFormat === "json") {
2424
- return supportsJsonMode(normalizedModel)
2425
- ? { type: "json_object" }
2426
- : { type: "text" };
2435
+ if (responseFormat === 'json') {
2436
+ return supportsJsonMode(normalizedModel) ? { type: 'json_object' } : { type: 'text' };
2427
2437
  }
2428
- if (typeof responseFormat === "object" &&
2429
- responseFormat.type === "json_schema") {
2438
+ if (typeof responseFormat === 'object' && responseFormat.type === 'json_schema') {
2430
2439
  if (!isStructuredOutputCompatibleModel(normalizedModel)) {
2431
2440
  throw new Error(`${normalizedModel} is not compatible with structured outputs / json object.`);
2432
2441
  }
2433
2442
  return {
2434
- type: "json_schema",
2443
+ type: 'json_schema',
2435
2444
  json_schema: {
2436
- type: "object",
2445
+ type: 'object',
2437
2446
  properties: responseFormat.schema.properties,
2438
2447
  required: responseFormat.schema.required || [],
2439
- name: "Response",
2448
+ name: 'Response',
2440
2449
  },
2441
2450
  };
2442
2451
  }
@@ -2495,7 +2504,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2495
2504
  const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
2496
2505
  const apiKey = options.apiKey || getSecrets().openai.apiKey;
2497
2506
  if (!apiKey) {
2498
- throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
2507
+ throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
2499
2508
  }
2500
2509
  const openai = new OpenAI({
2501
2510
  apiKey: apiKey,
@@ -2505,7 +2514,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2505
2514
  // Add developer prompt if present
2506
2515
  if (options.developerPrompt && supportsDeveloperPrompt(normalizedModel)) {
2507
2516
  messages.push({
2508
- role: "developer",
2517
+ role: 'developer',
2509
2518
  content: options.developerPrompt,
2510
2519
  });
2511
2520
  }
@@ -2514,15 +2523,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2514
2523
  messages.push(...options.context);
2515
2524
  }
2516
2525
  // Add user content
2517
- if (typeof content === "string") {
2526
+ if (typeof content === 'string') {
2518
2527
  messages.push({
2519
- role: "user",
2528
+ role: 'user',
2520
2529
  content,
2521
2530
  });
2522
2531
  }
2523
2532
  else {
2524
2533
  messages.push({
2525
- role: "user",
2534
+ role: 'user',
2526
2535
  content,
2527
2536
  });
2528
2537
  }
@@ -2536,8 +2545,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2536
2545
  queryOptions.tools = options.tools;
2537
2546
  }
2538
2547
  // Only include temperature if the model supports it
2539
- if (options.temperature !== undefined &&
2540
- supportsTemperature(normalizedModel)) {
2548
+ if (options.temperature !== undefined && supportsTemperature(normalizedModel)) {
2541
2549
  queryOptions.temperature = options.temperature;
2542
2550
  }
2543
2551
  // Only include max_completion_tokens when specified
@@ -2547,16 +2555,25 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2547
2555
  // Only set response_format when it's not the default 'text' type
2548
2556
  // (sending response_format: {type: 'text'} is redundant and may cause issues)
2549
2557
  const responseFormatOption = getResponseFormatOption(responseFormat, normalizedModel);
2550
- if (responseFormatOption.type !== "text") {
2558
+ if (responseFormatOption.type !== 'text') {
2551
2559
  queryOptions.response_format = responseFormatOption;
2552
2560
  }
2553
2561
  let completion;
2554
2562
  try {
2555
- completion = await withRetry(() => openai.chat.completions.create(queryOptions), {
2563
+ completion = await withRetry(
2564
+ // Pass the AbortSignal into the OpenAI SDK request-options object.
2565
+ // The OpenAI SDK honours `signal` to cancel the in-flight fetch
2566
+ // immediately when it fires — this is what makes caller-driven
2567
+ // cancel-and-restart (e.g. signal-monitoring's materiality
2568
+ // re-trigger path in the engine) actually release the HTTP
2569
+ // connection and response-body buffer rather than just discarding
2570
+ // the resolved value at the engine boundary.
2571
+ () => openai.chat.completions.create(queryOptions, options.signal ? { signal: options.signal } : undefined), {
2556
2572
  maxRetries: 3,
2557
2573
  baseDelayMs: 2000,
2558
2574
  maxDelayMs: 30000,
2559
2575
  retryableErrors: isRetryableLLMError,
2576
+ signal: options.signal,
2560
2577
  }, `OpenAI:${normalizedModel}`);
2561
2578
  }
2562
2579
  catch (error) {
@@ -2568,19 +2585,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2568
2585
  // for production prompts containing sensitive context.
2569
2586
  const errorMessage = error instanceof Error ? error.message : String(error);
2570
2587
  const totalContentChars = messages.reduce((sum, msg) => {
2571
- if (typeof msg.content === "string")
2588
+ if (typeof msg.content === 'string')
2572
2589
  return sum + msg.content.length;
2573
2590
  if (Array.isArray(msg.content)) {
2574
- return (sum +
2575
- msg.content.reduce((s, part) => {
2576
- if (typeof part === "object" &&
2577
- part !== null &&
2578
- "text" in part &&
2579
- typeof part.text === "string") {
2580
- return s + part.text.length;
2581
- }
2582
- return s;
2583
- }, 0));
2591
+ return sum + msg.content.reduce((s, part) => {
2592
+ if (typeof part === 'object' && part !== null && 'text' in part && typeof part.text === 'string') {
2593
+ return s + part.text.length;
2594
+ }
2595
+ return s;
2596
+ }, 0);
2584
2597
  }
2585
2598
  return sum;
2586
2599
  }, 0);
@@ -2607,7 +2620,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2607
2620
  const cachedTokens = completion.usage?.prompt_tokens_details?.cached_tokens ?? 0;
2608
2621
  const response = {
2609
2622
  id: completion.id,
2610
- content: completion.choices[0]?.message?.content || "",
2623
+ content: completion.choices[0]?.message?.content || '',
2611
2624
  tool_calls: completion.choices[0]?.message?.tool_calls,
2612
2625
  usage: {
2613
2626
  prompt_tokens: completion.usage?.prompt_tokens ?? 0,
@@ -2617,7 +2630,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2617
2630
  },
2618
2631
  system_fingerprint: completion.system_fingerprint,
2619
2632
  service_tier: options.service_tier,
2620
- provider: "openai",
2633
+ provider: 'openai',
2621
2634
  model: normalizedModel,
2622
2635
  };
2623
2636
  return response;
@@ -2630,7 +2643,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2630
2643
  * @param options The options for the LLM call. Defaults to an empty object.
2631
2644
  * @return A promise that resolves to the response from the LLM.
2632
2645
  */
2633
- const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", options = {}) => {
2646
+ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', options = {}) => {
2634
2647
  const mergedOptions = {
2635
2648
  ...DEFAULT_OPTIONS$1,
2636
2649
  ...options,
@@ -2639,7 +2652,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2639
2652
  // Track cost in the global cost tracker. Pass cached tokens through so the
2640
2653
  // tracker applies the discounted cached-input rate (typically ~50% of the
2641
2654
  // standard input rate) instead of billing every input token at full price.
2642
- getLLMCostTracker().trackUsage("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
2655
+ getLLMCostTracker().trackUsage('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
2643
2656
  // Handle tool calls differently
2644
2657
  if (completion.tool_calls && completion.tool_calls.length > 0) {
2645
2658
  const toolCallResponse = {
@@ -2655,10 +2668,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2655
2668
  prompt_tokens: completion.usage.prompt_tokens,
2656
2669
  completion_tokens: completion.usage.completion_tokens,
2657
2670
  reasoning_tokens: 0,
2658
- provider: "openai",
2671
+ provider: 'openai',
2659
2672
  model: completion.model,
2660
2673
  cached_tokens: completion.usage.cached_tokens,
2661
- cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2674
+ cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2662
2675
  },
2663
2676
  tool_calls: completion.tool_calls,
2664
2677
  };
@@ -2666,7 +2679,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2666
2679
  // Handle regular responses
2667
2680
  const parsedResponse = await parseResponse(completion.content, responseFormat);
2668
2681
  if (parsedResponse === null) {
2669
- throw new Error("Failed to parse response");
2682
+ throw new Error('Failed to parse response');
2670
2683
  }
2671
2684
  return {
2672
2685
  response: parsedResponse,
@@ -2674,10 +2687,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
2674
2687
  prompt_tokens: completion.usage.prompt_tokens,
2675
2688
  completion_tokens: completion.usage.completion_tokens,
2676
2689
  reasoning_tokens: 0,
2677
- provider: "openai",
2690
+ provider: 'openai',
2678
2691
  model: completion.model,
2679
2692
  cached_tokens: completion.usage.cached_tokens,
2680
- cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2693
+ cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2681
2694
  },
2682
2695
  tool_calls: completion.tool_calls,
2683
2696
  };
@@ -2712,44 +2725,45 @@ const makeResponsesAPICall = async (input, options = {}) => {
2712
2725
  const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
2713
2726
  const apiKey = options.apiKey || getSecrets().openai.apiKey;
2714
2727
  if (!apiKey) {
2715
- throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
2728
+ throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
2716
2729
  }
2717
2730
  const openai = new OpenAI({
2718
2731
  apiKey: apiKey,
2719
2732
  timeout: options.timeout ?? LLM_TIMEOUT_MS,
2720
2733
  });
2721
- // Remove apiKey, model, and timeout from options before creating request body
2722
- const { apiKey: _, model: __, timeout: ___, ...cleanOptions } = options;
2734
+ // Remove apiKey, model, timeout, and signal from options before creating request body
2735
+ const { apiKey: _, model: __, timeout: ___, signal: ____, ...cleanOptions } = options;
2723
2736
  const requestBody = {
2724
2737
  model: normalizedModel,
2725
2738
  input,
2726
2739
  ...cleanOptions,
2727
2740
  };
2728
2741
  // Make the API call to the Responses endpoint
2729
- const response = await withRetry(() => openai.responses.create(requestBody), {
2742
+ const response = await withRetry(() => openai.responses.create(requestBody, options.signal ? { signal: options.signal } : undefined), {
2730
2743
  maxRetries: 3,
2731
2744
  baseDelayMs: 2000,
2732
2745
  maxDelayMs: 30000,
2733
2746
  retryableErrors: isRetryableLLMError,
2747
+ signal: options.signal,
2734
2748
  }, `OpenAI-Responses:${normalizedModel}`);
2735
2749
  // Responses API exposes cached input tokens under `input_tokens_details.cached_tokens`
2736
2750
  // (the equivalent of Chat Completions' `prompt_tokens_details.cached_tokens`).
2737
2751
  const responsesCachedTokens = response.usage?.input_tokens_details?.cached_tokens || 0;
2738
2752
  // Track cost in the global cost tracker
2739
- getLLMCostTracker().trackUsage("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
2753
+ getLLMCostTracker().trackUsage('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
2740
2754
  // Extract tool calls from the output
2741
2755
  const toolCalls = response.output
2742
- ?.filter((item) => item.type === "function_call")
2756
+ ?.filter((item) => item.type === 'function_call')
2743
2757
  .map((toolCall) => ({
2744
2758
  id: toolCall.call_id,
2745
- type: "function",
2759
+ type: 'function',
2746
2760
  function: {
2747
2761
  name: toolCall.name,
2748
2762
  arguments: toolCall.arguments,
2749
2763
  },
2750
2764
  }));
2751
2765
  // Extract code interpreter outputs if present
2752
- const codeInterpreterCalls = response.output?.filter((item) => item.type === "code_interpreter_call");
2766
+ const codeInterpreterCalls = response.output?.filter((item) => item.type === 'code_interpreter_call');
2753
2767
  let codeInterpreterOutputs = undefined;
2754
2768
  if (codeInterpreterCalls && codeInterpreterCalls.length > 0) {
2755
2769
  // Each code_interpreter_call has an 'outputs' array property
@@ -2775,34 +2789,32 @@ const makeResponsesAPICall = async (input, options = {}) => {
2775
2789
  prompt_tokens: response.usage?.input_tokens || 0,
2776
2790
  completion_tokens: response.usage?.output_tokens || 0,
2777
2791
  reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
2778
- provider: "openai",
2792
+ provider: 'openai',
2779
2793
  model: normalizedModel,
2780
2794
  cached_tokens: responsesCachedTokens,
2781
- cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2795
+ cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2782
2796
  },
2783
2797
  tool_calls: toolCalls,
2784
- ...(codeInterpreterOutputs
2785
- ? { code_interpreter_outputs: codeInterpreterOutputs }
2786
- : {}),
2798
+ ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
2787
2799
  };
2788
2800
  }
2789
2801
  // Extract text content from the response output
2790
2802
  const textContent = response.output
2791
- ?.filter((item) => item.type === "message")
2803
+ ?.filter((item) => item.type === 'message')
2792
2804
  .map((item) => item.content
2793
- .filter((content) => content.type === "output_text")
2805
+ .filter((content) => content.type === 'output_text')
2794
2806
  .map((content) => content.text)
2795
- .join(""))
2796
- .join("") || "";
2807
+ .join(''))
2808
+ .join('') || '';
2797
2809
  // Determine the format for parsing the response
2798
- let parsingFormat = "text";
2799
- if (requestBody.text?.format?.type === "json_object") {
2800
- parsingFormat = "json";
2810
+ let parsingFormat = 'text';
2811
+ if (requestBody.text?.format?.type === 'json_object') {
2812
+ parsingFormat = 'json';
2801
2813
  }
2802
2814
  // Handle regular responses
2803
2815
  const parsedResponse = await parseResponse(textContent, parsingFormat);
2804
2816
  if (parsedResponse === null) {
2805
- throw new Error("Failed to parse response from Responses API");
2817
+ throw new Error('Failed to parse response from Responses API');
2806
2818
  }
2807
2819
  return {
2808
2820
  response: parsedResponse,
@@ -2810,15 +2822,13 @@ const makeResponsesAPICall = async (input, options = {}) => {
2810
2822
  prompt_tokens: response.usage?.input_tokens || 0,
2811
2823
  completion_tokens: response.usage?.output_tokens || 0,
2812
2824
  reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
2813
- provider: "openai",
2825
+ provider: 'openai',
2814
2826
  model: normalizedModel,
2815
2827
  cached_tokens: responsesCachedTokens,
2816
- cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2828
+ cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2817
2829
  },
2818
2830
  tool_calls: toolCalls,
2819
- ...(codeInterpreterOutputs
2820
- ? { code_interpreter_outputs: codeInterpreterOutputs }
2821
- : {}),
2831
+ ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
2822
2832
  };
2823
2833
  };
2824
2834
 
@@ -8128,12 +8138,12 @@ function sanitizeObject(obj, sensitiveFields = ['apiKey', 'token', 'secret', 'pa
8128
8138
  const isRetryableAnthropicError = (error) => {
8129
8139
  if (error instanceof Error) {
8130
8140
  const message = error.message;
8131
- if (message.includes("429") ||
8132
- message.includes("500") ||
8133
- message.includes("503") ||
8134
- message.includes("529") ||
8135
- message.includes("rate limit") ||
8136
- message.includes("overloaded")) {
8141
+ if (message.includes('429') ||
8142
+ message.includes('500') ||
8143
+ message.includes('503') ||
8144
+ message.includes('529') ||
8145
+ message.includes('rate limit') ||
8146
+ message.includes('overloaded')) {
8137
8147
  return true;
8138
8148
  }
8139
8149
  }
@@ -8148,11 +8158,8 @@ const isRetryableAnthropicError = (error) => {
8148
8158
  function translateToolsToAnthropic(tools) {
8149
8159
  return tools.map((tool) => ({
8150
8160
  name: tool.function.name,
8151
- description: tool.function.description || "",
8152
- input_schema: (tool.function.parameters || {
8153
- type: "object",
8154
- properties: {},
8155
- }),
8161
+ description: tool.function.description || '',
8162
+ input_schema: (tool.function.parameters || { type: 'object', properties: {} }),
8156
8163
  }));
8157
8164
  }
8158
8165
  /**
@@ -8165,31 +8172,27 @@ function translateContextToAnthropic(context) {
8165
8172
  const systemParts = [];
8166
8173
  const messages = [];
8167
8174
  for (const msg of context) {
8168
- if (msg.role === "system" || msg.role === "developer") {
8169
- if (typeof msg.content === "string") {
8175
+ if (msg.role === 'system' || msg.role === 'developer') {
8176
+ if (typeof msg.content === 'string') {
8170
8177
  systemParts.push(msg.content);
8171
8178
  }
8172
8179
  continue;
8173
8180
  }
8174
- if (msg.role === "user") {
8175
- const content = typeof msg.content === "string"
8181
+ if (msg.role === 'user') {
8182
+ const content = typeof msg.content === 'string'
8176
8183
  ? msg.content
8177
8184
  : JSON.stringify(msg.content);
8178
- messages.push({ role: "user", content });
8185
+ messages.push({ role: 'user', content });
8179
8186
  continue;
8180
8187
  }
8181
- if (msg.role === "assistant") {
8188
+ if (msg.role === 'assistant') {
8182
8189
  const assistantMsg = msg;
8183
8190
  // If the assistant message has tool_calls, translate to Anthropic tool_use blocks
8184
8191
  if (assistantMsg.tool_calls && assistantMsg.tool_calls.length > 0) {
8185
8192
  const contentBlocks = [];
8186
8193
  // Include text content if present
8187
- if (typeof assistantMsg.content === "string" &&
8188
- assistantMsg.content.trim()) {
8189
- contentBlocks.push({
8190
- type: "text",
8191
- text: assistantMsg.content,
8192
- });
8194
+ if (typeof assistantMsg.content === 'string' && assistantMsg.content.trim()) {
8195
+ contentBlocks.push({ type: 'text', text: assistantMsg.content });
8193
8196
  }
8194
8197
  // Translate each tool_call to a tool_use block
8195
8198
  for (const tc of assistantMsg.tool_calls) {
@@ -8201,36 +8204,32 @@ function translateContextToAnthropic(context) {
8201
8204
  input = { raw: tc.function.arguments };
8202
8205
  }
8203
8206
  contentBlocks.push({
8204
- type: "tool_use",
8207
+ type: 'tool_use',
8205
8208
  id: tc.id,
8206
8209
  name: tc.function.name,
8207
8210
  input,
8208
8211
  });
8209
8212
  }
8210
- messages.push({ role: "assistant", content: contentBlocks });
8213
+ messages.push({ role: 'assistant', content: contentBlocks });
8211
8214
  }
8212
8215
  else {
8213
- const content = typeof assistantMsg.content === "string"
8216
+ const content = typeof assistantMsg.content === 'string'
8214
8217
  ? assistantMsg.content
8215
8218
  : JSON.stringify(assistantMsg.content);
8216
- messages.push({ role: "assistant", content });
8219
+ messages.push({ role: 'assistant', content });
8217
8220
  }
8218
8221
  continue;
8219
8222
  }
8220
- if (msg.role === "tool") {
8223
+ if (msg.role === 'tool') {
8221
8224
  // Anthropic expects tool results as user messages with tool_result content blocks
8222
8225
  const toolMsg = msg;
8223
8226
  messages.push({
8224
- role: "user",
8225
- content: [
8226
- {
8227
- type: "tool_result",
8227
+ role: 'user',
8228
+ content: [{
8229
+ type: 'tool_result',
8228
8230
  tool_use_id: toolMsg.tool_call_id,
8229
- content: typeof toolMsg.content === "string"
8230
- ? toolMsg.content
8231
- : JSON.stringify(toolMsg.content),
8232
- },
8233
- ],
8231
+ content: typeof toolMsg.content === 'string' ? toolMsg.content : JSON.stringify(toolMsg.content),
8232
+ }],
8234
8233
  });
8235
8234
  continue;
8236
8235
  }
@@ -8251,13 +8250,13 @@ function translateContextToAnthropic(context) {
8251
8250
  merged.push({ role: msg.role, content: toContentBlocks(msg.content) });
8252
8251
  }
8253
8252
  }
8254
- return { messages: merged, systemText: systemParts.join("\n\n") };
8253
+ return { messages: merged, systemText: systemParts.join('\n\n') };
8255
8254
  }
8256
8255
  /** Convert string or content block array to a uniform content block array. */
8257
8256
  function toContentBlocks(content) {
8258
- if (typeof content === "string") {
8257
+ if (typeof content === 'string') {
8259
8258
  const textBlock = {
8260
- type: "text",
8259
+ type: 'text',
8261
8260
  text: content,
8262
8261
  citations: null,
8263
8262
  };
@@ -8277,11 +8276,11 @@ function toContentBlocks(content) {
8277
8276
  * @param options The options for the LLM call.
8278
8277
  * @return A promise that resolves to the response from the Anthropic API.
8279
8278
  */
8280
- async function makeAnthropicCall(content, responseFormat = "text", options = {}) {
8281
- const model = (options.model || "claude-sonnet-4-6");
8279
+ async function makeAnthropicCall(content, responseFormat = 'text', options = {}) {
8280
+ const model = (options.model || 'claude-sonnet-4-6');
8282
8281
  const apiKey = options.apiKey || getSecrets().anthropic.apiKey;
8283
8282
  if (!apiKey) {
8284
- throw new Error("Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set");
8283
+ throw new Error('Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set');
8285
8284
  }
8286
8285
  const client = new Anthropic({
8287
8286
  apiKey,
@@ -8293,10 +8292,8 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8293
8292
  systemParts.push(options.developerPrompt);
8294
8293
  }
8295
8294
  // If JSON response is requested, instruct the model via system prompt
8296
- if (responseFormat === "json" ||
8297
- (typeof responseFormat === "object" &&
8298
- responseFormat.type === "json_schema")) {
8299
- systemParts.push("You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.");
8295
+ if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
8296
+ systemParts.push('You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.');
8300
8297
  }
8301
8298
  // Build messages array
8302
8299
  const messages = [];
@@ -8309,7 +8306,7 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8309
8306
  messages.push(...translated.messages);
8310
8307
  }
8311
8308
  // Add user content
8312
- messages.push({ role: "user", content });
8309
+ messages.push({ role: 'user', content });
8313
8310
  // Build request params
8314
8311
  const requestParams = {
8315
8312
  model,
@@ -8318,37 +8315,46 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8318
8315
  };
8319
8316
  // Add system prompt if present
8320
8317
  if (systemParts.length > 0) {
8321
- requestParams.system = systemParts.join("\n\n");
8322
- }
8323
- // Add temperature if specified
8324
- if (options.temperature !== undefined) {
8325
- requestParams.temperature = options.temperature;
8326
- }
8327
- if (options.top_p !== undefined) {
8328
- requestParams.top_p = options.top_p;
8329
- }
8318
+ requestParams.system = systemParts.join('\n\n');
8319
+ }
8320
+ // Sampling controls are intentionally disabled for the Anthropic provider.
8321
+ //
8322
+ // The claude-*-4.x model family rejects any request that specifies BOTH
8323
+ // `temperature` and `top_p` ("`temperature` and `top_p` cannot both be
8324
+ // specified for this model. Please use only one." — HTTP 400), which aborts
8325
+ // the entire caller flow (e.g. the engine's account decision session).
8326
+ // Callers across the org pass both knobs generically, so rather than make
8327
+ // each one provider-aware we drop both here and let Anthropic use its model
8328
+ // defaults. `options.temperature` / `options.top_p` are intentionally not
8329
+ // forwarded. This is the Anthropic adapter only — the OpenAI, DeepSeek, and
8330
+ // OpenAI-compatible adapters still honor `temperature` and `top_p`.
8330
8331
  // Translate and add tools if present
8331
8332
  if (options.tools && options.tools.length > 0) {
8332
8333
  requestParams.tools = translateToolsToAnthropic(options.tools);
8333
8334
  }
8334
8335
  try {
8335
- const response = await withRetry(() => client.messages.create(requestParams), {
8336
+ const response = await withRetry(
8337
+ // Pass AbortSignal into the Anthropic SDK request-options object
8338
+ // so caller-driven cancellation interrupts the in-flight HTTP
8339
+ // request immediately. See LLMOptions.signal JSDoc for context.
8340
+ () => client.messages.create(requestParams, options.signal ? { signal: options.signal } : undefined), {
8336
8341
  maxRetries: 3,
8337
8342
  baseDelayMs: 2000,
8338
8343
  maxDelayMs: 30000,
8339
8344
  retryableErrors: isRetryableAnthropicError,
8345
+ signal: options.signal,
8340
8346
  }, `Anthropic:${model}`);
8341
8347
  const inputTokens = response.usage.input_tokens;
8342
8348
  const outputTokens = response.usage.output_tokens;
8343
8349
  // Track cost
8344
- getLLMCostTracker().trackUsage("anthropic", model, inputTokens, outputTokens);
8350
+ getLLMCostTracker().trackUsage('anthropic', model, inputTokens, outputTokens);
8345
8351
  // Extract tool use blocks
8346
- const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
8352
+ const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
8347
8353
  if (toolUseBlocks.length > 0) {
8348
8354
  // Translate Anthropic tool_use to OpenAI tool_calls format
8349
8355
  const toolCalls = toolUseBlocks.map((block) => ({
8350
8356
  id: block.id,
8351
- type: "function",
8357
+ type: 'function',
8352
8358
  function: {
8353
8359
  name: block.name,
8354
8360
  arguments: JSON.stringify(block.input),
@@ -8367,22 +8373,22 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8367
8373
  prompt_tokens: inputTokens,
8368
8374
  completion_tokens: outputTokens,
8369
8375
  reasoning_tokens: 0,
8370
- provider: "anthropic",
8376
+ provider: 'anthropic',
8371
8377
  model,
8372
- cost: calculateCost("anthropic", model, inputTokens, outputTokens),
8378
+ cost: calculateCost('anthropic', model, inputTokens, outputTokens),
8373
8379
  },
8374
8380
  tool_calls: toolCalls,
8375
8381
  };
8376
8382
  }
8377
8383
  // Extract text content
8378
8384
  const textContent = response.content
8379
- .filter((block) => block.type === "text")
8385
+ .filter((block) => block.type === 'text')
8380
8386
  .map((block) => block.text)
8381
- .join("");
8387
+ .join('');
8382
8388
  // Parse response
8383
8389
  const parsedResponse = await parseResponse(textContent, responseFormat);
8384
8390
  if (parsedResponse === null) {
8385
- throw new Error("Failed to parse Anthropic response");
8391
+ throw new Error('Failed to parse Anthropic response');
8386
8392
  }
8387
8393
  return {
8388
8394
  response: parsedResponse,
@@ -8390,9 +8396,9 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8390
8396
  prompt_tokens: inputTokens,
8391
8397
  completion_tokens: outputTokens,
8392
8398
  reasoning_tokens: 0,
8393
- provider: "anthropic",
8399
+ provider: 'anthropic',
8394
8400
  model,
8395
- cost: calculateCost("anthropic", model, inputTokens, outputTokens),
8401
+ cost: calculateCost('anthropic', model, inputTokens, outputTokens),
8396
8402
  },
8397
8403
  };
8398
8404
  }
@@ -8414,9 +8420,7 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
8414
8420
  const isRetryableError = (error) => {
8415
8421
  if (error instanceof Error) {
8416
8422
  const message = error.message;
8417
- if (message.includes("429") ||
8418
- message.includes("rate limit") ||
8419
- message.includes("Rate limit")) {
8423
+ if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
8420
8424
  return true;
8421
8425
  }
8422
8426
  }
@@ -8430,16 +8434,14 @@ function resolveApiKey(provider, optionsApiKey) {
8430
8434
  if (optionsApiKey)
8431
8435
  return optionsApiKey;
8432
8436
  const secrets = getSecrets();
8433
- const pathParts = provider.apiKeySecretPath.split(".");
8437
+ const pathParts = provider.apiKeySecretPath.split('.');
8434
8438
  let current = secrets;
8435
8439
  for (const part of pathParts) {
8436
- if (current === null ||
8437
- current === undefined ||
8438
- typeof current !== "object")
8439
- return "";
8440
+ if (current === null || current === undefined || typeof current !== 'object')
8441
+ return '';
8440
8442
  current = current[part];
8441
8443
  }
8442
- return (typeof current === "string" ? current : "") || "";
8444
+ return (typeof current === 'string' ? current : '') || '';
8443
8445
  }
8444
8446
  /**
8445
8447
  * Makes a call to an OpenAI-compatible provider (Kimi, Qwen, or any future provider).
@@ -8453,12 +8455,12 @@ function resolveApiKey(provider, optionsApiKey) {
8453
8455
  * @param providerName The provider key in OPENAI_COMPATIBLE_PROVIDERS.
8454
8456
  * @return A promise that resolves to the response from the provider.
8455
8457
  */
8456
- async function makeOpenAICompatibleCall(content, responseFormat = "text", options = {}, providerName) {
8458
+ async function makeOpenAICompatibleCall(content, responseFormat = 'text', options = {}, providerName) {
8457
8459
  const providerConfig = OPENAI_COMPATIBLE_PROVIDERS[providerName];
8458
8460
  if (!providerConfig) {
8459
8461
  throw new Error(`Unknown OpenAI-compatible provider: ${providerName}`);
8460
8462
  }
8461
- const model = normalizeModelName(options.model || "");
8463
+ const model = normalizeModelName(options.model || '');
8462
8464
  const apiKey = resolveApiKey(providerConfig, options.apiKey);
8463
8465
  if (!apiKey) {
8464
8466
  throw new Error(`${providerConfig.name} API key is not provided and ${providerConfig.apiKeyEnvVar} environment variable is not set`);
@@ -8471,36 +8473,31 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8471
8473
  const messages = [];
8472
8474
  // Add system message with developer prompt if present
8473
8475
  if (options.developerPrompt) {
8474
- messages.push({ role: "system", content: options.developerPrompt });
8476
+ messages.push({ role: 'system', content: options.developerPrompt });
8475
8477
  }
8476
8478
  // Add context if present
8477
8479
  if (options.context) {
8478
8480
  messages.push(...options.context);
8479
8481
  }
8480
8482
  // Add user content
8481
- if (typeof content === "string") {
8482
- messages.push({ role: "user", content });
8483
+ if (typeof content === 'string') {
8484
+ messages.push({ role: 'user', content });
8483
8485
  }
8484
8486
  else {
8485
- messages.push({ role: "user", content });
8487
+ messages.push({ role: 'user', content });
8486
8488
  }
8487
8489
  // Determine if the model supports JSON mode
8488
8490
  const supportsJson = isValidModel(model) && getModelCapabilities(model).supportsJson;
8489
8491
  // If JSON response format, add hint to system prompt
8490
- if ((responseFormat === "json" ||
8491
- (typeof responseFormat === "object" &&
8492
- responseFormat.type === "json_schema")) &&
8492
+ if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) &&
8493
8493
  supportsJson) {
8494
- if (!messages.some((m) => m.role === "system")) {
8495
- messages.unshift({
8496
- role: "system",
8497
- content: "Please respond in valid JSON format.",
8498
- });
8494
+ if (!messages.some((m) => m.role === 'system')) {
8495
+ messages.unshift({ role: 'system', content: 'Please respond in valid JSON format.' });
8499
8496
  }
8500
8497
  else {
8501
- const systemMsgIndex = messages.findIndex((m) => m.role === "system");
8498
+ const systemMsgIndex = messages.findIndex((m) => m.role === 'system');
8502
8499
  const systemMsg = messages[systemMsgIndex];
8503
- if (typeof systemMsg.content === "string") {
8500
+ if (typeof systemMsg.content === 'string') {
8504
8501
  messages[systemMsgIndex] = {
8505
8502
  ...systemMsg,
8506
8503
  content: `${systemMsg.content} Please respond in valid JSON format.`,
@@ -8513,8 +8510,8 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8513
8510
  messages,
8514
8511
  };
8515
8512
  // Add response format if JSON is supported
8516
- if (responseFormat !== "text" && supportsJson) {
8517
- queryOptions.response_format = { type: "json_object" };
8513
+ if (responseFormat !== 'text' && supportsJson) {
8514
+ queryOptions.response_format = { type: 'json_object' };
8518
8515
  }
8519
8516
  // Temperature
8520
8517
  if (options.temperature !== undefined) {
@@ -8538,11 +8535,16 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8538
8535
  queryOptions.tools = options.tools;
8539
8536
  }
8540
8537
  try {
8541
- const completion = await withRetry(() => openai.chat.completions.create(queryOptions), {
8538
+ const completion = await withRetry(
8539
+ // Pass AbortSignal into the SDK request-options object so
8540
+ // caller-driven cancellation interrupts the in-flight HTTP
8541
+ // request immediately. See LLMOptions.signal JSDoc for context.
8542
+ () => openai.chat.completions.create(queryOptions, options.signal ? { signal: options.signal } : undefined), {
8542
8543
  maxRetries: 3,
8543
8544
  baseDelayMs: 2000,
8544
8545
  maxDelayMs: 30000,
8545
8546
  retryableErrors: isRetryableError,
8547
+ signal: options.signal,
8546
8548
  }, `${providerConfig.name}:${model}`);
8547
8549
  const promptTokens = completion.usage?.prompt_tokens || 0;
8548
8550
  const completionTokens = completion.usage?.completion_tokens || 0;
@@ -8572,7 +8574,7 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
8572
8574
  };
8573
8575
  }
8574
8576
  // Handle regular responses
8575
- const textContent = completion.choices[0]?.message?.content || "";
8577
+ const textContent = completion.choices[0]?.message?.content || '';
8576
8578
  const parsedResponse = await parseResponse(textContent, responseFormat);
8577
8579
  if (parsedResponse === null) {
8578
8580
  throw new Error(`Failed to parse ${providerConfig.name} response`);
@@ -8864,9 +8866,7 @@ const isRetryableDeepseekError = (error) => {
8864
8866
  if (error instanceof Error) {
8865
8867
  const message = error.message;
8866
8868
  // Retry only on rate limits (429)
8867
- if (message.includes("429") ||
8868
- message.includes("rate limit") ||
8869
- message.includes("Rate limit")) {
8869
+ if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
8870
8870
  return true;
8871
8871
  }
8872
8872
  }
@@ -8876,7 +8876,7 @@ const isRetryableDeepseekError = (error) => {
8876
8876
  * Default options for Deepseek API calls
8877
8877
  */
8878
8878
  const DEFAULT_DEEPSEEK_OPTIONS = {
8879
- defaultModel: "deepseek-chat",
8879
+ defaultModel: 'deepseek-chat',
8880
8880
  };
8881
8881
  /**
8882
8882
  * Checks if the given model is a supported Deepseek model
@@ -8888,7 +8888,7 @@ const isSupportedDeepseekModel = (model) => {
8888
8888
  return false;
8889
8889
  }
8890
8890
  const capabilities = getModelCapabilities(model);
8891
- return capabilities.provider === "deepseek";
8891
+ return capabilities.provider === 'deepseek';
8892
8892
  };
8893
8893
  /**
8894
8894
  * Checks if the given Deepseek model supports JSON output format
@@ -8921,19 +8921,17 @@ const supportsToolCalling = (model) => {
8921
8921
  */
8922
8922
  function getDeepseekResponseFormatOption(responseFormat, model) {
8923
8923
  // Check if the model supports JSON output
8924
- if (responseFormat !== "text" && !supportsJsonOutput(model)) {
8924
+ if (responseFormat !== 'text' && !supportsJsonOutput(model)) {
8925
8925
  getLumicLogger().warn(`Model ${model} does not support JSON output. Using text format instead.`);
8926
- return { type: "text" };
8926
+ return { type: 'text' };
8927
8927
  }
8928
- if (responseFormat === "text") {
8929
- return { type: "text" };
8928
+ if (responseFormat === 'text') {
8929
+ return { type: 'text' };
8930
8930
  }
8931
- if (responseFormat === "json" ||
8932
- (typeof responseFormat === "object" &&
8933
- responseFormat.type === "json_schema")) {
8934
- return { type: "json_object" };
8931
+ if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
8932
+ return { type: 'json_object' };
8935
8933
  }
8936
- return { type: "text" };
8934
+ return { type: 'text' };
8937
8935
  }
8938
8936
  /**
8939
8937
  * Creates a completion using the Deepseek API
@@ -8952,26 +8950,24 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8952
8950
  throw new Error(`Unsupported Deepseek model: ${normalizedModel}. Please use 'deepseek-chat' or 'deepseek-reasoner'.`);
8953
8951
  }
8954
8952
  // Check if tools are requested with a model that doesn't support them
8955
- if (options.tools &&
8956
- options.tools.length > 0 &&
8957
- !supportsToolCalling(normalizedModel)) {
8953
+ if (options.tools && options.tools.length > 0 && !supportsToolCalling(normalizedModel)) {
8958
8954
  throw new Error(`Model ${normalizedModel} does not support tool calling.`);
8959
8955
  }
8960
8956
  const apiKey = options.apiKey || getSecrets().deepseek.apiKey;
8961
8957
  if (!apiKey) {
8962
- throw new Error("Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set");
8958
+ throw new Error('Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set');
8963
8959
  }
8964
8960
  // Initialize OpenAI client with Deepseek API URL
8965
8961
  const openai = new OpenAI({
8966
8962
  apiKey,
8967
- baseURL: "https://api.deepseek.com",
8963
+ baseURL: 'https://api.deepseek.com',
8968
8964
  timeout: options.timeout ?? LLM_TIMEOUT_MS,
8969
8965
  });
8970
8966
  const messages = [];
8971
8967
  // Add system message with developer prompt if present
8972
8968
  if (options.developerPrompt) {
8973
8969
  messages.push({
8974
- role: "system",
8970
+ role: 'system',
8975
8971
  content: options.developerPrompt,
8976
8972
  });
8977
8973
  }
@@ -8980,35 +8976,33 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8980
8976
  messages.push(...options.context);
8981
8977
  }
8982
8978
  // Add user content
8983
- if (typeof content === "string") {
8979
+ if (typeof content === 'string') {
8984
8980
  messages.push({
8985
- role: "user",
8981
+ role: 'user',
8986
8982
  content,
8987
8983
  });
8988
8984
  }
8989
8985
  else {
8990
8986
  messages.push({
8991
- role: "user",
8987
+ role: 'user',
8992
8988
  content,
8993
8989
  });
8994
8990
  }
8995
8991
  // If JSON response format, include a hint in the system prompt
8996
- if ((responseFormat === "json" ||
8997
- (typeof responseFormat === "object" &&
8998
- responseFormat.type === "json_schema")) &&
8999
- supportsJsonOutput(normalizedModel)) {
8992
+ if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema'))
8993
+ && supportsJsonOutput(normalizedModel)) {
9000
8994
  // If there's no system message yet, add one
9001
- if (!messages.some((m) => m.role === "system")) {
8995
+ if (!messages.some(m => m.role === 'system')) {
9002
8996
  messages.unshift({
9003
- role: "system",
9004
- content: "Please respond in valid JSON format.",
8997
+ role: 'system',
8998
+ content: 'Please respond in valid JSON format.',
9005
8999
  });
9006
9000
  }
9007
9001
  else {
9008
9002
  // Append to existing system message
9009
- const systemMsgIndex = messages.findIndex((m) => m.role === "system");
9003
+ const systemMsgIndex = messages.findIndex(m => m.role === 'system');
9010
9004
  const systemMsg = messages[systemMsgIndex];
9011
- if (typeof systemMsg.content === "string") {
9005
+ if (typeof systemMsg.content === 'string') {
9012
9006
  messages[systemMsgIndex] = {
9013
9007
  ...systemMsg,
9014
9008
  content: `${systemMsg.content} Please respond in valid JSON format.`,
@@ -9049,7 +9043,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
9049
9043
  0;
9050
9044
  return {
9051
9045
  id: completion.id,
9052
- content: completion.choices[0]?.message?.content || "",
9046
+ content: completion.choices[0]?.message?.content || '',
9053
9047
  tool_calls: completion.choices[0]?.message?.tool_calls,
9054
9048
  usage: {
9055
9049
  prompt_tokens: completion.usage?.prompt_tokens ?? 0,
@@ -9058,7 +9052,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
9058
9052
  cached_tokens: cachedTokens,
9059
9053
  },
9060
9054
  system_fingerprint: completion.system_fingerprint,
9061
- provider: "deepseek",
9055
+ provider: 'deepseek',
9062
9056
  model: normalizedModel,
9063
9057
  };
9064
9058
  }
@@ -9075,7 +9069,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
9075
9069
  * @param options Configuration options including model ('deepseek-chat' or 'deepseek-reasoner'), tools, and apiKey.
9076
9070
  * @return A promise that resolves to the response from the Deepseek API.
9077
9071
  */
9078
- const makeDeepseekCall = async (content, responseFormat = "json", options = {}) => {
9072
+ const makeDeepseekCall = async (content, responseFormat = 'json', options = {}) => {
9079
9073
  // Set default model if not provided
9080
9074
  const mergedOptions = {
9081
9075
  ...options,
@@ -9085,17 +9079,17 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9085
9079
  }
9086
9080
  const modelName = normalizeModelName(mergedOptions.model);
9087
9081
  // Check if the requested response format is compatible with the model
9088
- if (responseFormat !== "text" && !supportsJsonOutput(modelName)) {
9082
+ if (responseFormat !== 'text' && !supportsJsonOutput(modelName)) {
9089
9083
  getLumicLogger().warn(`Model ${modelName} does not support JSON output. Will return error in the response.`);
9090
9084
  return {
9091
9085
  response: {
9092
- error: `Model ${modelName} does not support JSON output format.`,
9086
+ error: `Model ${modelName} does not support JSON output format.`
9093
9087
  },
9094
9088
  usage: {
9095
9089
  prompt_tokens: 0,
9096
9090
  completion_tokens: 0,
9097
9091
  reasoning_tokens: 0,
9098
- provider: "deepseek",
9092
+ provider: 'deepseek',
9099
9093
  model: modelName,
9100
9094
  cached_tokens: 0,
9101
9095
  cost: 0,
@@ -9104,19 +9098,17 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9104
9098
  };
9105
9099
  }
9106
9100
  // Check if tools are requested with a model that doesn't support them
9107
- if (mergedOptions.tools &&
9108
- mergedOptions.tools.length > 0 &&
9109
- !supportsToolCalling(modelName)) {
9101
+ if (mergedOptions.tools && mergedOptions.tools.length > 0 && !supportsToolCalling(modelName)) {
9110
9102
  getLumicLogger().warn(`Model ${modelName} does not support tool calling. Will return error in the response.`);
9111
9103
  return {
9112
9104
  response: {
9113
- error: `Model ${modelName} does not support tool calling.`,
9105
+ error: `Model ${modelName} does not support tool calling.`
9114
9106
  },
9115
9107
  usage: {
9116
9108
  prompt_tokens: 0,
9117
9109
  completion_tokens: 0,
9118
9110
  reasoning_tokens: 0,
9119
- provider: "deepseek",
9111
+ provider: 'deepseek',
9120
9112
  model: modelName,
9121
9113
  cached_tokens: 0,
9122
9114
  cost: 0,
@@ -9128,7 +9120,7 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9128
9120
  const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
9129
9121
  // Track cost in the global cost tracker. Pass cached tokens through so the
9130
9122
  // discounted cached-input pricing tier is applied.
9131
- getLLMCostTracker().trackUsage("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
9123
+ getLLMCostTracker().trackUsage('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
9132
9124
  // Handle tool calls similarly to OpenAI
9133
9125
  if (completion.tool_calls && completion.tool_calls.length > 0) {
9134
9126
  const toolCallResponse = {
@@ -9144,10 +9136,10 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9144
9136
  prompt_tokens: completion.usage.prompt_tokens,
9145
9137
  completion_tokens: completion.usage.completion_tokens,
9146
9138
  reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
9147
- provider: "deepseek",
9139
+ provider: 'deepseek',
9148
9140
  model: completion.model,
9149
9141
  cached_tokens: completion.usage.cached_tokens,
9150
- cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9142
+ cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9151
9143
  },
9152
9144
  tool_calls: completion.tool_calls,
9153
9145
  };
@@ -9155,7 +9147,7 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9155
9147
  // Handle regular responses
9156
9148
  const parsedResponse = await parseResponse(completion.content, responseFormat);
9157
9149
  if (parsedResponse === null) {
9158
- throw new Error("Failed to parse Deepseek response");
9150
+ throw new Error('Failed to parse Deepseek response');
9159
9151
  }
9160
9152
  return {
9161
9153
  response: parsedResponse,
@@ -9163,10 +9155,10 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9163
9155
  prompt_tokens: completion.usage.prompt_tokens,
9164
9156
  completion_tokens: completion.usage.completion_tokens,
9165
9157
  reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
9166
- provider: "deepseek",
9158
+ provider: 'deepseek',
9167
9159
  model: completion.model,
9168
9160
  cached_tokens: completion.usage.cached_tokens,
9169
- cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9161
+ cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9170
9162
  },
9171
9163
  tool_calls: completion.tool_calls,
9172
9164
  };
@@ -9176,13 +9168,13 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
9176
9168
  getLumicLogger().error(`Error in Deepseek API call: ${sanitizeError(error)}`);
9177
9169
  return {
9178
9170
  response: {
9179
- error: sanitizeError(error),
9171
+ error: sanitizeError(error)
9180
9172
  },
9181
9173
  usage: {
9182
9174
  prompt_tokens: 0,
9183
9175
  completion_tokens: 0,
9184
9176
  reasoning_tokens: 0,
9185
- provider: "deepseek",
9177
+ provider: 'deepseek',
9186
9178
  model: modelName,
9187
9179
  cached_tokens: 0,
9188
9180
  cost: 0,
@@ -9291,15 +9283,15 @@ const generateCacheKey = (auth, envVarsCheck = null) => {
9291
9283
  if (auth) {
9292
9284
  return `${auth.AWS_ACCESS_KEY_ID}:${auth.AWS_SECRET_ACCESS_KEY}:${auth.AWS_REGION}`;
9293
9285
  }
9294
- if (envVarsCheck?.source === "lambda") {
9286
+ if (envVarsCheck?.source === 'lambda') {
9295
9287
  // In Lambda, use function name as cache key since it uses IAM role
9296
9288
  const secrets = getSecrets();
9297
- return `lambda:${secrets.aws.lambdaFunctionName || "default"}`;
9289
+ return `lambda:${secrets.aws.lambdaFunctionName || 'default'}`;
9298
9290
  }
9299
9291
  if (envVarsCheck?.vars) {
9300
9292
  return `${envVarsCheck.vars.accessKeyId}:${envVarsCheck.vars.secretAccessKey}:${envVarsCheck.vars.region}`;
9301
9293
  }
9302
- return "default";
9294
+ return 'default';
9303
9295
  };
9304
9296
  const validateEnvironmentVars = () => {
9305
9297
  const secrets = getSecrets();
@@ -9312,37 +9304,37 @@ const validateEnvironmentVars = () => {
9312
9304
  // We're in a Lambda environment
9313
9305
  return {
9314
9306
  isValid: true,
9315
- source: "lambda",
9316
- vars: { accessKeyId: "", secretAccessKey: "", region: "" }, // No need for explicit credentials in Lambda
9307
+ source: 'lambda',
9308
+ vars: { accessKeyId: '', secretAccessKey: '', region: '' } // No need for explicit credentials in Lambda
9317
9309
  };
9318
9310
  }
9319
9311
  else if (areVarsComplete(secrets.aws.myAccessKeyId, secrets.aws.mySecretAccessKey, secrets.aws.myRegion)) {
9320
9312
  return {
9321
9313
  isValid: true,
9322
- source: "my_prefix",
9314
+ source: 'my_prefix',
9323
9315
  vars: {
9324
- accessKeyId: secrets.aws.myAccessKeyId ?? "",
9325
- secretAccessKey: secrets.aws.mySecretAccessKey ?? "",
9326
- region: secrets.aws.myRegion ?? "",
9327
- },
9316
+ accessKeyId: secrets.aws.myAccessKeyId,
9317
+ secretAccessKey: secrets.aws.mySecretAccessKey,
9318
+ region: secrets.aws.myRegion
9319
+ }
9328
9320
  };
9329
9321
  }
9330
9322
  else if (areVarsComplete(secrets.aws.accessKeyId, secrets.aws.secretAccessKey, secrets.aws.region)) {
9331
9323
  return {
9332
9324
  isValid: true,
9333
- source: "standard",
9325
+ source: 'standard',
9334
9326
  vars: {
9335
- accessKeyId: secrets.aws.accessKeyId ?? "",
9336
- secretAccessKey: secrets.aws.secretAccessKey ?? "",
9337
- region: secrets.aws.region ?? "",
9338
- },
9327
+ accessKeyId: secrets.aws.accessKeyId,
9328
+ secretAccessKey: secrets.aws.secretAccessKey,
9329
+ region: secrets.aws.region
9330
+ }
9339
9331
  };
9340
9332
  }
9341
9333
  // If we get here, no complete set of variables was found
9342
9334
  return {
9343
9335
  isValid: false,
9344
9336
  source: null,
9345
- missingVars: [],
9337
+ missingVars: []
9346
9338
  };
9347
9339
  };
9348
9340
  const initialiseAWSClient = (ClientClass, auth = null) => {
@@ -9351,52 +9343,48 @@ const initialiseAWSClient = (ClientClass, auth = null) => {
9351
9343
  const requestTimeout = ClientClass === clientS3.S3Client ? AWS_S3_TIMEOUT_MS : AWS_LAMBDA_TIMEOUT_MS;
9352
9344
  // Case 1: Explicit auth provided
9353
9345
  if (auth) {
9354
- if (typeof auth !== "object" || auth === null) {
9355
- throw new Error("Auth parameter must be an object");
9346
+ if (typeof auth !== 'object' || auth === null) {
9347
+ throw new Error('Auth parameter must be an object');
9356
9348
  }
9357
- const requiredAuthFields = [
9358
- "AWS_ACCESS_KEY_ID",
9359
- "AWS_SECRET_ACCESS_KEY",
9360
- "AWS_REGION",
9361
- ];
9362
- const missingFields = requiredAuthFields.filter((field) => !auth[field]);
9349
+ const requiredAuthFields = ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION'];
9350
+ const missingFields = requiredAuthFields.filter(field => !auth[field]);
9363
9351
  if (missingFields.length > 0) {
9364
- throw new Error(`Auth object is missing required fields: ${missingFields.join(", ")}`);
9352
+ throw new Error(`Auth object is missing required fields: ${missingFields.join(', ')}`);
9365
9353
  }
9366
9354
  return new ClientClass({
9367
9355
  region: auth.AWS_REGION,
9368
9356
  credentials: {
9369
9357
  accessKeyId: auth.AWS_ACCESS_KEY_ID,
9370
- secretAccessKey: auth.AWS_SECRET_ACCESS_KEY,
9358
+ secretAccessKey: auth.AWS_SECRET_ACCESS_KEY
9371
9359
  },
9372
9360
  requestHandler: {
9373
- requestTimeout,
9374
- },
9361
+ requestTimeout
9362
+ }
9375
9363
  });
9376
9364
  }
9377
9365
  // Case 2: Check environment variables
9378
9366
  const envVarsCheck = validateEnvironmentVars();
9379
9367
  if (!envVarsCheck.isValid) {
9380
- throw new Error("No valid AWS credentials found in environment variables");
9368
+ throw new Error('No valid AWS credentials found in environment variables');
9381
9369
  }
9382
9370
  // Case 2a: Lambda execution environment
9383
- if (envVarsCheck.source === "lambda") {
9371
+ if (envVarsCheck.source === 'lambda') {
9384
9372
  return new ClientClass({
9385
9373
  requestHandler: {
9386
- requestTimeout,
9387
- },
9374
+ requestTimeout
9375
+ }
9388
9376
  }); // AWS SDK will automatically use Lambda role credentials
9389
9377
  }
9390
9378
  // Case 2b: MY_ prefixed vars or standard AWS vars
9391
9379
  return new ClientClass({
9392
- region: envVarsCheck.vars?.region ?? "",
9380
+ region: envVarsCheck.vars.region,
9393
9381
  credentials: {
9394
- accessKeyId: envVarsCheck.vars?.accessKeyId ?? "",
9395
- secretAccessKey: envVarsCheck.vars?.secretAccessKey ?? "",
9382
+ accessKeyId: envVarsCheck.vars.accessKeyId,
9383
+ secretAccessKey: envVarsCheck.vars.secretAccessKey
9396
9384
  },
9397
9385
  requestHandler: {
9398
- requestTimeout,
9399
- },
9386
+ requestTimeout
9387
+ }
9400
9388
  });
9401
9389
  }
9402
9390
  catch (error) {
@@ -9409,9 +9397,7 @@ const initialiseS3Client = (auth = null) => {
9409
9397
  const envVarsCheck = auth ? null : validateEnvironmentVars();
9410
9398
  const cacheKey = generateCacheKey(auth, envVarsCheck);
9411
9399
  if (s3ClientCache.has(cacheKey)) {
9412
- const cached = s3ClientCache.get(cacheKey);
9413
- if (cached)
9414
- return cached;
9400
+ return s3ClientCache.get(cacheKey);
9415
9401
  }
9416
9402
  // Create new client and cache it
9417
9403
  const client = initialiseAWSClient(clientS3.S3Client, auth);
@@ -9423,9 +9409,7 @@ const initialiseLambdaClient = (auth = null) => {
9423
9409
  const envVarsCheck = auth ? null : validateEnvironmentVars();
9424
9410
  const cacheKey = generateCacheKey(auth, envVarsCheck);
9425
9411
  if (lambdaClientCache.has(cacheKey)) {
9426
- const cached = lambdaClientCache.get(cacheKey);
9427
- if (cached)
9428
- return cached;
9412
+ return lambdaClientCache.get(cacheKey);
9429
9413
  }
9430
9414
  // Create new client and cache it
9431
9415
  const client = initialiseAWSClient(clientLambda.Lambda, auth);
@@ -9925,17 +9909,15 @@ const isRetryableS3Error = (error) => {
9925
9909
  if (error instanceof Error) {
9926
9910
  const message = error.message;
9927
9911
  // Retry on throttling
9928
- if (message.includes("SlowDown") || message.includes("RequestTimeout")) {
9912
+ if (message.includes('SlowDown') || message.includes('RequestTimeout')) {
9929
9913
  return true;
9930
9914
  }
9931
9915
  // Retry on 5xx server errors
9932
- if (message.includes("InternalError") ||
9933
- message.includes("ServiceUnavailable")) {
9916
+ if (message.includes('InternalError') || message.includes('ServiceUnavailable')) {
9934
9917
  return true;
9935
9918
  }
9936
9919
  // Retry on connection errors
9937
- if (message.includes("RequestTimeTooSkewed") ||
9938
- message.includes("NetworkingError")) {
9920
+ if (message.includes('RequestTimeTooSkewed') || message.includes('NetworkingError')) {
9939
9921
  return true;
9940
9922
  }
9941
9923
  }
@@ -9949,11 +9931,10 @@ const generateRandomHash = (length) => {
9949
9931
  };
9950
9932
  function generateDateTimeStamp() {
9951
9933
  const now = new Date();
9952
- return now
9953
- .toISOString()
9954
- .replace(/T/, "-")
9955
- .replace(/\..+/, "")
9956
- .replace(/:/g, "-");
9934
+ return now.toISOString()
9935
+ .replace(/T/, '-')
9936
+ .replace(/\..+/, '')
9937
+ .replace(/:/g, '-');
9957
9938
  }
9958
9939
  const isValidBucketName = (name) => {
9959
9940
  const regex = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
@@ -9984,7 +9965,7 @@ async function uploadFile(s3Client, bucketName, filePath, destinationPath, isPub
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,
@@ -10004,7 +9985,7 @@ async function uploadDirectory(s3Client, bucketName, dirPath, destinationPath, i
10004
9985
  Bucket: bucketName,
10005
9986
  Key: s3Key,
10006
9987
  Body: fileContent,
10007
- ACL: isPublic ? "public-read" : undefined,
9988
+ ACL: isPublic ? 'public-read' : undefined
10008
9989
  });
10009
9990
  await withRetry(() => s3Client.send(command), {
10010
9991
  maxRetries: 3,
@@ -10020,7 +10001,7 @@ async function zipDirectory(sourceDir, outPath) {
10020
10001
  const zip = new AdmZip();
10021
10002
  let fileCount = 0;
10022
10003
  try {
10023
- async function addFilesToZip(currentPath, relativePath = "") {
10004
+ async function addFilesToZip(currentPath, relativePath = '') {
10024
10005
  const files = await fs$1.readdir(currentPath, { withFileTypes: true });
10025
10006
  for (const file of files) {
10026
10007
  const filePath = path$1.join(currentPath, file.name);
@@ -10063,33 +10044,27 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
10063
10044
  Prefix: s3Path,
10064
10045
  ContinuationToken: continuationToken,
10065
10046
  });
10066
- const listResponse = (await withRetry(() => s3Client.send(listCommand), {
10047
+ const listResponse = await withRetry(() => s3Client.send(listCommand), {
10067
10048
  maxRetries: 3,
10068
10049
  baseDelayMs: 1000,
10069
10050
  maxDelayMs: 15000,
10070
10051
  retryableErrors: isRetryableS3Error,
10071
- }, `S3:ListObjects:${bucketName}`));
10052
+ }, `S3:ListObjects:${bucketName}`);
10072
10053
  const objects = listResponse.Contents || [];
10073
10054
  if (objects.length === 0) {
10074
10055
  return { fileCount: 0, folderCount: 0, totalSize: 0, fileList: [] };
10075
10056
  }
10076
- if (objects.length === 1 && objects[0].Key?.endsWith(".zip")) {
10057
+ if (objects.length === 1 && objects[0].Key?.endsWith('.zip')) {
10077
10058
  // It's a zip file, download and extract it
10078
10059
  const zipPath = path$1.join(localPath, path$1.basename(objects[0].Key));
10079
10060
  await downloadFile(s3Client, bucketName, objects[0].Key, zipPath);
10080
- const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList, } = await unzipFile(zipPath, localPath);
10061
+ const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList } = await unzipFile(zipPath, localPath);
10081
10062
  await fs$1.unlink(zipPath);
10082
- return {
10083
- fileCount: totalFiles,
10084
- folderCount: totalFolders,
10085
- totalSize: extractedSize,
10086
- fileList: extractedFileList,
10087
- };
10063
+ return { fileCount: totalFiles, folderCount: totalFolders, totalSize: extractedSize, fileList: extractedFileList };
10088
10064
  }
10089
10065
  else {
10090
10066
  // Non-zip files. Download files in batches.
10091
- for (let i = 0; i < objects.length; i += 1000) {
10092
- // AWS allows up to 1000 per request
10067
+ for (let i = 0; i < objects.length; i += 1000) { // AWS allows up to 1000 per request
10093
10068
  const batch = objects.slice(i, i + 1000);
10094
10069
  await Promise.all(batch.map(async (obj) => {
10095
10070
  if (!obj.Key)
@@ -10105,9 +10080,7 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
10105
10080
  }
10106
10081
  // Count folders
10107
10082
  const relativePath = path$1.relative(localPath, path$1.dirname(localFilePath));
10108
- if (relativePath &&
10109
- !relativePath.startsWith("..") &&
10110
- relativePath !== ".") {
10083
+ if (relativePath && !relativePath.startsWith('..') && relativePath !== '.') {
10111
10084
  folderCount++;
10112
10085
  }
10113
10086
  }));
@@ -10128,7 +10101,7 @@ async function downloadFile(s3Client, bucketName, s3Key, localFilePath) {
10128
10101
  await withRetry(async () => {
10129
10102
  const { Body } = await s3Client.send(getCommand);
10130
10103
  if (!Body)
10131
- throw new Error("No body returned from S3");
10104
+ throw new Error('No body returned from S3');
10132
10105
  const writeStream = fs.createWriteStream(localFilePath);
10133
10106
  await promises.pipeline(Body, writeStream);
10134
10107
  }, {
@@ -10184,19 +10157,17 @@ async function emptyBucket(s3Client, bucketName) {
10184
10157
  Bucket: bucketName,
10185
10158
  ContinuationToken: continuationToken,
10186
10159
  });
10187
- const response = (await withRetry(() => s3Client.send(listCommand), {
10160
+ const response = await withRetry(() => s3Client.send(listCommand), {
10188
10161
  maxRetries: 3,
10189
10162
  baseDelayMs: 1000,
10190
10163
  maxDelayMs: 15000,
10191
10164
  retryableErrors: isRetryableS3Error,
10192
- }, `S3:ListObjects:${bucketName}`));
10165
+ }, `S3:ListObjects:${bucketName}`);
10193
10166
  const { Contents, IsTruncated, NextContinuationToken } = response;
10194
10167
  if (Contents && Contents.length > 0) {
10195
10168
  const deleteCommand = new clientS3.DeleteObjectsCommand({
10196
10169
  Bucket: bucketName,
10197
- Delete: {
10198
- Objects: Contents.filter((c) => c.Key).map((c) => ({ Key: c.Key })),
10199
- },
10170
+ Delete: { Objects: Contents.map(({ Key }) => ({ Key: Key })) },
10200
10171
  });
10201
10172
  await withRetry(() => s3Client.send(deleteCommand), {
10202
10173
  maxRetries: 3,
@@ -23104,11 +23075,11 @@ let poolConfig = DEFAULT_POOL_CONFIG;
23104
23075
  async function loadApolloModules() {
23105
23076
  if (typeof window === "undefined" || process.env.AWS_EXECUTION_ENV) {
23106
23077
  // Server-side (or Lambda): load the CommonJS‑based implementation.
23107
- return (await Promise.resolve().then(function () { return require('./apollo-client.server-DB3jLbBx.js'); }));
23078
+ return (await Promise.resolve().then(function () { return require('./apollo-client.server-Cx30LaNh.js'); }));
23108
23079
  }
23109
23080
  else {
23110
23081
  // Client-side: load the ESM‑based implementation.
23111
- return (await Promise.resolve().then(function () { return require('./apollo-client.client-CyxU1hTu.js'); }));
23082
+ return (await Promise.resolve().then(function () { return require('./apollo-client.client-D6jG5B1n.js'); }));
23112
23083
  }
23113
23084
  }
23114
23085
  /**
@@ -79135,7 +79106,7 @@ const RETRY_CONFIG = {
79135
79106
  BASE_DELAY: 2000, // Increased base delay to 2 seconds
79136
79107
  MAX_DELAY: 64000,
79137
79108
  MAX_RETRIES: 5,
79138
- MAX_RANDOM_DELAY: 1000,
79109
+ MAX_RANDOM_DELAY: 1000
79139
79110
  };
79140
79111
  /**
79141
79112
  * Determines if an error is related to Google Sheets API quotas or rate limits.
@@ -79161,10 +79132,10 @@ const RETRY_CONFIG = {
79161
79132
  */
79162
79133
  function isQuotaError(error) {
79163
79134
  const message = error.message.toLowerCase();
79164
- return (message.includes("quota exceeded") ||
79165
- message.includes("rate limit") ||
79166
- message.includes("429") ||
79167
- message.includes("too many requests"));
79135
+ return message.includes('quota exceeded') ||
79136
+ message.includes('rate limit') ||
79137
+ message.includes('429') ||
79138
+ message.includes('too many requests');
79168
79139
  }
79169
79140
  /**
79170
79141
  * Implements exponential backoff retry mechanism for Google Sheets API calls.
@@ -79223,14 +79194,14 @@ async function withExponentialBackoff(operation) {
79223
79194
  const exponentialDelay = Math.min(Math.pow(2, retries) * RETRY_CONFIG.BASE_DELAY + randomDelay, RETRY_CONFIG.MAX_DELAY);
79224
79195
  logIfDebug(`Quota/Rate limit exceeded. Error: ${error.message}`);
79225
79196
  logIfDebug(`Retrying in ${exponentialDelay}ms (attempt ${retries}/${RETRY_CONFIG.MAX_RETRIES})`);
79226
- await new Promise((resolve) => setTimeout(resolve, exponentialDelay));
79197
+ await new Promise(resolve => setTimeout(resolve, exponentialDelay));
79227
79198
  }
79228
79199
  }
79229
79200
  }
79230
79201
  function checkEnvVars() {
79231
79202
  const secrets = getSecrets();
79232
79203
  if (!secrets.googleSheets.clientEmail || !secrets.googleSheets.privateKey) {
79233
- throw new Error("GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.");
79204
+ throw new Error('GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.');
79234
79205
  }
79235
79206
  }
79236
79207
  /**
@@ -79245,16 +79216,16 @@ async function getAuthClient() {
79245
79216
  const secrets = getSecrets();
79246
79217
  const credentials = {
79247
79218
  client_email: secrets.googleSheets.clientEmail,
79248
- private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, "\n"),
79219
+ private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, '\n'),
79249
79220
  };
79250
79221
  const auth = new require$$0$6.GoogleAuth({
79251
79222
  credentials,
79252
- scopes: ["https://www.googleapis.com/auth/spreadsheets"],
79223
+ scopes: ['https://www.googleapis.com/auth/spreadsheets'],
79253
79224
  });
79254
79225
  return auth.getClient();
79255
79226
  }
79256
79227
  catch (error) {
79257
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79228
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79258
79229
  logIfDebug(`Error initializing Google Sheets auth: ${errorMessage}`);
79259
79230
  throw new Error(`Failed to initialize Google Sheets auth: ${errorMessage}`);
79260
79231
  }
@@ -79266,7 +79237,7 @@ async function getSheetsClient() {
79266
79237
  const auth = await getAuthClient();
79267
79238
  return new buildExports.sheets_v4.Sheets({
79268
79239
  auth,
79269
- timeout: GOOGLE_SHEETS_TIMEOUT_MS,
79240
+ timeout: GOOGLE_SHEETS_TIMEOUT_MS
79270
79241
  });
79271
79242
  }
79272
79243
  /**
@@ -79299,7 +79270,7 @@ function escapeSheetName(sheetName) {
79299
79270
  * @param startColumn Optional starting column (defaults to 'A')
79300
79271
  * @returns Promise resolving to response with success status and row details
79301
79272
  */
79302
- async function addRow(config, values, startColumn = "A") {
79273
+ async function addRow(config, values, startColumn = 'A') {
79303
79274
  try {
79304
79275
  const sheets = await getSheetsClient();
79305
79276
  const escapedSheetName = escapeSheetName(config.sheetName);
@@ -79307,7 +79278,7 @@ async function addRow(config, values, startColumn = "A") {
79307
79278
  const response = await withExponentialBackoff(() => sheets.spreadsheets.values.append({
79308
79279
  spreadsheetId: config.spreadsheetId,
79309
79280
  range,
79310
- valueInputOption: "USER_ENTERED",
79281
+ valueInputOption: 'USER_ENTERED',
79311
79282
  requestBody: {
79312
79283
  values: [values],
79313
79284
  },
@@ -79315,9 +79286,9 @@ async function addRow(config, values, startColumn = "A") {
79315
79286
  //logIfDebug(`Added row to sheet ${config.sheetName}: ${values.join(', ')}`);
79316
79287
  const rowNumber = response.data.updates?.updatedRange
79317
79288
  ? parseInt(response.data.updates.updatedRange
79318
- .split("!")[1]
79319
- .split(":")[0]
79320
- .replace(/[^0-9]/g, ""))
79289
+ .split('!')[1]
79290
+ .split(':')[0]
79291
+ .replace(/[^0-9]/g, ''))
79321
79292
  : 0;
79322
79293
  return {
79323
79294
  success: true,
@@ -79328,7 +79299,7 @@ async function addRow(config, values, startColumn = "A") {
79328
79299
  };
79329
79300
  }
79330
79301
  catch (error) {
79331
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79302
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79332
79303
  logIfDebug(`Error adding row to sheet: ${errorMessage}`);
79333
79304
  return {
79334
79305
  success: false,
@@ -79370,7 +79341,7 @@ async function addSheet(spreadsheetId, sheetTitle) {
79370
79341
  };
79371
79342
  }
79372
79343
  catch (error) {
79373
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79344
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79374
79345
  logIfDebug(`Error adding sheet: ${errorMessage}`);
79375
79346
  return {
79376
79347
  success: false,
@@ -79394,7 +79365,7 @@ async function writeCell(config, cell, value) {
79394
79365
  await withExponentialBackoff(() => sheets.spreadsheets.values.update({
79395
79366
  spreadsheetId: config.spreadsheetId,
79396
79367
  range,
79397
- valueInputOption: "USER_ENTERED",
79368
+ valueInputOption: 'USER_ENTERED',
79398
79369
  requestBody: {
79399
79370
  values: [[value]],
79400
79371
  },
@@ -79408,7 +79379,7 @@ async function writeCell(config, cell, value) {
79408
79379
  };
79409
79380
  }
79410
79381
  catch (error) {
79411
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79382
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79412
79383
  logIfDebug(`Error writing to cell: ${errorMessage}`);
79413
79384
  return {
79414
79385
  success: false,
@@ -79432,11 +79403,8 @@ async function getCell(config, cell) {
79432
79403
  spreadsheetId: config.spreadsheetId,
79433
79404
  range,
79434
79405
  }));
79435
- const rawValue = response.data.values?.[0]?.[0];
79436
- const value = rawValue === undefined || rawValue === null
79437
- ? null
79438
- : rawValue;
79439
- logIfDebug(`Read value from cell ${range}: ${String(value)}`);
79406
+ const value = response.data.values?.[0]?.[0];
79407
+ logIfDebug(`Read value from cell ${range}: ${value}`);
79440
79408
  return {
79441
79409
  success: true,
79442
79410
  data: {
@@ -79445,7 +79413,7 @@ async function getCell(config, cell) {
79445
79413
  };
79446
79414
  }
79447
79415
  catch (error) {
79448
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79416
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79449
79417
  logIfDebug(`Error reading from cell: ${errorMessage}`);
79450
79418
  return {
79451
79419
  success: false,
@@ -79478,11 +79446,10 @@ function convertA1ToRowCol(a1Notation) {
79478
79446
  * @returns Padded array of arrays
79479
79447
  */
79480
79448
  function padArrays(arrays) {
79481
- const maxLength = Math.max(...arrays.map((arr) => arr.length));
79482
- return arrays.map((arr) => {
79449
+ const maxLength = Math.max(...arrays.map(arr => arr.length));
79450
+ return arrays.map(arr => {
79483
79451
  if (arr.length < maxLength) {
79484
- const padding = Array(maxLength - arr.length).fill("");
79485
- return [...arr, ...padding];
79452
+ return [...arr, ...Array(maxLength - arr.length).fill('')];
79486
79453
  }
79487
79454
  return arr;
79488
79455
  });
@@ -79494,10 +79461,10 @@ function padArrays(arrays) {
79494
79461
  * @param startCell Optional starting cell in A1 notation (defaults to 'A1')
79495
79462
  * @returns Promise resolving to response with success status and update details
79496
79463
  */
79497
- async function writeArray(config, data, startCell = "A1") {
79464
+ async function writeArray(config, data, startCell = 'A1') {
79498
79465
  try {
79499
79466
  if (!data.length) {
79500
- throw new Error("Data array is empty");
79467
+ throw new Error('Data array is empty');
79501
79468
  }
79502
79469
  const sheets = await getSheetsClient();
79503
79470
  const { row: startRow, column: startCol } = convertA1ToRowCol(startCell);
@@ -79513,7 +79480,7 @@ async function writeArray(config, data, startCell = "A1") {
79513
79480
  const response = await withExponentialBackoff(() => sheets.spreadsheets.values.update({
79514
79481
  spreadsheetId: config.spreadsheetId,
79515
79482
  range,
79516
- valueInputOption: "USER_ENTERED",
79483
+ valueInputOption: 'USER_ENTERED',
79517
79484
  requestBody: {
79518
79485
  values: paddedData,
79519
79486
  },
@@ -79529,7 +79496,7 @@ async function writeArray(config, data, startCell = "A1") {
79529
79496
  };
79530
79497
  }
79531
79498
  catch (error) {
79532
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79499
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79533
79500
  logIfDebug(`Error writing array to sheet: ${errorMessage}`);
79534
79501
  return {
79535
79502
  success: false,
@@ -81823,4 +81790,4 @@ exports.withCorrelationId = withCorrelationId;
81823
81790
  exports.withMetrics = withMetrics;
81824
81791
  exports.withRateLimit = withRateLimit;
81825
81792
  exports.withRetry = withRetry;
81826
- //# sourceMappingURL=index-KzQOh2uu.js.map
81793
+ //# sourceMappingURL=index-ChygDqeG.js.map