@defai.digital/automatosx 11.2.7 → 11.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![Windows](https://img.shields.io/badge/Windows-10+-blue.svg)](https://www.microsoft.com/windows)
14
14
  [![Ubuntu](https://img.shields.io/badge/Ubuntu-24.04-blue.svg)](https://ubuntu.com)
15
15
 
16
- **Status**: ✅ **Production Ready** | v11.2.7 | Unified Event System | MCP Streaming
16
+ **Status**: ✅ **Production Ready** | v11.2.9 | Performance Refactors | Array Optimization
17
17
 
18
18
  > 🎯 **What AutomatosX Does**: Adds 20+ specialized agents, persistent memory, workflow automation, and 80% cost savings to Claude Code/Codex - **without changing how you work**.
19
19
 
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import chalk5 from 'chalk';
15
15
  import ora8 from 'ora';
16
16
  import * as readline2 from 'readline';
17
17
  import readline2__default, { createInterface } from 'readline';
18
+ import { Mutex } from 'async-mutex';
18
19
  import { promisify } from 'util';
19
20
  import yargs from 'yargs';
20
21
  import { hideBin } from 'yargs/helpers';
@@ -22,7 +23,6 @@ import { randomUUID, createHash } from 'crypto';
22
23
  import * as yaml4 from 'js-yaml';
23
24
  import yaml4__default, { load, dump } from 'js-yaml';
24
25
  import Table from 'cli-table3';
25
- import { Mutex } from 'async-mutex';
26
26
  import inquirer from 'inquirer';
27
27
  import Ajv from 'ajv';
28
28
  import addFormats from 'ajv-formats';
@@ -2535,6 +2535,15 @@ var init_base_provider = __esm({
2535
2535
  "test-provider"
2536
2536
  // For unit tests
2537
2537
  ];
2538
+ /** Environment variables to force non-interactive CLI mode */
2539
+ static NON_INTERACTIVE_ENV = {
2540
+ TERM: "dumb",
2541
+ NO_COLOR: "1",
2542
+ FORCE_COLOR: "0",
2543
+ CI: "true",
2544
+ NO_UPDATE_NOTIFIER: "1",
2545
+ DEBIAN_FRONTEND: "noninteractive"
2546
+ };
2538
2547
  config;
2539
2548
  logger = logger;
2540
2549
  health;
@@ -2627,19 +2636,7 @@ var init_base_provider = __esm({
2627
2636
  shell: true,
2628
2637
  // Auto-detects: cmd.exe on Windows, /bin/sh on Unix
2629
2638
  timeout: this.config.timeout || 12e4,
2630
- env: {
2631
- ...process.env,
2632
- // Force non-interactive mode for CLIs
2633
- TERM: "dumb",
2634
- NO_COLOR: "1",
2635
- // Disable TTY checks for codex and other CLIs
2636
- FORCE_COLOR: "0",
2637
- CI: "true",
2638
- // Many CLIs disable TTY checks in CI mode
2639
- NO_UPDATE_NOTIFIER: "1",
2640
- // Disable interactive prompts
2641
- DEBIAN_FRONTEND: "noninteractive"
2642
- }
2639
+ env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
2643
2640
  });
2644
2641
  let stdout = "";
2645
2642
  let stderr = "";
@@ -2789,22 +2786,14 @@ var init_base_provider = __esm({
2789
2786
  });
2790
2787
  const child = spawn(cliCommand2, commandArgs, {
2791
2788
  timeout: this.config.timeout || 12e4,
2792
- env: {
2793
- ...process.env,
2794
- // Force non-interactive mode for CLIs
2795
- TERM: "dumb",
2796
- NO_COLOR: "1",
2797
- FORCE_COLOR: "0",
2798
- CI: "true",
2799
- NO_UPDATE_NOTIFIER: "1",
2800
- DEBIAN_FRONTEND: "noninteractive"
2801
- }
2789
+ env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
2802
2790
  });
2803
2791
  let stdout = "";
2804
2792
  let stderr = "";
2805
2793
  let timeoutId = null;
2806
2794
  let forceKillTimer = null;
2807
2795
  let readlineInterface = null;
2796
+ let stderrInterface = null;
2808
2797
  const streamingEnabled = process.env.AUTOMATOSX_SHOW_PROVIDER_OUTPUT === "true";
2809
2798
  const debugMode = process.env.AUTOMATOSX_DEBUG === "true";
2810
2799
  const verbosity = VerbosityManager.getInstance();
@@ -2849,7 +2838,7 @@ var init_base_provider = __esm({
2849
2838
  });
2850
2839
  }
2851
2840
  if (child.stderr) {
2852
- const stderrInterface = readline2__default.createInterface({
2841
+ stderrInterface = readline2__default.createInterface({
2853
2842
  input: child.stderr,
2854
2843
  crlfDelay: Infinity
2855
2844
  });
@@ -2865,6 +2854,13 @@ var init_base_provider = __esm({
2865
2854
  }
2866
2855
  }
2867
2856
  });
2857
+ stderrInterface.on("error", (error) => {
2858
+ if (error.message !== "Readable stream already read") {
2859
+ logger.debug("Stderr readline error (non-fatal)", {
2860
+ error: error.message
2861
+ });
2862
+ }
2863
+ });
2868
2864
  }
2869
2865
  const cleanup = () => {
2870
2866
  if (timeoutId) {
@@ -2883,22 +2879,35 @@ var init_base_provider = __esm({
2883
2879
  readlineInterface = null;
2884
2880
  }
2885
2881
  }
2882
+ if (stderrInterface) {
2883
+ try {
2884
+ stderrInterface.close();
2885
+ } catch (error) {
2886
+ } finally {
2887
+ stderrInterface = null;
2888
+ }
2889
+ }
2886
2890
  };
2887
2891
  child.on("close", (code, signal) => {
2888
2892
  cleanup();
2889
2893
  if (stderr) {
2890
2894
  logger.debug(`${cliCommand2} CLI stderr output`, { stderr: stderr.trim() });
2891
2895
  }
2892
- if (code === 0) {
2896
+ if ((code === 0 || code === null) && !signal) {
2893
2897
  if (progressParser) {
2894
2898
  progressParser.succeed(`${cliCommand2} completed successfully`);
2895
2899
  }
2896
2900
  resolve13({ stdout, stderr });
2901
+ } else if (signal) {
2902
+ if (progressParser) {
2903
+ progressParser.fail(`${cliCommand2} killed by signal ${signal}`);
2904
+ }
2905
+ reject(new Error(`${cliCommand2} CLI killed by signal ${signal}. stderr: ${stderr || "none"}`));
2897
2906
  } else {
2898
2907
  if (progressParser) {
2899
2908
  progressParser.fail(`${cliCommand2} failed with code ${code}`);
2900
2909
  }
2901
- reject(new Error(`${cliCommand2} CLI exited with code ${code}${signal ? ` (signal: ${signal})` : ""}. stderr: ${stderr || "none"}`));
2910
+ reject(new Error(`${cliCommand2} CLI exited with code ${code}. stderr: ${stderr || "none"}`));
2902
2911
  }
2903
2912
  });
2904
2913
  child.on("error", (error) => {
@@ -3854,8 +3863,6 @@ var init_cli_wrapper = __esm({
3854
3863
  };
3855
3864
  }
3856
3865
  });
3857
-
3858
- // src/integrations/openai-codex/sdk-adapter.ts
3859
3866
  var CodexSdkAdapter;
3860
3867
  var init_sdk_adapter = __esm({
3861
3868
  "src/integrations/openai-codex/sdk-adapter.ts"() {
@@ -3868,6 +3875,8 @@ var init_sdk_adapter = __esm({
3868
3875
  sdkModule = null;
3869
3876
  options;
3870
3877
  initialized = false;
3878
+ initMutex = new Mutex();
3879
+ // v11.2.8: Prevent race condition in ensureInitialized
3871
3880
  constructor(options = {}) {
3872
3881
  this.options = {
3873
3882
  streamingEnabled: true,
@@ -3962,18 +3971,20 @@ var init_sdk_adapter = __esm({
3962
3971
  return this.activeThread;
3963
3972
  }
3964
3973
  async ensureInitialized() {
3965
- if (this.initialized) return;
3966
- try {
3967
- this.sdkModule = await import('@openai/codex-sdk');
3968
- this.codex = new this.sdkModule.Codex();
3969
- this.initialized = true;
3970
- logger.info("Codex SDK initialized");
3971
- } catch (error) {
3972
- throw new CodexError(
3973
- "CLI_NOT_FOUND" /* CLI_NOT_FOUND */,
3974
- "Codex SDK not available. Install with: npm install @openai/codex-sdk"
3975
- );
3976
- }
3974
+ return this.initMutex.runExclusive(async () => {
3975
+ if (this.initialized) return;
3976
+ try {
3977
+ this.sdkModule = await import('@openai/codex-sdk');
3978
+ this.codex = new this.sdkModule.Codex();
3979
+ this.initialized = true;
3980
+ logger.info("Codex SDK initialized");
3981
+ } catch (error) {
3982
+ throw new CodexError(
3983
+ "CLI_NOT_FOUND" /* CLI_NOT_FOUND */,
3984
+ "Codex SDK not available. Install with: npm install @openai/codex-sdk"
3985
+ );
3986
+ }
3987
+ });
3977
3988
  }
3978
3989
  async destroy() {
3979
3990
  this.activeThread = null;
@@ -4379,6 +4390,9 @@ var init_command_builder = __esm({
4379
4390
  * @returns Environment variable object
4380
4391
  */
4381
4392
  buildEnv(options) {
4393
+ if (!options.apiKey && !options.baseUrl && !options.model) {
4394
+ return null;
4395
+ }
4382
4396
  const env = {};
4383
4397
  if (options.apiKey) {
4384
4398
  env.YOUR_API_KEY = options.apiKey;
@@ -4426,6 +4440,27 @@ var init_response_parser = __esm({
4426
4440
  AxCliResponseParser = class _AxCliResponseParser {
4427
4441
  /** Maximum number of assistant messages to prevent memory exhaustion */
4428
4442
  static MAX_ASSISTANT_MESSAGES = 1e3;
4443
+ /** Pre-compiled regex patterns for error detection (avoids recompilation per call) */
4444
+ static ERROR_WITH_COLON_REGEX = /Sorry, I encountered an error: (.+?)$/;
4445
+ static ERROR_WITHOUT_COLON_REGEX = /Sorry, I encountered an error(.*?)$/;
4446
+ /**
4447
+ * Extract error message from content if present
4448
+ * @returns Error message or null if no error
4449
+ */
4450
+ extractErrorFromContent(content) {
4451
+ if (!content.includes("Sorry, I encountered an error")) {
4452
+ return null;
4453
+ }
4454
+ const matchWithColon = content.match(_AxCliResponseParser.ERROR_WITH_COLON_REGEX);
4455
+ if (matchWithColon?.[1]) {
4456
+ return matchWithColon[1];
4457
+ }
4458
+ const matchWithoutColon = content.match(_AxCliResponseParser.ERROR_WITHOUT_COLON_REGEX);
4459
+ if (matchWithoutColon) {
4460
+ return matchWithoutColon[1] ? matchWithoutColon[1].trim() : "API error";
4461
+ }
4462
+ return "API error";
4463
+ }
4429
4464
  /**
4430
4465
  * Parse JSONL output from ax-cli into ProviderResponse
4431
4466
  *
@@ -4450,17 +4485,9 @@ var init_response_parser = __esm({
4450
4485
  const parsed = JSON.parse(line);
4451
4486
  const validated = AxCliMessageSchema.parse(parsed);
4452
4487
  if (validated.role === "assistant") {
4453
- if (validated.content.includes("Sorry, I encountered an error")) {
4454
- const matchWithColon = validated.content.match(/Sorry, I encountered an error: (.+?)$/);
4455
- if (matchWithColon) {
4456
- throw new Error(`ax-cli error: ${matchWithColon[1]}`);
4457
- }
4458
- const matchWithoutColon = validated.content.match(/Sorry, I encountered an error(.*?)$/);
4459
- if (matchWithoutColon) {
4460
- const errorMsg = matchWithoutColon[1] ? matchWithoutColon[1].trim() : "API error";
4461
- throw new Error(`ax-cli error: ${errorMsg}`);
4462
- }
4463
- throw new Error("ax-cli error: API error");
4488
+ const errorMsg = this.extractErrorFromContent(validated.content);
4489
+ if (errorMsg) {
4490
+ throw new Error(`ax-cli error: ${errorMsg}`);
4464
4491
  }
4465
4492
  if (assistantMessages.length >= _AxCliResponseParser.MAX_ASSISTANT_MESSAGES) {
4466
4493
  throw new Error(
@@ -4538,16 +4565,9 @@ var init_response_parser = __esm({
4538
4565
  try {
4539
4566
  const parsed = JSON.parse(line);
4540
4567
  if (parsed.role === "assistant" && parsed.content) {
4541
- if (parsed.content.includes("Sorry, I encountered an error")) {
4542
- const matchWithColon = parsed.content.match(/Sorry, I encountered an error: (.+?)$/);
4543
- if (matchWithColon) {
4544
- return matchWithColon[1];
4545
- }
4546
- const matchWithoutColon = parsed.content.match(/Sorry, I encountered an error(.*?)$/);
4547
- if (matchWithoutColon) {
4548
- return matchWithoutColon[1] ? matchWithoutColon[1].trim() : "Sorry, I encountered an error";
4549
- }
4550
- return parsed.content;
4568
+ const errorMsg = this.extractErrorFromContent(parsed.content);
4569
+ if (errorMsg) {
4570
+ return errorMsg;
4551
4571
  }
4552
4572
  }
4553
4573
  } catch {
@@ -4605,7 +4625,7 @@ var init_adapter = __esm({
4605
4625
  // 10MB
4606
4626
  env: {
4607
4627
  ...process.env,
4608
- ...this.commandBuilder.buildEnv(options)
4628
+ ...this.commandBuilder.buildEnv(options) ?? {}
4609
4629
  }
4610
4630
  });
4611
4631
  if (stderr && stderr.trim().length > 0) {
@@ -4824,11 +4844,12 @@ var init_token_estimator = __esm({
4824
4844
  });
4825
4845
 
4826
4846
  // src/integrations/ax-cli-sdk/subagent-adapter.ts
4827
- var SubagentAdapter;
4847
+ var TOKEN_SPLIT, SubagentAdapter;
4828
4848
  var init_subagent_adapter = __esm({
4829
4849
  "src/integrations/ax-cli-sdk/subagent-adapter.ts"() {
4830
4850
  init_esm_shims();
4831
4851
  init_logger();
4852
+ TOKEN_SPLIT = { PROMPT: 0.3, COMPLETION: 0.7 };
4832
4853
  SubagentAdapter = class {
4833
4854
  orchestrator = null;
4834
4855
  subagents = /* @__PURE__ */ new Map();
@@ -5033,9 +5054,8 @@ ${task.task}` : task.task;
5033
5054
  success: true,
5034
5055
  latencyMs,
5035
5056
  tokensUsed: totalTokens > 0 ? {
5036
- prompt: Math.floor(totalTokens * 0.3),
5037
- // Estimated split
5038
- completion: Math.floor(totalTokens * 0.7),
5057
+ prompt: Math.floor(totalTokens * TOKEN_SPLIT.PROMPT),
5058
+ completion: Math.floor(totalTokens * TOKEN_SPLIT.COMPLETION),
5039
5059
  total: totalTokens
5040
5060
  } : void 0
5041
5061
  };
@@ -5198,7 +5218,10 @@ ${task.task}` : task.task;
5198
5218
  for (const [key, subagent] of this.subagents) {
5199
5219
  try {
5200
5220
  if (subagent.dispose) {
5201
- subagent.dispose();
5221
+ const result = subagent.dispose();
5222
+ if (result instanceof Promise) {
5223
+ await result;
5224
+ }
5202
5225
  }
5203
5226
  logger.debug("Subagent disposed", { key });
5204
5227
  } catch (error) {
@@ -6354,7 +6377,7 @@ var init_adapter2 = __esm({
6354
6377
  init_instructions_bridge();
6355
6378
  init_mcp_manager();
6356
6379
  init_mcp_manager();
6357
- AxCliSdkAdapter = class {
6380
+ AxCliSdkAdapter = class _AxCliSdkAdapter {
6358
6381
  agent = null;
6359
6382
  // Will type as LLMAgent after import
6360
6383
  agentConfig = null;
@@ -6565,7 +6588,7 @@ var init_adapter2 = __esm({
6565
6588
  if (!await this.ensureSDKAvailable()) {
6566
6589
  throw new Error("ax-cli SDK not available. Install with: npm install @defai.digital/ax-cli");
6567
6590
  }
6568
- if (!this.agent || await this.hasConfigChanged(options)) {
6591
+ if (!this.agent || this.hasConfigChanged(options)) {
6569
6592
  await this.initializeAgent(options);
6570
6593
  }
6571
6594
  logger.debug("Executing via SDK (streaming mode for token tracking)", {
@@ -6712,8 +6735,9 @@ var init_adapter2 = __esm({
6712
6735
  * Check if agent config has changed (requires new agent)
6713
6736
  *
6714
6737
  * Since SDK manages credentials via settings, we only track maxToolRounds
6738
+ * v11.2.8: Removed unnecessary async (no async operations)
6715
6739
  */
6716
- async hasConfigChanged(options) {
6740
+ hasConfigChanged(options) {
6717
6741
  if (!this.agentConfig) return true;
6718
6742
  const changed = this.agentConfig.maxToolRounds !== (options.maxToolRounds || 400);
6719
6743
  if (changed) {
@@ -6797,6 +6821,49 @@ var init_adapter2 = __esm({
6797
6821
  });
6798
6822
  }
6799
6823
  }
6824
+ /**
6825
+ * Extract token usage from various sources with priority:
6826
+ * 1. SDK events (token_count emissions) - most accurate
6827
+ * 2. Usage object (ChatEntry.usage or response.usage)
6828
+ * 3. Estimation fallback
6829
+ */
6830
+ extractTokenUsage(prompt, content, totalTokensFromEvents, usageObject, sourceName) {
6831
+ if (totalTokensFromEvents > 0) {
6832
+ const estimated2 = TokenEstimator.estimateUsage(prompt, content);
6833
+ const tokens = {
6834
+ prompt: estimated2.prompt,
6835
+ completion: estimated2.completion,
6836
+ total: totalTokensFromEvents
6837
+ };
6838
+ logger.info(`Using token count from SDK events${sourceName ? ` (${sourceName})` : ""}`, {
6839
+ tokens: TokenEstimator.format(tokens),
6840
+ accuracy: "100% (total), 80-90% (split)",
6841
+ source: "token_count events"
6842
+ });
6843
+ return tokens;
6844
+ }
6845
+ const usage = usageObject || {};
6846
+ const actualTokens = {
6847
+ prompt: usage.prompt_tokens || usage.prompt || usage.input || usage.promptTokens || 0,
6848
+ completion: usage.completion_tokens || usage.completion || usage.output || usage.completionTokens || 0,
6849
+ total: usage.total_tokens || usage.total || usage.totalTokens || 0
6850
+ };
6851
+ if (actualTokens.total > 0) {
6852
+ logger.info(`Using token counts from ${sourceName || "usage object"}`, {
6853
+ tokens: TokenEstimator.format(actualTokens),
6854
+ accuracy: "100%",
6855
+ source: sourceName || "usage"
6856
+ });
6857
+ return actualTokens;
6858
+ }
6859
+ const estimated = TokenEstimator.estimateUsage(prompt, content);
6860
+ logger.warn(`SDK did not provide token counts${sourceName ? ` (${sourceName})` : ""}, using estimation`, {
6861
+ tokens: TokenEstimator.format(estimated, true),
6862
+ accuracy: "80-90% (estimated)",
6863
+ source: "estimation"
6864
+ });
6865
+ return estimated;
6866
+ }
6800
6867
  /**
6801
6868
  * Convert SDK response to ExecutionResponse format
6802
6869
  *
@@ -6827,116 +6894,47 @@ var init_adapter2 = __esm({
6827
6894
  }
6828
6895
  const lastAssistant = assistantMessages[assistantMessages.length - 1];
6829
6896
  const content2 = lastAssistant.content || "";
6830
- let finalTokens2;
6831
- if (totalTokensFromEvents > 0) {
6832
- const estimated = TokenEstimator.estimateUsage(prompt, content2);
6833
- finalTokens2 = {
6834
- prompt: estimated.prompt,
6835
- // Estimated split
6836
- completion: estimated.completion,
6837
- // Estimated split
6838
- total: totalTokensFromEvents
6839
- // Actual total from events
6840
- };
6841
- logger.info("Using token count from SDK events", {
6842
- tokens: TokenEstimator.format(finalTokens2),
6843
- accuracy: "100% (total), 80-90% (split)",
6844
- source: "token_count events"
6845
- });
6846
- } else {
6847
- const usage = lastAssistant.usage || lastAssistant.tokens || {};
6848
- const actualTokens = {
6849
- prompt: usage.prompt_tokens || usage.prompt || usage.input || usage.promptTokens || 0,
6850
- completion: usage.completion_tokens || usage.completion || usage.output || usage.completionTokens || 0,
6851
- total: usage.total_tokens || usage.total || usage.totalTokens || 0
6852
- };
6853
- const hasActualTokens = actualTokens.total > 0;
6854
- if (hasActualTokens) {
6855
- finalTokens2 = {
6856
- prompt: actualTokens.prompt,
6857
- completion: actualTokens.completion,
6858
- total: actualTokens.total
6859
- };
6860
- logger.info("Using token counts from ChatEntry.usage", {
6861
- tokens: TokenEstimator.format(finalTokens2),
6862
- accuracy: "100%",
6863
- source: "ChatEntry.usage"
6864
- });
6865
- } else {
6866
- finalTokens2 = TokenEstimator.estimateUsage(prompt, content2);
6867
- logger.warn("SDK did not provide token counts, using estimation", {
6868
- tokens: TokenEstimator.format(finalTokens2, true),
6869
- accuracy: "80-90% (estimated)",
6870
- source: "estimation"
6871
- });
6872
- }
6873
- }
6874
- const model2 = this.agent.getCurrentModel();
6875
- const finishReason2 = "stop";
6897
+ const tokensUsed2 = this.extractTokenUsage(
6898
+ prompt,
6899
+ content2,
6900
+ totalTokensFromEvents,
6901
+ lastAssistant.usage || lastAssistant.tokens,
6902
+ "ChatEntry.usage"
6903
+ );
6876
6904
  return {
6877
6905
  content: content2,
6878
- model: model2,
6879
- tokensUsed: finalTokens2,
6906
+ model: this.agent?.getCurrentModel() ?? "unknown",
6907
+ tokensUsed: tokensUsed2,
6880
6908
  latencyMs,
6881
- finishReason: finishReason2,
6909
+ finishReason: "stop",
6882
6910
  cached: false
6883
- // SDK doesn't expose cache status via ChatEntry
6884
6911
  };
6885
6912
  }
6886
6913
  const content = typeof result === "string" ? result : result.content || result.text || "";
6887
- let finalTokens;
6888
- if (totalTokensFromEvents > 0) {
6889
- const estimated = TokenEstimator.estimateUsage(prompt, content);
6890
- finalTokens = {
6891
- prompt: estimated.prompt,
6892
- completion: estimated.completion,
6893
- total: totalTokensFromEvents
6894
- };
6895
- logger.info("Using token count from SDK events (fallback path)", {
6896
- tokens: TokenEstimator.format(finalTokens),
6897
- accuracy: "100% (total), 80-90% (split)",
6898
- source: "token_count events"
6899
- });
6900
- } else {
6901
- const usage = result.usage || result.tokens || {};
6902
- const actualTokens = {
6903
- prompt: usage.prompt_tokens || usage.prompt || usage.input || usage.promptTokens || 0,
6904
- completion: usage.completion_tokens || usage.completion || usage.output || usage.completionTokens || 0,
6905
- total: usage.total_tokens || usage.total || usage.totalTokens || 0
6906
- };
6907
- const hasActualTokens = actualTokens.total > 0;
6908
- if (hasActualTokens) {
6909
- finalTokens = {
6910
- prompt: actualTokens.prompt,
6911
- completion: actualTokens.completion,
6912
- total: actualTokens.total
6913
- };
6914
- logger.info("Using token counts from response.usage (fallback path)", {
6915
- tokens: TokenEstimator.format(finalTokens),
6916
- accuracy: "100%",
6917
- source: "response.usage"
6918
- });
6919
- } else {
6920
- finalTokens = TokenEstimator.estimateUsage(prompt, content);
6921
- logger.warn("SDK did not provide token counts (fallback path), using estimation", {
6922
- tokens: TokenEstimator.format(finalTokens, true),
6923
- accuracy: "80-90% (estimated)",
6924
- source: "estimation"
6925
- });
6926
- }
6927
- }
6928
- const model = this.agent.getCurrentModel();
6929
- const finishReason = "stop";
6914
+ const tokensUsed = this.extractTokenUsage(
6915
+ prompt,
6916
+ content,
6917
+ totalTokensFromEvents,
6918
+ result.usage || result.tokens,
6919
+ "response.usage (fallback path)"
6920
+ );
6930
6921
  return {
6931
6922
  content,
6932
- model,
6933
- tokensUsed: finalTokens,
6923
+ model: this.agent?.getCurrentModel() ?? "unknown",
6924
+ tokensUsed,
6934
6925
  latencyMs,
6935
- finishReason,
6926
+ finishReason: "stop",
6936
6927
  cached: false
6937
- // SDK doesn't expose cache status
6938
6928
  };
6939
6929
  }
6930
+ /** Error mapping patterns: [patterns, prefix, code] */
6931
+ static ERROR_MAPPINGS = [
6932
+ [["rate limit", "rate_limit"], "Rate limit exceeded", "RATE_LIMIT_EXCEEDED"],
6933
+ [["context length", "max_tokens"], "Context length exceeded", "CONTEXT_LENGTH_EXCEEDED"],
6934
+ [["timeout", "timed out"], "Request timeout", "TIMEOUT"],
6935
+ [["invalid api key", "authentication"], "Authentication failed", "AUTHENTICATION_FAILED"],
6936
+ [["network", "econnrefused"], "Network error", "NETWORK_ERROR"]
6937
+ ];
6940
6938
  /**
6941
6939
  * Map SDK errors to AutomatosX error types
6942
6940
  */
@@ -6945,30 +6943,12 @@ var init_adapter2 = __esm({
6945
6943
  return new Error(`SDK error: ${String(error)}`);
6946
6944
  }
6947
6945
  const message = error.message.toLowerCase();
6948
- if (message.includes("rate limit") || message.includes("rate_limit")) {
6949
- const mappedError = new Error(`Rate limit exceeded: ${error.message}`);
6950
- mappedError.code = "RATE_LIMIT_EXCEEDED";
6951
- return mappedError;
6952
- }
6953
- if (message.includes("context length") || message.includes("max_tokens")) {
6954
- const mappedError = new Error(`Context length exceeded: ${error.message}`);
6955
- mappedError.code = "CONTEXT_LENGTH_EXCEEDED";
6956
- return mappedError;
6957
- }
6958
- if (message.includes("timeout") || message.includes("timed out")) {
6959
- const mappedError = new Error(`Request timeout: ${error.message}`);
6960
- mappedError.code = "TIMEOUT";
6961
- return mappedError;
6962
- }
6963
- if (message.includes("invalid api key") || message.includes("authentication")) {
6964
- const mappedError = new Error(`Authentication failed: ${error.message}`);
6965
- mappedError.code = "AUTHENTICATION_FAILED";
6966
- return mappedError;
6967
- }
6968
- if (message.includes("network") || message.includes("econnrefused")) {
6969
- const mappedError = new Error(`Network error: ${error.message}`);
6970
- mappedError.code = "NETWORK_ERROR";
6971
- return mappedError;
6946
+ for (const [patterns, prefix, code] of _AxCliSdkAdapter.ERROR_MAPPINGS) {
6947
+ if (patterns.some((pattern) => message.includes(pattern))) {
6948
+ const mappedError = new Error(`${prefix}: ${error.message}`);
6949
+ mappedError.code = code;
6950
+ return mappedError;
6951
+ }
6972
6952
  }
6973
6953
  return error;
6974
6954
  }
@@ -7003,15 +6983,10 @@ var init_adapter2 = __esm({
7003
6983
  * Get SDK version
7004
6984
  */
7005
6985
  async getVersion() {
7006
- try {
7007
- await import('@defai.digital/ax-cli/sdk');
6986
+ if (await this.ensureSDKAvailable()) {
7008
6987
  return "3.14.5+";
7009
- } catch (error) {
7010
- logger.warn("Failed to get SDK version", {
7011
- error: error instanceof Error ? error.message : String(error)
7012
- });
7013
- return "unknown";
7014
6988
  }
6989
+ return "unknown";
7015
6990
  }
7016
6991
  /**
7017
6992
  * Get command name (for compatibility with AxCliAdapter interface)
@@ -7031,7 +7006,10 @@ var init_adapter2 = __esm({
7031
7006
  async destroy() {
7032
7007
  if (this.agent) {
7033
7008
  try {
7034
- this.agent.dispose();
7009
+ const result = this.agent.dispose();
7010
+ if (result instanceof Promise) {
7011
+ await result;
7012
+ }
7035
7013
  this.agent = null;
7036
7014
  this.agentConfig = null;
7037
7015
  logger.debug("SDK agent destroyed");
@@ -9285,7 +9263,7 @@ var PRECOMPILED_CONFIG = {
9285
9263
  "enableFreeTierPrioritization": true,
9286
9264
  "enableWorkloadAwareRouting": true
9287
9265
  },
9288
- "version": "11.2.7"
9266
+ "version": "11.2.9"
9289
9267
  };
9290
9268
 
9291
9269
  // src/core/config/schemas.ts
@@ -15174,7 +15152,7 @@ function parseAXMD(content) {
15174
15152
  "Useful Links"
15175
15153
  ];
15176
15154
  for (const section of sections) {
15177
- const sectionName = section.split("\n")[0];
15155
+ const sectionName = section.split("\n")[0] ?? "";
15178
15156
  if (sectionName && !standardSections.includes(sectionName)) {
15179
15157
  result.customSections.push(sectionName);
15180
15158
  }
@@ -16567,6 +16545,8 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
16567
16545
  getManualOverride() {
16568
16546
  if (this.manualOverride && this.manualOverride.expiresAtMs) {
16569
16547
  if (Date.now() >= this.manualOverride.expiresAtMs) {
16548
+ this.manualOverride;
16549
+ this.manualOverride = void 0;
16570
16550
  void this.clearManualOverride();
16571
16551
  return void 0;
16572
16552
  }
@@ -16907,19 +16887,33 @@ var ProviderMetricsTracker = class extends EventEmitter {
16907
16887
  const p50 = getPercentile(latencies, 50);
16908
16888
  const p95 = getPercentile(latencies, 95);
16909
16889
  const p99 = getPercentile(latencies, 99);
16910
- const successful = records.filter((r) => r.success);
16911
- const failed = records.filter((r) => !r.success);
16912
- const stopFinishes = successful.filter((r) => r.finishReason === "stop");
16913
- const lengthFinishes = successful.filter((r) => r.finishReason === "length");
16914
- const errorFinishes = records.filter((r) => r.finishReason === "error");
16915
- const successRate = successful.length / records.length;
16916
- const properStopRate = successful.length > 0 ? stopFinishes.length / successful.length : 0;
16890
+ let successCount = 0;
16891
+ let failCount = 0;
16892
+ let stopFinishCount = 0;
16893
+ let lengthFinishCount = 0;
16894
+ let errorFinishCount = 0;
16895
+ let lastSuccessTimestamp = 0;
16896
+ let lastFailureTimestamp = 0;
16897
+ for (const r of records) {
16898
+ if (r.success) {
16899
+ successCount++;
16900
+ lastSuccessTimestamp = r.timestamp;
16901
+ if (r.finishReason === "stop") stopFinishCount++;
16902
+ else if (r.finishReason === "length") lengthFinishCount++;
16903
+ } else {
16904
+ failCount++;
16905
+ lastFailureTimestamp = r.timestamp;
16906
+ }
16907
+ if (r.finishReason === "error") errorFinishCount++;
16908
+ }
16909
+ const successRate = successCount / records.length;
16910
+ const properStopRate = successCount > 0 ? stopFinishCount / successCount : 0;
16917
16911
  const totalCost = records.reduce((sum, r) => sum + r.costUsd, 0);
16918
16912
  const avgCostPerRequest = totalCost / records.length;
16919
16913
  const totalTokens = records.reduce((sum, r) => sum + r.totalTokens, 0);
16920
16914
  const avgCostPer1M = totalTokens > 0 ? totalCost / totalTokens * 1e6 : 0;
16921
- const lastSuccess = successful.length > 0 ? successful[successful.length - 1].timestamp : 0;
16922
- const lastFailure = failed.length > 0 ? failed[failed.length - 1].timestamp : 0;
16915
+ const lastSuccess = lastSuccessTimestamp;
16916
+ const lastFailure = lastFailureTimestamp;
16923
16917
  let consecutiveFailures = 0;
16924
16918
  for (let i = records.length - 1; i >= 0; i--) {
16925
16919
  if (!records[i].success) {
@@ -16942,12 +16936,12 @@ var ProviderMetricsTracker = class extends EventEmitter {
16942
16936
  },
16943
16937
  quality: {
16944
16938
  totalRequests: records.length,
16945
- successfulRequests: successful.length,
16946
- failedRequests: failed.length,
16939
+ successfulRequests: successCount,
16940
+ failedRequests: failCount,
16947
16941
  successRate,
16948
- stopFinishes: stopFinishes.length,
16949
- lengthFinishes: lengthFinishes.length,
16950
- errorFinishes: errorFinishes.length,
16942
+ stopFinishes: stopFinishCount,
16943
+ lengthFinishes: lengthFinishCount,
16944
+ errorFinishes: errorFinishCount,
16951
16945
  properStopRate
16952
16946
  },
16953
16947
  cost: {
@@ -17056,7 +17050,7 @@ var ProviderMetricsTracker = class extends EventEmitter {
17056
17050
  * v9.0.2: Added score caching to reduce redundant calculations
17057
17051
  */
17058
17052
  async calculateScore(provider, weights, allProviders, healthMultiplier = 1) {
17059
- const cacheKey = `${provider}-${JSON.stringify(weights)}-${healthMultiplier}`;
17053
+ const cacheKey = `${provider}-${weights.cost}-${weights.latency}-${weights.quality}-${weights.availability}-${healthMultiplier}`;
17060
17054
  const cached = this.scoreCache.get(cacheKey);
17061
17055
  const currentRequestCount = this.getRequestCount(provider);
17062
17056
  if (cached) {
@@ -18221,8 +18215,7 @@ var Router = class {
18221
18215
  }
18222
18216
  }
18223
18217
  if (limitedProviders2.length === allProviders2.length && limitedProviders2.length > 0) {
18224
- const soonestReset = Math.min(...limitedProviders2.map((p) => p.resetAtMs));
18225
- throw ProviderError.allProvidersLimited(limitedProviders2, soonestReset);
18218
+ throw ProviderError.allProvidersLimited(limitedProviders2, this.getSoonestLimitReset(limitedProviders2));
18226
18219
  }
18227
18220
  throw ProviderError.noAvailableProviders();
18228
18221
  }
@@ -18628,8 +18621,7 @@ var Router = class {
18628
18621
  }
18629
18622
  }
18630
18623
  if (limitedProviders.length === allProviders.length && limitedProviders.length > 0) {
18631
- const soonestReset = Math.min(...limitedProviders.map((p) => p.resetAtMs));
18632
- throw ProviderError.allProvidersLimited(limitedProviders, soonestReset);
18624
+ throw ProviderError.allProvidersLimited(limitedProviders, this.getSoonestLimitReset(limitedProviders));
18633
18625
  }
18634
18626
  const errorDetails = {
18635
18627
  lastError: lastError?.message,
@@ -18863,6 +18855,16 @@ Run 'ax doctor' to diagnose provider setup.` : "";
18863
18855
  })
18864
18856
  };
18865
18857
  }
18858
+ /**
18859
+ * Calculate the soonest reset time from limited providers
18860
+ * v11.2.8: Added guard against empty array (Math.min on empty array returns Infinity)
18861
+ */
18862
+ getSoonestLimitReset(limitedProviders) {
18863
+ if (limitedProviders.length === 0) {
18864
+ return Date.now() + 6e4;
18865
+ }
18866
+ return Math.min(...limitedProviders.map((p) => p.resetAtMs));
18867
+ }
18866
18868
  };
18867
18869
 
18868
18870
  // src/core/memory/lazy-manager.ts
@@ -20698,8 +20700,6 @@ z.object({
20698
20700
  limit: z.number().int().positive().max(1e4).optional(),
20699
20701
  offset: z.number().int().nonnegative().optional()
20700
20702
  }).strict();
20701
-
20702
- // src/core/session/manager.ts
20703
20703
  var SessionManager = class _SessionManager {
20704
20704
  /** Active sessions (in-memory, keyed by session ID) */
20705
20705
  activeSessions = /* @__PURE__ */ new Map();
@@ -20772,8 +20772,9 @@ var SessionManager = class _SessionManager {
20772
20772
  try {
20773
20773
  SessionManagerConfigSchema.parse(config);
20774
20774
  } catch (error) {
20775
+ const message = error instanceof ZodError ? error.message : String(error);
20775
20776
  throw new SessionError(
20776
- `Invalid session manager config: ${error.message}`,
20777
+ `Invalid session manager config: ${message}`,
20777
20778
  void 0,
20778
20779
  "invalid_configuration"
20779
20780
  );
@@ -29880,13 +29881,23 @@ var MetricsCollector = class {
29880
29881
  */
29881
29882
  getMetricsSummary(serverMetrics) {
29882
29883
  const totalServers = serverMetrics.length;
29883
- const healthyServers = serverMetrics.filter((m) => m.memoryUsageMB > 0).length;
29884
- const totalCpu = serverMetrics.reduce((sum, m) => sum + m.cpuUsagePercent, 0);
29885
- const totalMemory = serverMetrics.reduce((sum, m) => sum + m.memoryUsageMB, 0);
29886
- const totalRequests = serverMetrics.reduce((sum, m) => sum + m.totalRequests, 0);
29887
- const totalFailed = serverMetrics.reduce((sum, m) => sum + m.failedRequests, 0);
29888
- const totalRestarts = serverMetrics.reduce((sum, m) => sum + m.restartCount, 0);
29889
- const avgResponseTime = serverMetrics.length > 0 ? serverMetrics.reduce((sum, m) => sum + m.avgResponseTimeMs, 0) / serverMetrics.length : 0;
29884
+ let healthyServers = 0;
29885
+ let totalCpu = 0;
29886
+ let totalMemory = 0;
29887
+ let totalRequests = 0;
29888
+ let totalFailed = 0;
29889
+ let totalRestarts = 0;
29890
+ let totalResponseTime = 0;
29891
+ for (const m of serverMetrics) {
29892
+ if (m.memoryUsageMB > 0) healthyServers++;
29893
+ totalCpu += m.cpuUsagePercent;
29894
+ totalMemory += m.memoryUsageMB;
29895
+ totalRequests += m.totalRequests;
29896
+ totalFailed += m.failedRequests;
29897
+ totalRestarts += m.restartCount;
29898
+ totalResponseTime += m.avgResponseTimeMs;
29899
+ }
29900
+ const avgResponseTime = totalServers > 0 ? totalResponseTime / totalServers : 0;
29890
29901
  const errorRate = totalRequests > 0 ? totalFailed / totalRequests * 100 : 0;
29891
29902
  return {
29892
29903
  totalServers,
@@ -31758,7 +31769,7 @@ var addCommand = {
31758
31769
  throw new Error(`Invalid metadata JSON: ${error.message}`);
31759
31770
  }
31760
31771
  }
31761
- const embedding = new Array(1536).fill(0);
31772
+ const embedding = Array(1536).fill(0);
31762
31773
  const entry = await manager.add(argv.content, embedding, metadata);
31763
31774
  printSuccess("\nMemory entry added successfully\n");
31764
31775
  console.log(`${chalk5.bold("ID:")} ${chalk5.white(entry.id)}`);
@@ -34106,47 +34117,39 @@ ${action.modifications}`;
34106
34117
  * @param stage - Stage to execute
34107
34118
  * @param context - Stage context
34108
34119
  * @returns Stage task prompt
34120
+ * v11.2.8: Refactored to use array.join() for better string building performance
34109
34121
  */
34110
34122
  buildStageTask(stage, context) {
34111
- let prompt = `# Stage: ${stage.name}
34112
-
34113
- `;
34114
- prompt += `## Stage Description
34123
+ const parts = [
34124
+ `# Stage: ${stage.name}
34125
+ `,
34126
+ `## Stage Description
34115
34127
  ${stage.description}
34116
-
34117
- `;
34118
- prompt += `## Original Task
34128
+ `,
34129
+ `## Original Task
34119
34130
  ${context.task}
34120
-
34121
- `;
34131
+ `
34132
+ ];
34122
34133
  if (context.previousOutputs.length > 0) {
34123
- prompt += `## Previous Stage Outputs
34124
-
34125
- `;
34126
- context.previousOutputs.forEach((output, index) => {
34127
- prompt += `### Stage ${index + 1} Output
34134
+ parts.push(`## Previous Stage Outputs
34135
+ `);
34136
+ parts.push(...context.previousOutputs.map(
34137
+ (output, index) => `### Stage ${index + 1} Output
34128
34138
  ${output}
34129
-
34130
- `;
34131
- });
34139
+ `
34140
+ ));
34132
34141
  }
34133
34142
  if (stage.key_questions && stage.key_questions.length > 0) {
34134
- prompt += `## Key Questions to Address
34135
- `;
34136
- stage.key_questions.forEach((q) => prompt += `- ${q}
34137
- `);
34138
- prompt += `
34139
- `;
34143
+ parts.push(`## Key Questions to Address`);
34144
+ parts.push(...stage.key_questions.map((q) => `- ${q}`));
34145
+ parts.push("");
34140
34146
  }
34141
34147
  if (stage.outputs && stage.outputs.length > 0) {
34142
- prompt += `## Expected Outputs
34143
- `;
34144
- stage.outputs.forEach((o) => prompt += `- ${o}
34145
- `);
34146
- prompt += `
34147
- `;
34148
+ parts.push(`## Expected Outputs`);
34149
+ parts.push(...stage.outputs.map((o) => `- ${o}`));
34150
+ parts.push("");
34148
34151
  }
34149
- return prompt;
34152
+ return parts.join("\n");
34150
34153
  }
34151
34154
  /**
34152
34155
  * Enhance stages with index and defaults
@@ -44704,10 +44707,11 @@ function displayPlanSummary(plan) {
44704
44707
  console.log(chalk5.gray(` Storage: ${plan.resourceRequirements.storage}`));
44705
44708
  console.log(chalk5.gray(` Network: ${plan.resourceRequirements.network}
44706
44709
  `));
44710
+ const SEVERITY_ICONS = { high: "\u{1F534}", medium: "\u{1F7E1}", low: "\u{1F7E2}" };
44707
44711
  if (plan.risks.length > 0) {
44708
44712
  console.log(chalk5.blue("Identified Risks:"));
44709
44713
  for (const risk of plan.risks) {
44710
- const icon = risk.severity === "high" ? "\u{1F534}" : risk.severity === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
44714
+ const icon = SEVERITY_ICONS[risk.severity] ?? "\u{1F7E2}";
44711
44715
  console.log(` ${icon} ${risk.severity.toUpperCase()}: ${risk.title}`);
44712
44716
  console.log(chalk5.gray(` ${risk.description}`));
44713
44717
  console.log(chalk5.gray(` Mitigation: ${risk.mitigation}
@@ -46833,37 +46837,38 @@ var TelemetryCollector = class {
46833
46837
  logger.warn("Query called on closed or uninitialized telemetry collector");
46834
46838
  return [];
46835
46839
  }
46836
- let sql = "SELECT * FROM telemetry_events WHERE 1=1";
46840
+ const conditions = [];
46837
46841
  const params = [];
46838
46842
  if (filters.provider) {
46839
- sql += " AND provider = ?";
46843
+ conditions.push("provider = ?");
46840
46844
  params.push(filters.provider);
46841
46845
  }
46842
46846
  if (filters.agentName) {
46843
- sql += " AND agent_name = ?";
46847
+ conditions.push("agent_name = ?");
46844
46848
  params.push(filters.agentName);
46845
46849
  }
46846
46850
  if (filters.sessionId) {
46847
- sql += " AND session_id = ?";
46851
+ conditions.push("session_id = ?");
46848
46852
  params.push(filters.sessionId);
46849
46853
  }
46850
46854
  if (filters.type) {
46851
- sql += " AND type = ?";
46855
+ conditions.push("type = ?");
46852
46856
  params.push(filters.type);
46853
46857
  }
46854
46858
  if (filters.startDate) {
46855
- sql += " AND timestamp >= ?";
46859
+ conditions.push("timestamp >= ?");
46856
46860
  params.push(filters.startDate);
46857
46861
  }
46858
46862
  if (filters.endDate) {
46859
- sql += " AND timestamp <= ?";
46863
+ conditions.push("timestamp <= ?");
46860
46864
  params.push(filters.endDate);
46861
46865
  }
46862
46866
  if (filters.success !== void 0) {
46863
- sql += " AND success = ?";
46867
+ conditions.push("success = ?");
46864
46868
  params.push(filters.success ? 1 : 0);
46865
46869
  }
46866
- sql += " ORDER BY timestamp DESC LIMIT ?";
46870
+ const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
46871
+ const sql = `SELECT * FROM telemetry_events ${whereClause} ORDER BY timestamp DESC LIMIT ?`;
46867
46872
  params.push(filters.limit || 1e3);
46868
46873
  const rows = this.db.prepare(sql).all(...params);
46869
46874
  return rows.map((row) => this.mapRowToEvent(row));
@@ -46877,20 +46882,22 @@ var TelemetryCollector = class {
46877
46882
  if (this.closed || !this.db) {
46878
46883
  return 0;
46879
46884
  }
46880
- let sql = "SELECT COUNT(*) as count FROM telemetry_events WHERE 1=1";
46885
+ const conditions = [];
46881
46886
  const params = [];
46882
46887
  if (filters.provider) {
46883
- sql += " AND provider = ?";
46888
+ conditions.push("provider = ?");
46884
46889
  params.push(filters.provider);
46885
46890
  }
46886
46891
  if (filters.startDate) {
46887
- sql += " AND timestamp >= ?";
46892
+ conditions.push("timestamp >= ?");
46888
46893
  params.push(filters.startDate);
46889
46894
  }
46890
46895
  if (filters.endDate) {
46891
- sql += " AND timestamp <= ?";
46896
+ conditions.push("timestamp <= ?");
46892
46897
  params.push(filters.endDate);
46893
46898
  }
46899
+ const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
46900
+ const sql = `SELECT COUNT(*) as count FROM telemetry_events ${whereClause}`;
46894
46901
  const result = this.db.prepare(sql).get(...params);
46895
46902
  return result.count;
46896
46903
  }
@@ -47610,9 +47617,11 @@ async function optimizeHandler(argv) {
47610
47617
  console.log(chalk5.green("\u2705 No optimization opportunities found. You're doing great!\n"));
47611
47618
  return;
47612
47619
  }
47620
+ const TYPE_ICONS = { cost_saving: "\u{1F4B0}", performance: "\u26A1", security: "\u{1F512}" };
47621
+ const SEVERITY_COLORS = { high: chalk5.red, medium: chalk5.yellow, low: chalk5.gray };
47613
47622
  for (const rec of recommendations) {
47614
- const icon = rec.type === "cost_saving" ? "\u{1F4B0}" : rec.type === "performance" ? "\u26A1" : "\u{1F512}";
47615
- const severityColor = rec.severity === "high" ? chalk5.red : rec.severity === "medium" ? chalk5.yellow : chalk5.gray;
47623
+ const icon = TYPE_ICONS[rec.type] ?? "\u{1F512}";
47624
+ const severityColor = SEVERITY_COLORS[rec.severity] ?? chalk5.gray;
47616
47625
  console.log(`${icon} ${severityColor(rec.severity.toUpperCase())}: ${chalk5.bold(rec.title)}`);
47617
47626
  console.log(` ${rec.description}`);
47618
47627
  if (rec.estimatedSavings) {
package/dist/mcp/index.js CHANGED
@@ -7,13 +7,13 @@ import { existsSync, readFileSync, promises, mkdirSync, createWriteStream, write
7
7
  import Database2 from 'better-sqlite3';
8
8
  import { glob } from 'glob';
9
9
  import { spawn, spawnSync } from 'child_process';
10
- import { z } from 'zod';
10
+ import { z, ZodError } from 'zod';
11
11
  import chalk4 from 'chalk';
12
12
  import ora2 from 'ora';
13
13
  import readline, { createInterface } from 'readline';
14
+ import { Mutex } from 'async-mutex';
14
15
  import Ajv from 'ajv';
15
16
  import addFormats from 'ajv-formats';
16
- import { Mutex } from 'async-mutex';
17
17
  import os2, { cpus } from 'os';
18
18
  import { load } from 'js-yaml';
19
19
  import { EventEmitter } from 'events';
@@ -2277,6 +2277,15 @@ var init_base_provider = __esm({
2277
2277
  "test-provider"
2278
2278
  // For unit tests
2279
2279
  ];
2280
+ /** Environment variables to force non-interactive CLI mode */
2281
+ static NON_INTERACTIVE_ENV = {
2282
+ TERM: "dumb",
2283
+ NO_COLOR: "1",
2284
+ FORCE_COLOR: "0",
2285
+ CI: "true",
2286
+ NO_UPDATE_NOTIFIER: "1",
2287
+ DEBIAN_FRONTEND: "noninteractive"
2288
+ };
2280
2289
  config;
2281
2290
  logger = logger;
2282
2291
  health;
@@ -2369,19 +2378,7 @@ var init_base_provider = __esm({
2369
2378
  shell: true,
2370
2379
  // Auto-detects: cmd.exe on Windows, /bin/sh on Unix
2371
2380
  timeout: this.config.timeout || 12e4,
2372
- env: {
2373
- ...process.env,
2374
- // Force non-interactive mode for CLIs
2375
- TERM: "dumb",
2376
- NO_COLOR: "1",
2377
- // Disable TTY checks for codex and other CLIs
2378
- FORCE_COLOR: "0",
2379
- CI: "true",
2380
- // Many CLIs disable TTY checks in CI mode
2381
- NO_UPDATE_NOTIFIER: "1",
2382
- // Disable interactive prompts
2383
- DEBIAN_FRONTEND: "noninteractive"
2384
- }
2381
+ env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
2385
2382
  });
2386
2383
  let stdout = "";
2387
2384
  let stderr = "";
@@ -2531,22 +2528,14 @@ var init_base_provider = __esm({
2531
2528
  });
2532
2529
  const child = spawn(cliCommand, commandArgs, {
2533
2530
  timeout: this.config.timeout || 12e4,
2534
- env: {
2535
- ...process.env,
2536
- // Force non-interactive mode for CLIs
2537
- TERM: "dumb",
2538
- NO_COLOR: "1",
2539
- FORCE_COLOR: "0",
2540
- CI: "true",
2541
- NO_UPDATE_NOTIFIER: "1",
2542
- DEBIAN_FRONTEND: "noninteractive"
2543
- }
2531
+ env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
2544
2532
  });
2545
2533
  let stdout = "";
2546
2534
  let stderr = "";
2547
2535
  let timeoutId = null;
2548
2536
  let forceKillTimer = null;
2549
2537
  let readlineInterface = null;
2538
+ let stderrInterface = null;
2550
2539
  const streamingEnabled = process.env.AUTOMATOSX_SHOW_PROVIDER_OUTPUT === "true";
2551
2540
  const debugMode = process.env.AUTOMATOSX_DEBUG === "true";
2552
2541
  const verbosity = VerbosityManager.getInstance();
@@ -2591,7 +2580,7 @@ var init_base_provider = __esm({
2591
2580
  });
2592
2581
  }
2593
2582
  if (child.stderr) {
2594
- const stderrInterface = readline.createInterface({
2583
+ stderrInterface = readline.createInterface({
2595
2584
  input: child.stderr,
2596
2585
  crlfDelay: Infinity
2597
2586
  });
@@ -2607,6 +2596,13 @@ var init_base_provider = __esm({
2607
2596
  }
2608
2597
  }
2609
2598
  });
2599
+ stderrInterface.on("error", (error) => {
2600
+ if (error.message !== "Readable stream already read") {
2601
+ logger.debug("Stderr readline error (non-fatal)", {
2602
+ error: error.message
2603
+ });
2604
+ }
2605
+ });
2610
2606
  }
2611
2607
  const cleanup = () => {
2612
2608
  if (timeoutId) {
@@ -2625,22 +2621,35 @@ var init_base_provider = __esm({
2625
2621
  readlineInterface = null;
2626
2622
  }
2627
2623
  }
2624
+ if (stderrInterface) {
2625
+ try {
2626
+ stderrInterface.close();
2627
+ } catch (error) {
2628
+ } finally {
2629
+ stderrInterface = null;
2630
+ }
2631
+ }
2628
2632
  };
2629
2633
  child.on("close", (code, signal) => {
2630
2634
  cleanup();
2631
2635
  if (stderr) {
2632
2636
  logger.debug(`${cliCommand} CLI stderr output`, { stderr: stderr.trim() });
2633
2637
  }
2634
- if (code === 0) {
2638
+ if ((code === 0 || code === null) && !signal) {
2635
2639
  if (progressParser) {
2636
2640
  progressParser.succeed(`${cliCommand} completed successfully`);
2637
2641
  }
2638
2642
  resolve5({ stdout, stderr });
2643
+ } else if (signal) {
2644
+ if (progressParser) {
2645
+ progressParser.fail(`${cliCommand} killed by signal ${signal}`);
2646
+ }
2647
+ reject(new Error(`${cliCommand} CLI killed by signal ${signal}. stderr: ${stderr || "none"}`));
2639
2648
  } else {
2640
2649
  if (progressParser) {
2641
2650
  progressParser.fail(`${cliCommand} failed with code ${code}`);
2642
2651
  }
2643
- reject(new Error(`${cliCommand} CLI exited with code ${code}${signal ? ` (signal: ${signal})` : ""}. stderr: ${stderr || "none"}`));
2652
+ reject(new Error(`${cliCommand} CLI exited with code ${code}. stderr: ${stderr || "none"}`));
2644
2653
  }
2645
2654
  });
2646
2655
  child.on("error", (error) => {
@@ -3596,8 +3605,6 @@ var init_cli_wrapper = __esm({
3596
3605
  };
3597
3606
  }
3598
3607
  });
3599
-
3600
- // src/integrations/openai-codex/sdk-adapter.ts
3601
3608
  var CodexSdkAdapter;
3602
3609
  var init_sdk_adapter = __esm({
3603
3610
  "src/integrations/openai-codex/sdk-adapter.ts"() {
@@ -3610,6 +3617,8 @@ var init_sdk_adapter = __esm({
3610
3617
  sdkModule = null;
3611
3618
  options;
3612
3619
  initialized = false;
3620
+ initMutex = new Mutex();
3621
+ // v11.2.8: Prevent race condition in ensureInitialized
3613
3622
  constructor(options = {}) {
3614
3623
  this.options = {
3615
3624
  streamingEnabled: true,
@@ -3704,18 +3713,20 @@ var init_sdk_adapter = __esm({
3704
3713
  return this.activeThread;
3705
3714
  }
3706
3715
  async ensureInitialized() {
3707
- if (this.initialized) return;
3708
- try {
3709
- this.sdkModule = await import('@openai/codex-sdk');
3710
- this.codex = new this.sdkModule.Codex();
3711
- this.initialized = true;
3712
- logger.info("Codex SDK initialized");
3713
- } catch (error) {
3714
- throw new CodexError(
3715
- "CLI_NOT_FOUND" /* CLI_NOT_FOUND */,
3716
- "Codex SDK not available. Install with: npm install @openai/codex-sdk"
3717
- );
3718
- }
3716
+ return this.initMutex.runExclusive(async () => {
3717
+ if (this.initialized) return;
3718
+ try {
3719
+ this.sdkModule = await import('@openai/codex-sdk');
3720
+ this.codex = new this.sdkModule.Codex();
3721
+ this.initialized = true;
3722
+ logger.info("Codex SDK initialized");
3723
+ } catch (error) {
3724
+ throw new CodexError(
3725
+ "CLI_NOT_FOUND" /* CLI_NOT_FOUND */,
3726
+ "Codex SDK not available. Install with: npm install @openai/codex-sdk"
3727
+ );
3728
+ }
3729
+ });
3719
3730
  }
3720
3731
  async destroy() {
3721
3732
  this.activeThread = null;
@@ -5229,7 +5240,7 @@ var PRECOMPILED_CONFIG = {
5229
5240
  "enableFreeTierPrioritization": true,
5230
5241
  "enableWorkloadAwareRouting": true
5231
5242
  },
5232
- "version": "11.2.7"
5243
+ "version": "11.2.9"
5233
5244
  };
5234
5245
 
5235
5246
  // src/core/config/schemas.ts
@@ -6478,6 +6489,8 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
6478
6489
  getManualOverride() {
6479
6490
  if (this.manualOverride && this.manualOverride.expiresAtMs) {
6480
6491
  if (Date.now() >= this.manualOverride.expiresAtMs) {
6492
+ this.manualOverride;
6493
+ this.manualOverride = void 0;
6481
6494
  void this.clearManualOverride();
6482
6495
  return void 0;
6483
6496
  }
@@ -6972,19 +6985,33 @@ var ProviderMetricsTracker = class extends EventEmitter {
6972
6985
  const p50 = getPercentile(latencies, 50);
6973
6986
  const p95 = getPercentile(latencies, 95);
6974
6987
  const p99 = getPercentile(latencies, 99);
6975
- const successful = records.filter((r) => r.success);
6976
- const failed = records.filter((r) => !r.success);
6977
- const stopFinishes = successful.filter((r) => r.finishReason === "stop");
6978
- const lengthFinishes = successful.filter((r) => r.finishReason === "length");
6979
- const errorFinishes = records.filter((r) => r.finishReason === "error");
6980
- const successRate = successful.length / records.length;
6981
- const properStopRate = successful.length > 0 ? stopFinishes.length / successful.length : 0;
6988
+ let successCount = 0;
6989
+ let failCount = 0;
6990
+ let stopFinishCount = 0;
6991
+ let lengthFinishCount = 0;
6992
+ let errorFinishCount = 0;
6993
+ let lastSuccessTimestamp = 0;
6994
+ let lastFailureTimestamp = 0;
6995
+ for (const r of records) {
6996
+ if (r.success) {
6997
+ successCount++;
6998
+ lastSuccessTimestamp = r.timestamp;
6999
+ if (r.finishReason === "stop") stopFinishCount++;
7000
+ else if (r.finishReason === "length") lengthFinishCount++;
7001
+ } else {
7002
+ failCount++;
7003
+ lastFailureTimestamp = r.timestamp;
7004
+ }
7005
+ if (r.finishReason === "error") errorFinishCount++;
7006
+ }
7007
+ const successRate = successCount / records.length;
7008
+ const properStopRate = successCount > 0 ? stopFinishCount / successCount : 0;
6982
7009
  const totalCost = records.reduce((sum, r) => sum + r.costUsd, 0);
6983
7010
  const avgCostPerRequest = totalCost / records.length;
6984
7011
  const totalTokens = records.reduce((sum, r) => sum + r.totalTokens, 0);
6985
7012
  const avgCostPer1M = totalTokens > 0 ? totalCost / totalTokens * 1e6 : 0;
6986
- const lastSuccess = successful.length > 0 ? successful[successful.length - 1].timestamp : 0;
6987
- const lastFailure = failed.length > 0 ? failed[failed.length - 1].timestamp : 0;
7013
+ const lastSuccess = lastSuccessTimestamp;
7014
+ const lastFailure = lastFailureTimestamp;
6988
7015
  let consecutiveFailures = 0;
6989
7016
  for (let i = records.length - 1; i >= 0; i--) {
6990
7017
  if (!records[i].success) {
@@ -7007,12 +7034,12 @@ var ProviderMetricsTracker = class extends EventEmitter {
7007
7034
  },
7008
7035
  quality: {
7009
7036
  totalRequests: records.length,
7010
- successfulRequests: successful.length,
7011
- failedRequests: failed.length,
7037
+ successfulRequests: successCount,
7038
+ failedRequests: failCount,
7012
7039
  successRate,
7013
- stopFinishes: stopFinishes.length,
7014
- lengthFinishes: lengthFinishes.length,
7015
- errorFinishes: errorFinishes.length,
7040
+ stopFinishes: stopFinishCount,
7041
+ lengthFinishes: lengthFinishCount,
7042
+ errorFinishes: errorFinishCount,
7016
7043
  properStopRate
7017
7044
  },
7018
7045
  cost: {
@@ -7121,7 +7148,7 @@ var ProviderMetricsTracker = class extends EventEmitter {
7121
7148
  * v9.0.2: Added score caching to reduce redundant calculations
7122
7149
  */
7123
7150
  async calculateScore(provider, weights, allProviders, healthMultiplier = 1) {
7124
- const cacheKey = `${provider}-${JSON.stringify(weights)}-${healthMultiplier}`;
7151
+ const cacheKey = `${provider}-${weights.cost}-${weights.latency}-${weights.quality}-${weights.availability}-${healthMultiplier}`;
7125
7152
  const cached = this.scoreCache.get(cacheKey);
7126
7153
  const currentRequestCount = this.getRequestCount(provider);
7127
7154
  if (cached) {
@@ -8285,8 +8312,7 @@ var Router = class {
8285
8312
  }
8286
8313
  }
8287
8314
  if (limitedProviders2.length === allProviders2.length && limitedProviders2.length > 0) {
8288
- const soonestReset = Math.min(...limitedProviders2.map((p) => p.resetAtMs));
8289
- throw ProviderError.allProvidersLimited(limitedProviders2, soonestReset);
8315
+ throw ProviderError.allProvidersLimited(limitedProviders2, this.getSoonestLimitReset(limitedProviders2));
8290
8316
  }
8291
8317
  throw ProviderError.noAvailableProviders();
8292
8318
  }
@@ -8692,8 +8718,7 @@ var Router = class {
8692
8718
  }
8693
8719
  }
8694
8720
  if (limitedProviders.length === allProviders.length && limitedProviders.length > 0) {
8695
- const soonestReset = Math.min(...limitedProviders.map((p) => p.resetAtMs));
8696
- throw ProviderError.allProvidersLimited(limitedProviders, soonestReset);
8721
+ throw ProviderError.allProvidersLimited(limitedProviders, this.getSoonestLimitReset(limitedProviders));
8697
8722
  }
8698
8723
  const errorDetails = {
8699
8724
  lastError: lastError?.message,
@@ -8927,6 +8952,16 @@ Run 'ax doctor' to diagnose provider setup.` : "";
8927
8952
  })
8928
8953
  };
8929
8954
  }
8955
+ /**
8956
+ * Calculate the soonest reset time from limited providers
8957
+ * v11.2.8: Added guard against empty array (Math.min on empty array returns Infinity)
8958
+ */
8959
+ getSoonestLimitReset(limitedProviders) {
8960
+ if (limitedProviders.length === 0) {
8961
+ return Date.now() + 6e4;
8962
+ }
8963
+ return Math.min(...limitedProviders.map((p) => p.resetAtMs));
8964
+ }
8930
8965
  };
8931
8966
 
8932
8967
  // src/core/memory/lazy-manager.ts
@@ -10760,8 +10795,6 @@ z.object({
10760
10795
  limit: z.number().int().positive().max(1e4).optional(),
10761
10796
  offset: z.number().int().nonnegative().optional()
10762
10797
  }).strict();
10763
-
10764
- // src/core/session/manager.ts
10765
10798
  var SessionManager = class _SessionManager {
10766
10799
  /** Active sessions (in-memory, keyed by session ID) */
10767
10800
  activeSessions = /* @__PURE__ */ new Map();
@@ -10834,8 +10867,9 @@ var SessionManager = class _SessionManager {
10834
10867
  try {
10835
10868
  SessionManagerConfigSchema.parse(config);
10836
10869
  } catch (error) {
10870
+ const message = error instanceof ZodError ? error.message : String(error);
10837
10871
  throw new SessionError(
10838
- `Invalid session manager config: ${error.message}`,
10872
+ `Invalid session manager config: ${message}`,
10839
10873
  void 0,
10840
10874
  "invalid_configuration"
10841
10875
  );
@@ -25,7 +25,7 @@
25
25
 
26
26
  **Rationale:**
27
27
 
28
- - Node.js 20+ has first-class ESM support
28
+ - Node.js 24+ has first-class ESM support
29
29
  - Better tree-shaking and bundle optimization
30
30
  - Modern standards compliance
31
31
 
@@ -137,7 +137,7 @@ import type { Provider } from '../types/provider.js';
137
137
  - `index.js.map` - Source map
138
138
  - `index.d.ts` - Type definitions
139
139
 
140
- **Bundle target:** ESM, Node 20+, <250KB target
140
+ **Bundle target:** ESM, Node 24+, <250KB target
141
141
 
142
142
  ## Workspace Structure
143
143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defai.digital/automatosx",
3
- "version": "11.2.7",
3
+ "version": "11.2.9",
4
4
  "description": "Provider-agnostic AI orchestration platform with 20+ specialized agents, persistent memory, and multi-provider routing for Claude Code, Gemini CLI, Codex CLI, and ax-cli",
5
5
  "type": "module",
6
6
  "publishConfig": {