@defai.digital/automatosx 11.3.1 → 11.3.3

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/dist/index.js CHANGED
@@ -2,8 +2,9 @@
2
2
  import * as path4 from 'path';
3
3
  import path4__default, { dirname, join, isAbsolute, basename, resolve, extname as extname$1, relative, sep, normalize, parse as parse$1, delimiter } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import * as fs4 from 'fs/promises';
5
+ import * as fs5 from 'fs/promises';
6
6
  import { mkdir, appendFile, access as access$1, readFile, stat, rm, readdir, copyFile, writeFile, rename, unlink, constants as constants$1, realpath as realpath$1 } from 'fs/promises';
7
+ import * as fs3 from 'fs';
7
8
  import { access, realpath, existsSync, readFileSync, constants, writeFileSync, mkdirSync, promises, statSync, readdirSync, createWriteStream, unlinkSync } from 'fs';
8
9
  import Database2 from 'better-sqlite3';
9
10
  import os2, { homedir, cpus } from 'os';
@@ -17,9 +18,9 @@ import * as readline2 from 'readline';
17
18
  import readline2__default, { createInterface } from 'readline';
18
19
  import { Mutex } from 'async-mutex';
19
20
  import { promisify } from 'util';
21
+ import crypto2, { randomUUID, createHash } from 'crypto';
20
22
  import yargs from 'yargs';
21
23
  import { hideBin } from 'yargs/helpers';
22
- import crypto2, { randomUUID, createHash } from 'crypto';
23
24
  import * as yaml4 from 'js-yaml';
24
25
  import yaml4__default, { load, dump } from 'js-yaml';
25
26
  import Table from 'cli-table3';
@@ -32,9 +33,7 @@ import yaml from 'yaml';
32
33
  import { parse } from '@iarna/toml';
33
34
 
34
35
  var __defProp = Object.defineProperty;
35
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
36
36
  var __getOwnPropNames = Object.getOwnPropertyNames;
37
- var __hasOwnProp = Object.prototype.hasOwnProperty;
38
37
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
39
38
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
40
39
  }) : x)(function(x) {
@@ -48,15 +47,6 @@ var __export = (target, all) => {
48
47
  for (var name in all)
49
48
  __defProp(target, name, { get: all[name], enumerable: true });
50
49
  };
51
- var __copyProps = (to, from, except, desc) => {
52
- if (from && typeof from === "object" || typeof from === "function") {
53
- for (let key of __getOwnPropNames(from))
54
- if (!__hasOwnProp.call(to, key) && key !== except)
55
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
56
- }
57
- return to;
58
- };
59
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
60
50
  var init_esm_shims = __esm({
61
51
  "node_modules/tsup/assets/esm_shims.js"() {
62
52
  }
@@ -753,16 +743,16 @@ var init_errors = __esm({
753
743
  constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
754
744
  super(message, code, suggestions, context);
755
745
  }
756
- static notFound(path7) {
746
+ static notFound(path8) {
757
747
  return new _ConfigError(
758
- `Configuration file not found: ${path7}`,
748
+ `Configuration file not found: ${path8}`,
759
749
  "E1000" /* CONFIG_NOT_FOUND */,
760
750
  [
761
751
  'Run "automatosx setup" to create a new configuration',
762
752
  "Specify a custom config path with --config option",
763
753
  "Check that you are in a valid AutomatosX project directory"
764
754
  ],
765
- { path: path7 }
755
+ { path: path8 }
766
756
  );
767
757
  }
768
758
  static invalid(reason, context) {
@@ -777,7 +767,7 @@ var init_errors = __esm({
777
767
  context
778
768
  );
779
769
  }
780
- static parseError(error, path7) {
770
+ static parseError(error, path8) {
781
771
  return new _ConfigError(
782
772
  `Failed to parse configuration: ${error.message}`,
783
773
  "E1002" /* CONFIG_PARSE_ERROR */,
@@ -786,7 +776,7 @@ var init_errors = __esm({
786
776
  "Use a JSON validator to find syntax errors",
787
777
  'Reset to default with "automatosx setup --force"'
788
778
  ],
789
- { path: path7, originalError: error.message }
779
+ { path: path8, originalError: error.message }
790
780
  );
791
781
  }
792
782
  };
@@ -1014,8 +1004,8 @@ __export(path_resolver_exports, {
1014
1004
  PathResolver: () => PathResolver,
1015
1005
  detectProjectRoot: () => detectProjectRoot
1016
1006
  });
1017
- function isWindowsPath(path7) {
1018
- return /^[a-zA-Z]:[/\\]/.test(path7);
1007
+ function isWindowsPath(path8) {
1008
+ return /^[a-zA-Z]:[/\\]/.test(path8);
1019
1009
  }
1020
1010
  async function detectProjectRoot(startDir = process.cwd()) {
1021
1011
  if (process.env.AUTOMATOSX_PROJECT_ROOT) {
@@ -1121,8 +1111,8 @@ var init_path_resolver = __esm({
1121
1111
  /**
1122
1112
  * Validate path is within allowed base directory
1123
1113
  */
1124
- validatePath(path7, baseDir) {
1125
- const normalized = normalizePath(resolvePath(path7));
1114
+ validatePath(path8, baseDir) {
1115
+ const normalized = normalizePath(resolvePath(path8));
1126
1116
  const base = normalizePath(resolvePath(baseDir));
1127
1117
  const separator = "/";
1128
1118
  const pathWithSep = normalized + separator;
@@ -1132,15 +1122,15 @@ var init_path_resolver = __esm({
1132
1122
  /**
1133
1123
  * Check if path is within allowed boundaries
1134
1124
  */
1135
- isPathAllowed(path7) {
1136
- const boundary = this.checkBoundaries(path7);
1125
+ isPathAllowed(path8) {
1126
+ const boundary = this.checkBoundaries(path8);
1137
1127
  return boundary === "agent_workspace" || boundary === "user_project";
1138
1128
  }
1139
1129
  /**
1140
1130
  * Check which boundary a path belongs to
1141
1131
  */
1142
- checkBoundaries(path7) {
1143
- const normalized = resolvePath(path7);
1132
+ checkBoundaries(path8) {
1133
+ const normalized = resolvePath(path8);
1144
1134
  if (this.validatePath(normalized, this.config.agentWorkspace)) {
1145
1135
  return "agent_workspace";
1146
1136
  }
@@ -1158,15 +1148,15 @@ var init_path_resolver = __esm({
1158
1148
  /**
1159
1149
  * Get relative path from project root
1160
1150
  */
1161
- getRelativeToProject(path7) {
1162
- const normalized = resolvePath(path7);
1151
+ getRelativeToProject(path8) {
1152
+ const normalized = resolvePath(path8);
1163
1153
  return normalizePath(getRelativePath(this.config.projectDir, normalized));
1164
1154
  }
1165
1155
  /**
1166
1156
  * Get relative path from working directory
1167
1157
  */
1168
- getRelativeToWorking(path7) {
1169
- const normalized = resolvePath(path7);
1158
+ getRelativeToWorking(path8) {
1159
+ const normalized = resolvePath(path8);
1170
1160
  return normalizePath(getRelativePath(this.config.workingDir, normalized));
1171
1161
  }
1172
1162
  /**
@@ -1185,11 +1175,11 @@ var init_path_resolver = __esm({
1185
1175
  * Validate path is within project boundaries
1186
1176
  * @throws PathError if outside project
1187
1177
  */
1188
- validateInProject(path7) {
1189
- const boundary = this.checkBoundaries(path7);
1178
+ validateInProject(path8) {
1179
+ const boundary = this.checkBoundaries(path8);
1190
1180
  if (boundary === "outside_boundaries" || boundary === "system_restricted") {
1191
1181
  throw new PathError("Path outside project directory", {
1192
- path: path7,
1182
+ path: path8,
1193
1183
  projectDir: this.config.projectDir,
1194
1184
  boundary
1195
1185
  });
@@ -1432,11 +1422,11 @@ var init_workspace_indexer = __esm({
1432
1422
  /**
1433
1423
  * Detect file type and language
1434
1424
  */
1435
- detectTypeAndLanguage(path7, ext) {
1436
- if (path7.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
1425
+ detectTypeAndLanguage(path8, ext) {
1426
+ if (path8.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
1437
1427
  return { type: "config", language: "json" };
1438
1428
  }
1439
- if (path7.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path7.includes("__tests__")) {
1429
+ if (path8.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path8.includes("__tests__")) {
1440
1430
  const lang = LANGUAGE_MAP[ext];
1441
1431
  return { type: "test", language: lang };
1442
1432
  }
@@ -1453,15 +1443,15 @@ var init_workspace_indexer = __esm({
1453
1443
  *
1454
1444
  * Higher score = more likely to be relevant
1455
1445
  */
1456
- calculateImportance(path7, type, size) {
1446
+ calculateImportance(path8, type, size) {
1457
1447
  let score = 0.5;
1458
- if (path7.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
1459
- if (path7.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
1460
- if (path7.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
1448
+ if (path8.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
1449
+ if (path8.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
1450
+ if (path8.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
1461
1451
  if (type === "config") score = 0.85;
1462
- if (path7.includes("/core/")) score += 0.15;
1463
- if (path7.includes("/types/")) score += 0.1;
1464
- if (path7.includes("/utils/")) score += 0.05;
1452
+ if (path8.includes("/core/")) score += 0.15;
1453
+ if (path8.includes("/types/")) score += 0.1;
1454
+ if (path8.includes("/utils/")) score += 0.05;
1465
1455
  if (type === "test") score = Math.max(0.3, score - 0.3);
1466
1456
  if (type === "doc") score = Math.max(0.4, score - 0.2);
1467
1457
  if (size > 1e4) score += 0.05;
@@ -1937,10 +1927,10 @@ function findOnPathUnix(cmdBase) {
1937
1927
  try {
1938
1928
  const which = spawnSync("which", [cmdBase], { timeout: 3e3 });
1939
1929
  if (which.status === 0) {
1940
- const path7 = which.stdout.toString().trim();
1941
- if (path7) {
1942
- logger.debug("Found via which", { cmdBase, path: path7 });
1943
- return { found: true, path: path7 };
1930
+ const path8 = which.stdout.toString().trim();
1931
+ if (path8) {
1932
+ logger.debug("Found via which", { cmdBase, path: path8 });
1933
+ return { found: true, path: path8 };
1944
1934
  }
1945
1935
  }
1946
1936
  } catch (error) {
@@ -1981,8 +1971,8 @@ var init_provider_schemas = __esm({
1981
1971
  completion: z.number().int().nonnegative(),
1982
1972
  total: z.number().int().nonnegative()
1983
1973
  }).refine(
1984
- (data) => data.total === data.prompt + data.completion,
1985
- "Total tokens must equal prompt + completion"
1974
+ (data) => data.total >= data.prompt + data.completion,
1975
+ "Total tokens must be greater than or equal to prompt + completion"
1986
1976
  ).describe("Token usage information");
1987
1977
  finishReasonSchema = z.enum([
1988
1978
  "stop",
@@ -2239,15 +2229,6 @@ var init_streaming_progress_parser = __esm({
2239
2229
  });
2240
2230
 
2241
2231
  // src/providers/error-patterns.ts
2242
- var error_patterns_exports = {};
2243
- __export(error_patterns_exports, {
2244
- GENERIC_ERROR_PATTERNS: () => GENERIC_ERROR_PATTERNS,
2245
- PROVIDER_ERROR_PATTERNS: () => PROVIDER_ERROR_PATTERNS,
2246
- getProviderErrorPatterns: () => getProviderErrorPatterns,
2247
- isLimitError: () => isLimitError,
2248
- isQuotaError: () => isQuotaError,
2249
- isRateLimitError: () => isRateLimitError
2250
- });
2251
2232
  function isQuotaError(error, providerName) {
2252
2233
  const patterns = PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
2253
2234
  const message = (error?.message || "").toLowerCase();
@@ -2289,9 +2270,6 @@ function isRateLimitError(error, providerName) {
2289
2270
  function isLimitError(error, providerName) {
2290
2271
  return isQuotaError(error, providerName) || isRateLimitError(error, providerName);
2291
2272
  }
2292
- function getProviderErrorPatterns(providerName) {
2293
- return PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
2294
- }
2295
2273
  var PROVIDER_ERROR_PATTERNS, GENERIC_ERROR_PATTERNS;
2296
2274
  var init_error_patterns = __esm({
2297
2275
  "src/providers/error-patterns.ts"() {
@@ -2480,6 +2458,62 @@ var init_error_patterns = __esm({
2480
2458
  "quota_exceeded",
2481
2459
  "billing_hard_limit_reached"
2482
2460
  ]
2461
+ },
2462
+ /**
2463
+ * ax-cli Provider Error Patterns (v9.2.0)
2464
+ *
2465
+ * ax-cli is a multi-model provider supporting GLM, xAI, OpenAI, Anthropic, Ollama, etc.
2466
+ * BUG FIX: Added missing patterns - previously fell back to generic patterns which
2467
+ * may miss provider-specific errors from the underlying model providers.
2468
+ *
2469
+ * Combines patterns from all supported backends since ax-cli proxies to them.
2470
+ */
2471
+ "ax-cli": {
2472
+ quota: [
2473
+ // GLM patterns
2474
+ "quota exceeded",
2475
+ "quota limit reached",
2476
+ "insufficient quota",
2477
+ // OpenAI patterns (via ax-cli)
2478
+ "insufficient_quota",
2479
+ "quota_exceeded",
2480
+ "billing hard limit reached",
2481
+ "usage limit exceeded",
2482
+ "monthly quota exceeded",
2483
+ "credit limit reached",
2484
+ // Anthropic patterns (via ax-cli)
2485
+ "credit limit reached",
2486
+ // Generic patterns
2487
+ "daily quota exceeded",
2488
+ "api quota exceeded"
2489
+ ],
2490
+ rateLimit: [
2491
+ // Common patterns
2492
+ "rate_limit_exceeded",
2493
+ "rate limit exceeded",
2494
+ "too_many_requests",
2495
+ "too many requests",
2496
+ "requests per minute exceeded",
2497
+ "tokens per minute exceeded",
2498
+ "rate limit reached",
2499
+ // Anthropic patterns (via ax-cli)
2500
+ "rate_limit_error",
2501
+ "overloaded_error",
2502
+ "overloaded",
2503
+ // xAI/Grok patterns
2504
+ "throttled",
2505
+ "request throttled"
2506
+ ],
2507
+ statusCodes: [429, 529],
2508
+ errorCodes: [
2509
+ "insufficient_quota",
2510
+ "rate_limit_exceeded",
2511
+ "quota_exceeded",
2512
+ "rate_limit_error",
2513
+ "overloaded_error",
2514
+ "RATE_LIMIT_EXCEEDED",
2515
+ "QUOTA_EXCEEDED"
2516
+ ]
2483
2517
  }
2484
2518
  };
2485
2519
  GENERIC_ERROR_PATTERNS = {
@@ -2502,6 +2536,105 @@ var init_error_patterns = __esm({
2502
2536
  };
2503
2537
  }
2504
2538
  });
2539
+
2540
+ // src/providers/retry-errors.ts
2541
+ function containsErrorPattern(message, patterns) {
2542
+ const lowerMessage = message.toLowerCase();
2543
+ return patterns.some((pattern) => lowerMessage.includes(pattern.toLowerCase()));
2544
+ }
2545
+ function getRetryableErrors(provider) {
2546
+ const baseErrors = [
2547
+ ...COMMON_NETWORK_ERRORS,
2548
+ ...COMMON_RATE_LIMIT_ERRORS,
2549
+ ...COMMON_SERVER_ERRORS
2550
+ ];
2551
+ switch (provider) {
2552
+ case "claude":
2553
+ return [...baseErrors, ...CLAUDE_RETRYABLE_ERRORS];
2554
+ case "gemini":
2555
+ return [...baseErrors, ...GEMINI_RETRYABLE_ERRORS];
2556
+ case "openai":
2557
+ return [...baseErrors, ...OPENAI_RETRYABLE_ERRORS];
2558
+ case "ax-cli":
2559
+ return [...baseErrors, ...AX_CLI_RETRYABLE_ERRORS];
2560
+ case "codex":
2561
+ return [...baseErrors, ...CODEX_RETRYABLE_ERRORS];
2562
+ case "base":
2563
+ default:
2564
+ return baseErrors;
2565
+ }
2566
+ }
2567
+ function shouldRetryError(error, provider) {
2568
+ const message = error.message;
2569
+ if (containsErrorPattern(message, NON_RETRYABLE_ERRORS)) {
2570
+ return false;
2571
+ }
2572
+ const retryableErrors = getRetryableErrors(provider);
2573
+ return containsErrorPattern(message, retryableErrors);
2574
+ }
2575
+ var COMMON_NETWORK_ERRORS, COMMON_RATE_LIMIT_ERRORS, COMMON_SERVER_ERRORS, CLAUDE_RETRYABLE_ERRORS, GEMINI_RETRYABLE_ERRORS, OPENAI_RETRYABLE_ERRORS, AX_CLI_RETRYABLE_ERRORS, CODEX_RETRYABLE_ERRORS, NON_RETRYABLE_ERRORS;
2576
+ var init_retry_errors = __esm({
2577
+ "src/providers/retry-errors.ts"() {
2578
+ init_esm_shims();
2579
+ COMMON_NETWORK_ERRORS = [
2580
+ "ECONNRESET",
2581
+ "ETIMEDOUT",
2582
+ "ENOTFOUND",
2583
+ "ECONNREFUSED",
2584
+ "connection_error",
2585
+ "network connection error",
2586
+ "timeout"
2587
+ ];
2588
+ COMMON_RATE_LIMIT_ERRORS = [
2589
+ "rate_limit",
2590
+ "rate_limit_error",
2591
+ "too many requests"
2592
+ ];
2593
+ COMMON_SERVER_ERRORS = [
2594
+ "internal_server_error",
2595
+ "server_error",
2596
+ "service_unavailable",
2597
+ "unavailable"
2598
+ ];
2599
+ CLAUDE_RETRYABLE_ERRORS = [
2600
+ "overloaded_error",
2601
+ "internal_server_error"
2602
+ ];
2603
+ GEMINI_RETRYABLE_ERRORS = [
2604
+ "resource_exhausted",
2605
+ "deadline_exceeded",
2606
+ "internal"
2607
+ ];
2608
+ OPENAI_RETRYABLE_ERRORS = [
2609
+ "internal_error"
2610
+ ];
2611
+ AX_CLI_RETRYABLE_ERRORS = [
2612
+ // Inherited from Anthropic (via ax-cli)
2613
+ "overloaded_error",
2614
+ // Inherited from OpenAI (via ax-cli)
2615
+ "internal_error",
2616
+ // ax-cli specific
2617
+ "agent_error",
2618
+ "tool_execution_error",
2619
+ // xAI/Grok specific
2620
+ "service_overloaded"
2621
+ ];
2622
+ CODEX_RETRYABLE_ERRORS = [
2623
+ ...OPENAI_RETRYABLE_ERRORS,
2624
+ "sandbox_error"
2625
+ // Codex-specific sandbox issues are often transient
2626
+ ];
2627
+ NON_RETRYABLE_ERRORS = [
2628
+ "authentication",
2629
+ "unauthorized",
2630
+ "api key",
2631
+ "not found",
2632
+ "permission denied",
2633
+ "invalid_api_key",
2634
+ "invalid_request"
2635
+ ];
2636
+ }
2637
+ });
2505
2638
  var BaseProvider;
2506
2639
  var init_base_provider = __esm({
2507
2640
  "src/providers/base-provider.ts"() {
@@ -2512,6 +2645,8 @@ var init_base_provider = __esm({
2512
2645
  init_provider_schemas();
2513
2646
  init_streaming_progress_parser();
2514
2647
  init_verbosity_manager();
2648
+ init_error_patterns();
2649
+ init_retry_errors();
2515
2650
  BaseProvider = class _BaseProvider {
2516
2651
  /**
2517
2652
  * Whitelist of allowed provider names for security
@@ -2544,6 +2679,10 @@ var init_base_provider = __esm({
2544
2679
  NO_UPDATE_NOTIFIER: "1",
2545
2680
  DEBIAN_FRONTEND: "noninteractive"
2546
2681
  };
2682
+ /** Default CLI execution timeout in milliseconds */
2683
+ static DEFAULT_TIMEOUT_MS = 12e4;
2684
+ /** Time to wait after SIGTERM before escalating to SIGKILL */
2685
+ static SIGKILL_ESCALATION_MS = 5e3;
2547
2686
  config;
2548
2687
  logger = logger;
2549
2688
  health;
@@ -2561,6 +2700,8 @@ var init_base_provider = __esm({
2561
2700
  latencyMs: 0,
2562
2701
  errorRate: 0,
2563
2702
  consecutiveFailures: 0,
2703
+ consecutiveSuccesses: 0,
2704
+ // BUG FIX: Track consecutive successes
2564
2705
  lastCheck: Date.now()
2565
2706
  };
2566
2707
  }
@@ -2601,7 +2742,7 @@ var init_base_provider = __esm({
2601
2742
  useStdin,
2602
2743
  streaming: process.env.AUTOMATOSX_SHOW_PROVIDER_OUTPUT === "true"
2603
2744
  });
2604
- const result = useStdin ? await this.executeWithStdin(cliCommand2, argsString.trim(), prompt) : await this.executeWithSpawn(fullCommand, cliCommand2);
2745
+ const result = useStdin ? await this.executeWithStdin(cliCommand2, cliArgs, prompt) : await this.executeWithSpawn(fullCommand, cliCommand2);
2605
2746
  if (!result.stdout) {
2606
2747
  throw new Error(`${cliCommand2} CLI returned empty output. stderr: ${result.stderr || "none"}`);
2607
2748
  }
@@ -2623,8 +2764,8 @@ var init_base_provider = __esm({
2623
2764
  /**
2624
2765
  * Execute command using spawn() with streaming support (v8.4.18)
2625
2766
  *
2626
- * Replaces execWithCleanup to support real-time line-by-line output streaming.
2627
- * Maintains all timeout and cleanup behaviors from execWithCleanup.
2767
+ * Supports real-time line-by-line output streaming with proper timeout
2768
+ * and cleanup behaviors (SIGTERM → SIGKILL escalation pattern).
2628
2769
  *
2629
2770
  * @param command - Full command string to execute
2630
2771
  * @param cliCommand - CLI command name (for logging)
@@ -2635,7 +2776,7 @@ var init_base_provider = __esm({
2635
2776
  const child = spawn(command, [], {
2636
2777
  shell: true,
2637
2778
  // Auto-detects: cmd.exe on Windows, /bin/sh on Unix
2638
- timeout: this.config.timeout || 12e4,
2779
+ timeout: this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS,
2639
2780
  env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
2640
2781
  });
2641
2782
  let stdout = "";
@@ -2746,7 +2887,7 @@ var init_base_provider = __esm({
2746
2887
  });
2747
2888
  reject(new Error(`Failed to spawn ${cliCommand2} CLI: ${error.message}`));
2748
2889
  });
2749
- const timeout = this.config.timeout || 12e4;
2890
+ const timeout = this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS;
2750
2891
  timeoutId = setTimeout(() => {
2751
2892
  if (child.pid && !child.killed) {
2752
2893
  logger.warn("Killing child process due to timeout", {
@@ -2760,7 +2901,7 @@ var init_base_provider = __esm({
2760
2901
  logger.warn("Force killing child process", { pid: child.pid });
2761
2902
  child.kill("SIGKILL");
2762
2903
  }
2763
- }, 5e3);
2904
+ }, _BaseProvider.SIGKILL_ESCALATION_MS);
2764
2905
  }
2765
2906
  }, timeout);
2766
2907
  });
@@ -2778,14 +2919,13 @@ var init_base_provider = __esm({
2778
2919
  */
2779
2920
  async executeWithStdin(cliCommand2, cliArgs, prompt) {
2780
2921
  return new Promise((resolve13, reject) => {
2781
- const commandArgs = cliArgs ? cliArgs.split(" ").filter(Boolean) : [];
2782
2922
  logger.debug(`Executing ${cliCommand2} CLI with stdin`, {
2783
2923
  command: cliCommand2,
2784
- args: commandArgs,
2924
+ args: cliArgs,
2785
2925
  promptLength: prompt.length
2786
2926
  });
2787
- const child = spawn(cliCommand2, commandArgs, {
2788
- timeout: this.config.timeout || 12e4,
2927
+ const child = spawn(cliCommand2, cliArgs, {
2928
+ timeout: this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS,
2789
2929
  env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
2790
2930
  });
2791
2931
  let stdout = "";
@@ -2808,11 +2948,20 @@ var init_base_provider = __esm({
2808
2948
  child.stdin.write(prompt);
2809
2949
  child.stdin.end();
2810
2950
  } catch (error) {
2951
+ const errorMessage = error instanceof Error ? error.message : String(error);
2811
2952
  logger.error("Failed to write to stdin", {
2812
2953
  command: cliCommand2,
2813
- error: error instanceof Error ? error.message : String(error)
2954
+ error: errorMessage
2814
2955
  });
2956
+ child.kill("SIGTERM");
2957
+ reject(new Error(`Failed to write prompt to ${cliCommand2} stdin: ${errorMessage}`));
2958
+ return;
2815
2959
  }
2960
+ } else {
2961
+ logger.error("stdin not available for child process", { command: cliCommand2 });
2962
+ child.kill("SIGTERM");
2963
+ reject(new Error(`${cliCommand2} stdin not available - cannot send prompt`));
2964
+ return;
2816
2965
  }
2817
2966
  if (child.stdout) {
2818
2967
  readlineInterface = readline2__default.createInterface({
@@ -2921,7 +3070,7 @@ var init_base_provider = __esm({
2921
3070
  });
2922
3071
  reject(new Error(`Failed to spawn ${cliCommand2} CLI: ${error.message}`));
2923
3072
  });
2924
- const timeout = this.config.timeout || 12e4;
3073
+ const timeout = this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS;
2925
3074
  timeoutId = setTimeout(() => {
2926
3075
  if (child.pid && !child.killed) {
2927
3076
  logger.warn("Killing child process due to timeout", {
@@ -2935,7 +3084,7 @@ var init_base_provider = __esm({
2935
3084
  logger.warn("Force killing child process", { pid: child.pid });
2936
3085
  child.kill("SIGKILL");
2937
3086
  }
2938
- }, 5e3);
3087
+ }, _BaseProvider.SIGKILL_ESCALATION_MS);
2939
3088
  }
2940
3089
  }, timeout);
2941
3090
  });
@@ -3001,10 +3150,8 @@ var init_base_provider = __esm({
3001
3150
  ${request.prompt}`;
3002
3151
  }
3003
3152
  if (process.env.AUTOMATOSX_DEBUG_PROMPT === "true") {
3004
- const fs7 = await import('fs');
3005
- const path7 = await import('path');
3006
- const debugPath = path7.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
3007
- fs7.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
3153
+ const debugPath = path4.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
3154
+ fs3.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
3008
3155
 
3009
3156
  ${fullPrompt}
3010
3157
 
@@ -3015,6 +3162,7 @@ ${fullPrompt}
3015
3162
  const result = await this.executeCLI(fullPrompt);
3016
3163
  const latencyMs = Date.now() - startTime;
3017
3164
  this.health.consecutiveFailures = 0;
3165
+ this.health.consecutiveSuccesses++;
3018
3166
  this.health.available = true;
3019
3167
  this.health.errorRate = 0;
3020
3168
  this.health.latencyMs = latencyMs;
@@ -3047,6 +3195,7 @@ ${fullPrompt}
3047
3195
  return response;
3048
3196
  } catch (error) {
3049
3197
  this.health.consecutiveFailures++;
3198
+ this.health.consecutiveSuccesses = 0;
3050
3199
  this.health.available = false;
3051
3200
  this.health.errorRate = 1;
3052
3201
  this.health.latencyMs = Date.now() - startTime;
@@ -3060,19 +3209,23 @@ ${fullPrompt}
3060
3209
  async isAvailable() {
3061
3210
  try {
3062
3211
  const available = await this.checkCLIAvailable();
3063
- this.health = {
3064
- available,
3065
- latencyMs: 0,
3066
- errorRate: available ? 0 : 1,
3067
- lastCheck: Date.now(),
3068
- consecutiveFailures: available ? 0 : this.health.consecutiveFailures + 1
3069
- };
3212
+ this.health.available = available;
3213
+ this.health.errorRate = available ? 0 : 1;
3214
+ this.health.lastCheck = Date.now();
3215
+ if (available) {
3216
+ this.health.consecutiveFailures = 0;
3217
+ this.health.consecutiveSuccesses++;
3218
+ } else {
3219
+ this.health.consecutiveFailures++;
3220
+ this.health.consecutiveSuccesses = 0;
3221
+ }
3070
3222
  return available;
3071
3223
  } catch (error) {
3072
3224
  this.health.available = false;
3073
3225
  this.health.errorRate = 1;
3074
3226
  this.health.lastCheck = Date.now();
3075
- this.health.consecutiveFailures = this.health.consecutiveFailures + 1;
3227
+ this.health.consecutiveFailures++;
3228
+ this.health.consecutiveSuccesses = 0;
3076
3229
  return false;
3077
3230
  }
3078
3231
  }
@@ -3127,26 +3280,16 @@ ${fullPrompt}
3127
3280
  * @returns true if this is a rate limit or quota error
3128
3281
  */
3129
3282
  detectRateLimitError(error) {
3130
- const { isLimitError: isLimitError2 } = (init_error_patterns(), __toCommonJS(error_patterns_exports));
3131
- try {
3132
- const isLimited = isLimitError2(error, this.config.name);
3133
- if (isLimited) {
3134
- this.logger.debug("Rate limit error detected", {
3135
- provider: this.config.name,
3136
- message: error?.message,
3137
- code: error?.code,
3138
- status: error?.status || error?.statusCode
3139
- });
3140
- }
3141
- return isLimited;
3142
- } catch (detectionError) {
3143
- this.logger.warn("Rate limit detection failed, using fallback", {
3283
+ const isLimited = isLimitError(error, this.config.name);
3284
+ if (isLimited) {
3285
+ this.logger.debug("Rate limit error detected", {
3144
3286
  provider: this.config.name,
3145
- error: detectionError
3287
+ message: error?.message,
3288
+ code: error?.code,
3289
+ status: error?.status || error?.statusCode
3146
3290
  });
3147
- const message = (error?.message || "").toLowerCase();
3148
- return message.includes("rate limit") || message.includes("quota") || message.includes("resource_exhausted") || message.includes("too many requests");
3149
3291
  }
3292
+ return isLimited;
3150
3293
  }
3151
3294
  /**
3152
3295
  * Escape shell command arguments to prevent injection
@@ -3239,8 +3382,20 @@ ${fullPrompt}
3239
3382
  };
3240
3383
  }
3241
3384
  shouldRetry(error) {
3242
- const message = error.message.toLowerCase();
3243
- return message.includes("timeout") || message.includes("rate limit") || message.includes("temporarily unavailable") || message.includes("503") || message.includes("502");
3385
+ const providerName = this.config.name.toLowerCase();
3386
+ let retryableProvider = "base";
3387
+ if (providerName === "claude" || providerName === "claude-code") {
3388
+ retryableProvider = "claude";
3389
+ } else if (providerName === "gemini" || providerName === "gemini-cli") {
3390
+ retryableProvider = "gemini";
3391
+ } else if (providerName === "openai") {
3392
+ retryableProvider = "openai";
3393
+ } else if (providerName === "codex") {
3394
+ retryableProvider = "codex";
3395
+ } else if (providerName === "ax-cli" || providerName === "glm") {
3396
+ retryableProvider = "ax-cli";
3397
+ }
3398
+ return shouldRetryError(error, retryableProvider);
3244
3399
  }
3245
3400
  getRetryDelay(attempt) {
3246
3401
  return Math.min(1e3 * Math.pow(2, attempt - 1), 3e4);
@@ -3264,7 +3419,9 @@ ${fullPrompt}
3264
3419
  },
3265
3420
  health: {
3266
3421
  consecutiveFailures: this.health.consecutiveFailures,
3267
- consecutiveSuccesses: this.health.consecutiveFailures === 0 ? 1 : 0,
3422
+ // BUG FIX: Use actual tracked value instead of incorrect derivation
3423
+ // Previously: `consecutiveFailures === 0 ? 1 : 0` which was wrong
3424
+ consecutiveSuccesses: this.health.consecutiveSuccesses,
3268
3425
  lastCheckTime: this.health.lastCheck,
3269
3426
  lastCheckDuration: 0,
3270
3427
  uptime: this.health.lastCheck > 0 ? Date.now() - this.health.lastCheck : 0
@@ -3964,7 +4121,20 @@ var init_sdk_adapter = __esm({
3964
4121
  }
3965
4122
  }
3966
4123
  const tokenCount = result.usage ? (result.usage.input_tokens || 0) + (result.usage.output_tokens || 0) : void 0;
3967
- if (!this.options.reuseThreads) {
4124
+ if (!this.options.reuseThreads && this.activeThread) {
4125
+ try {
4126
+ const threadWithDispose = this.activeThread;
4127
+ if (typeof threadWithDispose.dispose === "function") {
4128
+ const disposeResult = threadWithDispose.dispose();
4129
+ if (disposeResult instanceof Promise) {
4130
+ await disposeResult;
4131
+ }
4132
+ }
4133
+ } catch (disposeError) {
4134
+ logger.warn("Error disposing thread", {
4135
+ error: disposeError instanceof Error ? disposeError.message : String(disposeError)
4136
+ });
4137
+ }
3968
4138
  this.activeThread = null;
3969
4139
  }
3970
4140
  return {
@@ -4143,12 +4313,38 @@ var init_hybrid_adapter = __esm({
4143
4313
  }
4144
4314
  this.activeMode = "cli";
4145
4315
  }
4316
+ /**
4317
+ * Cleanup resources
4318
+ *
4319
+ * BUG FIX: Use Promise.allSettled to ensure cleanup continues even if one
4320
+ * adapter fails, and properly log any cleanup errors. Previously, a failure
4321
+ * in SDK destroy would prevent CLI cleanup from running.
4322
+ */
4146
4323
  async destroy() {
4147
- if (this.sdkAdapter) await this.sdkAdapter.destroy();
4148
- if (this.cliAdapter) await this.cliAdapter.cleanup();
4324
+ const cleanupPromises = [];
4325
+ if (this.sdkAdapter) {
4326
+ cleanupPromises.push(
4327
+ this.sdkAdapter.destroy().catch((err) => {
4328
+ logger.warn("Error destroying SDK adapter", {
4329
+ error: err instanceof Error ? err.message : String(err)
4330
+ });
4331
+ })
4332
+ );
4333
+ }
4334
+ if (this.cliAdapter) {
4335
+ cleanupPromises.push(
4336
+ this.cliAdapter.cleanup().catch((err) => {
4337
+ logger.warn("Error cleaning up CLI adapter", {
4338
+ error: err instanceof Error ? err.message : String(err)
4339
+ });
4340
+ })
4341
+ );
4342
+ }
4343
+ await Promise.allSettled(cleanupPromises);
4149
4344
  this.sdkAdapter = null;
4150
4345
  this.cliAdapter = null;
4151
4346
  this.activeMode = null;
4347
+ logger.debug("HybridCodexAdapter destroyed");
4152
4348
  }
4153
4349
  };
4154
4350
  }
@@ -4205,7 +4401,7 @@ var init_openai_provider = __esm({
4205
4401
  error: error instanceof Error ? error.message : String(error),
4206
4402
  mode: this.hybridAdapter?.getActiveMode() || "unknown"
4207
4403
  });
4208
- throw error;
4404
+ throw this.handleError(error);
4209
4405
  }
4210
4406
  }
4211
4407
  initializeHybridAdapter() {
@@ -4398,17 +4594,25 @@ var init_command_builder = __esm({
4398
4594
  /**
4399
4595
  * Escape string for shell execution
4400
4596
  *
4401
- * Wraps in double quotes and escapes:
4402
- * - Double quotes: " \"
4597
+ * Uses $'...' ANSI-C quoting for reliable cross-platform escaping.
4598
+ * This handles all special characters including newlines, tabs, and unicode.
4599
+ *
4600
+ * Escapes:
4601
+ * - Single quotes: ' → \'
4403
4602
  * - Backslashes: \ → \\
4404
- * - Dollar signs: $\$
4405
- * - Backticks: `\`
4603
+ * - Newlines: \n\n (literal escape sequence)
4604
+ * - Carriage returns: \r\r
4605
+ * - Tabs: \t → \t
4606
+ *
4607
+ * BUG FIX (v11.3.3): Previous implementation using double quotes didn't handle
4608
+ * newlines properly, which could break shell commands or cause unexpected behavior.
4406
4609
  *
4407
4610
  * @param input - String to escape
4408
- * @returns Shell-safe escaped string
4611
+ * @returns Shell-safe escaped string using ANSI-C quoting
4409
4612
  */
4410
4613
  escape(input) {
4411
- return `"${input.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`")}"`;
4614
+ const escaped = input.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
4615
+ return `$'${escaped}'`;
4412
4616
  }
4413
4617
  /**
4414
4618
  * Build environment variables for CLI execution
@@ -4874,24 +5078,25 @@ var init_token_estimator = __esm({
4874
5078
  };
4875
5079
  }
4876
5080
  });
4877
-
4878
- // src/integrations/ax-cli-sdk/subagent-adapter.ts
4879
- var TOKEN_SPLIT, SubagentAdapter;
5081
+ var TOKEN_SPLIT, DEFAULT_MAX_PARALLEL, DEFAULT_TIMEOUT_MS, DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS, SubagentAdapter;
4880
5082
  var init_subagent_adapter = __esm({
4881
5083
  "src/integrations/ax-cli-sdk/subagent-adapter.ts"() {
4882
5084
  init_esm_shims();
4883
5085
  init_logger();
4884
5086
  TOKEN_SPLIT = { PROMPT: 0.3, COMPLETION: 0.7 };
5087
+ DEFAULT_MAX_PARALLEL = 4;
5088
+ DEFAULT_TIMEOUT_MS = 3e5;
5089
+ DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS = 100;
4885
5090
  SubagentAdapter = class {
4886
5091
  orchestrator = null;
4887
5092
  subagents = /* @__PURE__ */ new Map();
5093
+ busySubagents = /* @__PURE__ */ new Set();
4888
5094
  sdkAvailable = null;
4889
5095
  options;
4890
5096
  constructor(options = {}) {
4891
5097
  this.options = {
4892
- maxParallel: options.maxParallel ?? 4,
4893
- timeout: options.timeout ?? 3e5,
4894
- // 5 minutes
5098
+ maxParallel: options.maxParallel ?? DEFAULT_MAX_PARALLEL,
5099
+ timeout: options.timeout ?? DEFAULT_TIMEOUT_MS,
4895
5100
  sharedContext: options.sharedContext ?? true,
4896
5101
  continueOnError: options.continueOnError ?? true,
4897
5102
  events: options.events,
@@ -4946,23 +5151,41 @@ var init_subagent_adapter = __esm({
4946
5151
  taskCount: tasks.length,
4947
5152
  maxParallel: this.options.maxParallel
4948
5153
  });
4949
- const sortedTasks = [...tasks].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
4950
- const results = [];
5154
+ const tasksWithIndex = tasks.map((task, index) => ({ task, index }));
5155
+ const sortedTasks = [...tasksWithIndex].sort(
5156
+ (a, b) => (a.task.priority ?? 0) - (b.task.priority ?? 0)
5157
+ );
5158
+ const results = new Array(tasks.length);
4951
5159
  const batchSize = this.options.maxParallel;
4952
5160
  for (let i = 0; i < sortedTasks.length; i += batchSize) {
4953
5161
  const batch = sortedTasks.slice(i, i + batchSize);
4954
- const batchResults = await this.executeBatch(batch);
4955
- results.push(...batchResults);
5162
+ const batchResults = await this.executeBatch(batch.map((item) => item.task));
5163
+ batch.forEach((item, idx) => {
5164
+ results[item.index] = batchResults[idx];
5165
+ });
4956
5166
  if (this.options.sharedContext && i + batchSize < sortedTasks.length) {
4957
5167
  await this.propagateContext(batchResults);
4958
5168
  }
4959
5169
  }
5170
+ const finalResults = results.map((result, index) => {
5171
+ if (result) {
5172
+ return result;
5173
+ }
5174
+ return {
5175
+ task: tasks[index]?.task ?? "unknown",
5176
+ role: tasks[index]?.config.role ?? "custom",
5177
+ content: "",
5178
+ success: false,
5179
+ error: "Subagent result missing",
5180
+ latencyMs: 0
5181
+ };
5182
+ });
4960
5183
  logger.info("Parallel execution complete", {
4961
5184
  taskCount: tasks.length,
4962
- successCount: results.filter((r) => r.success).length,
5185
+ successCount: finalResults.filter((r) => r.success).length,
4963
5186
  totalLatencyMs: Date.now() - startTime
4964
5187
  });
4965
- return results;
5188
+ return finalResults;
4966
5189
  }
4967
5190
  /**
4968
5191
  * Execute tasks sequentially with context propagation
@@ -4980,18 +5203,27 @@ var init_subagent_adapter = __esm({
4980
5203
  const results = [];
4981
5204
  let accumulatedContext = "";
4982
5205
  for (const task of tasks) {
4983
- const taskWithContext = {
4984
- ...task,
4985
- context: task.context ? `${task.context}
5206
+ let combinedContext;
5207
+ if (task.context && accumulatedContext) {
5208
+ combinedContext = `${task.context}
4986
5209
 
4987
5210
  Previous results:
4988
- ${accumulatedContext}` : accumulatedContext || void 0
5211
+ ${accumulatedContext}`;
5212
+ } else if (task.context) {
5213
+ combinedContext = task.context;
5214
+ } else if (accumulatedContext) {
5215
+ combinedContext = accumulatedContext;
5216
+ }
5217
+ const taskWithContext = {
5218
+ ...task,
5219
+ context: combinedContext
4989
5220
  };
4990
5221
  const result = await this.executeTask(taskWithContext);
4991
5222
  results.push(result);
4992
5223
  if (result.success && this.options.sharedContext) {
5224
+ const truncated = result.content.length > 500;
4993
5225
  accumulatedContext += `
4994
- [${result.role}]: ${result.content.substring(0, 500)}...
5226
+ [${result.role}]: ${result.content.substring(0, 500)}${truncated ? "..." : ""}
4995
5227
  `;
4996
5228
  }
4997
5229
  if (!result.success && !this.options.continueOnError) {
@@ -5017,6 +5249,7 @@ ${accumulatedContext}` : accumulatedContext || void 0
5017
5249
  const startTime = Date.now();
5018
5250
  const subagentKey = this.getSubagentKey(task.config);
5019
5251
  const taskId = `${subagentKey}:${Date.now()}`;
5252
+ let release = null;
5020
5253
  this.emitEvent("task_start", task.task);
5021
5254
  this.emitEvent("state_change", {
5022
5255
  previousState: "idle",
@@ -5029,7 +5262,9 @@ ${accumulatedContext}` : accumulatedContext || void 0
5029
5262
  message: `Starting ${task.config.role} task`
5030
5263
  });
5031
5264
  try {
5032
- const subagent = await this.getOrCreateSubagent(task.config);
5265
+ const acquired = await this.acquireSubagent(task.config);
5266
+ const subagent = acquired.subagent;
5267
+ release = acquired.release;
5033
5268
  const prompt = task.context ? `Context:
5034
5269
  ${task.context}
5035
5270
 
@@ -5120,6 +5355,10 @@ ${task.task}` : task.task;
5120
5355
  error: errorMessage,
5121
5356
  latencyMs
5122
5357
  };
5358
+ } finally {
5359
+ if (release) {
5360
+ release();
5361
+ }
5123
5362
  }
5124
5363
  }
5125
5364
  /**
@@ -5142,7 +5381,11 @@ ${task.task}` : task.task;
5142
5381
  * Propagate context from completed tasks
5143
5382
  */
5144
5383
  async propagateContext(results) {
5145
- const contextSummary = results.filter((r) => r.success).map((r) => `[${r.role}]: ${r.content.substring(0, 200)}...`).join("\n");
5384
+ const contextSummary = results.filter((r) => r.success).map((r) => {
5385
+ const truncated = r.content.length > 200;
5386
+ const summary = r.content.substring(0, 200);
5387
+ return `[${r.role}]: ${summary}${truncated ? "..." : ""}`;
5388
+ }).join("\n");
5146
5389
  if (contextSummary && this.orchestrator?.setSharedContext) {
5147
5390
  try {
5148
5391
  await this.orchestrator.setSharedContext(contextSummary);
@@ -5168,7 +5411,7 @@ ${task.task}` : task.task;
5168
5411
  const { createSubagent } = await import('@defai.digital/ax-cli/sdk');
5169
5412
  const subagent = createSubagent(config.role, {
5170
5413
  systemPrompt: config.instructions,
5171
- maxToolRounds: config.maxToolRounds ?? 100
5414
+ maxToolRounds: config.maxToolRounds ?? DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS
5172
5415
  });
5173
5416
  this.subagents.set(key, subagent);
5174
5417
  logger.debug("Created new subagent", {
@@ -5185,11 +5428,70 @@ ${task.task}` : task.task;
5185
5428
  throw error;
5186
5429
  }
5187
5430
  }
5431
+ /**
5432
+ * Create a temporary subagent instance. Used when the cached subagent is busy.
5433
+ */
5434
+ async createEphemeralSubagent(config) {
5435
+ const { createSubagent } = await import('@defai.digital/ax-cli/sdk');
5436
+ return createSubagent(config.role, {
5437
+ systemPrompt: config.instructions,
5438
+ maxToolRounds: config.maxToolRounds ?? DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS
5439
+ });
5440
+ }
5441
+ /**
5442
+ * Acquire a subagent instance for a task. If a cached instance is already
5443
+ * running another task, create a short-lived one to avoid interleaved
5444
+ * streams from concurrent executions.
5445
+ */
5446
+ async acquireSubagent(config) {
5447
+ const key = this.getSubagentKey(config);
5448
+ if (!this.busySubagents.has(key)) {
5449
+ this.busySubagents.add(key);
5450
+ let subagent;
5451
+ try {
5452
+ subagent = await this.getOrCreateSubagent(config);
5453
+ } catch (error) {
5454
+ this.busySubagents.delete(key);
5455
+ throw error;
5456
+ }
5457
+ return {
5458
+ subagent,
5459
+ release: () => this.releaseSubagent(key)
5460
+ };
5461
+ }
5462
+ logger.debug("Cached subagent busy, creating ephemeral instance", { key });
5463
+ const ephemeral = await this.createEphemeralSubagent(config);
5464
+ return {
5465
+ subagent: ephemeral,
5466
+ release: () => {
5467
+ try {
5468
+ if (ephemeral.dispose) {
5469
+ const result = ephemeral.dispose();
5470
+ if (result instanceof Promise) {
5471
+ result.catch((err) => logger.warn("Error disposing ephemeral subagent", { error: err instanceof Error ? err.message : String(err) }));
5472
+ }
5473
+ }
5474
+ } catch (error) {
5475
+ logger.warn("Error disposing ephemeral subagent", {
5476
+ error: error instanceof Error ? error.message : String(error)
5477
+ });
5478
+ }
5479
+ }
5480
+ };
5481
+ }
5482
+ /**
5483
+ * Release a cached subagent so it can be reused.
5484
+ */
5485
+ releaseSubagent(key) {
5486
+ this.busySubagents.delete(key);
5487
+ }
5188
5488
  /**
5189
5489
  * Generate unique key for subagent config
5190
5490
  */
5191
5491
  getSubagentKey(config) {
5192
- return `${config.role}:${config.specialization || "default"}`;
5492
+ const instructionsHash = config.instructions ? createHash("sha256").update(config.instructions).digest("hex").slice(0, 12) : "noinstr";
5493
+ const maxToolRounds = config.maxToolRounds ?? DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS;
5494
+ return `${config.role}:${config.specialization || "default"}:${maxToolRounds}:${instructionsHash}`;
5193
5495
  }
5194
5496
  /**
5195
5497
  * Ensure orchestrator is initialized
@@ -5245,8 +5547,11 @@ ${task.task}` : task.task;
5245
5547
  }
5246
5548
  /**
5247
5549
  * Cleanup all subagents
5550
+ * BUG FIX: Continue cleanup even if individual subagent disposal fails,
5551
+ * and report all errors at the end to ensure no resources are leaked.
5248
5552
  */
5249
5553
  async destroy() {
5554
+ const errors = [];
5250
5555
  for (const [key, subagent] of this.subagents) {
5251
5556
  try {
5252
5557
  if (subagent.dispose) {
@@ -5257,24 +5562,54 @@ ${task.task}` : task.task;
5257
5562
  }
5258
5563
  logger.debug("Subagent disposed", { key });
5259
5564
  } catch (error) {
5565
+ const errorMsg = error instanceof Error ? error.message : String(error);
5260
5566
  logger.warn("Error disposing subagent", {
5261
5567
  key,
5262
- error: error instanceof Error ? error.message : String(error)
5568
+ error: errorMsg
5263
5569
  });
5570
+ errors.push({ key, error: errorMsg });
5264
5571
  }
5265
5572
  }
5573
+ const orchestrator = this.orchestrator;
5266
5574
  this.subagents.clear();
5575
+ this.busySubagents.clear();
5576
+ if (orchestrator && typeof orchestrator.dispose === "function") {
5577
+ try {
5578
+ await orchestrator.dispose();
5579
+ } catch (error) {
5580
+ logger.warn("Error disposing subagent orchestrator", {
5581
+ error: error instanceof Error ? error.message : String(error)
5582
+ });
5583
+ }
5584
+ } else if (orchestrator && typeof orchestrator.shutdown === "function") {
5585
+ try {
5586
+ await orchestrator.shutdown();
5587
+ } catch (error) {
5588
+ logger.warn("Error shutting down subagent orchestrator", {
5589
+ error: error instanceof Error ? error.message : String(error)
5590
+ });
5591
+ }
5592
+ }
5267
5593
  this.orchestrator = null;
5594
+ if (errors.length > 0) {
5595
+ logger.error("SubagentAdapter cleanup had errors", {
5596
+ errorCount: errors.length,
5597
+ errors
5598
+ });
5599
+ }
5268
5600
  logger.debug("SubagentAdapter destroyed");
5269
5601
  }
5270
5602
  };
5271
5603
  }
5272
5604
  });
5273
- var CheckpointAdapter;
5605
+ var DEFAULT_CHECKPOINT_DIR, DEFAULT_MAX_CHECKPOINTS, FLUSH_TIMEOUT_MS, CheckpointAdapter;
5274
5606
  var init_checkpoint_adapter = __esm({
5275
5607
  "src/integrations/ax-cli-sdk/checkpoint-adapter.ts"() {
5276
5608
  init_esm_shims();
5277
5609
  init_logger();
5610
+ DEFAULT_CHECKPOINT_DIR = ".automatosx/checkpoints";
5611
+ DEFAULT_MAX_CHECKPOINTS = 10;
5612
+ FLUSH_TIMEOUT_MS = 5e3;
5278
5613
  CheckpointAdapter = class {
5279
5614
  checkpointDir;
5280
5615
  sdkCheckpointManager = null;
@@ -5282,11 +5617,12 @@ var init_checkpoint_adapter = __esm({
5282
5617
  options;
5283
5618
  autoSaveTimer = null;
5284
5619
  pendingCheckpoint = null;
5620
+ fsLock = Promise.resolve();
5285
5621
  constructor(options = {}) {
5286
5622
  this.options = {
5287
- checkpointDir: options.checkpointDir ?? ".automatosx/checkpoints",
5623
+ checkpointDir: options.checkpointDir ?? DEFAULT_CHECKPOINT_DIR,
5288
5624
  autoSaveInterval: options.autoSaveInterval ?? 0,
5289
- maxCheckpoints: options.maxCheckpoints ?? 10,
5625
+ maxCheckpoints: options.maxCheckpoints ?? DEFAULT_MAX_CHECKPOINTS,
5290
5626
  compress: options.compress ?? false
5291
5627
  };
5292
5628
  this.checkpointDir = this.options.checkpointDir;
@@ -5328,8 +5664,10 @@ var init_checkpoint_adapter = __esm({
5328
5664
  });
5329
5665
  }
5330
5666
  }
5331
- await this.saveToFileSystem(checkpoint);
5332
- await this.cleanupOldCheckpoints(workflowId);
5667
+ await this.withFsLock(async () => {
5668
+ await this.saveToFileSystem(checkpoint);
5669
+ await this.cleanupOldCheckpoints(workflowId);
5670
+ });
5333
5671
  logger.info("Checkpoint saved", {
5334
5672
  workflowId,
5335
5673
  phase: checkpoint.phase,
@@ -5387,23 +5725,32 @@ var init_checkpoint_adapter = __esm({
5387
5725
  });
5388
5726
  }
5389
5727
  }
5390
- await this.deleteFromFileSystem(workflowId);
5728
+ await this.withFsLock(() => this.deleteFromFileSystem(workflowId));
5391
5729
  logger.info("Checkpoints deleted", { workflowId });
5392
5730
  }
5393
5731
  /**
5394
5732
  * List all checkpoints for a workflow
5733
+ *
5734
+ * BUG FIX (v11.3.3): The previous pattern `startsWith(workflowId-)` would match
5735
+ * similar workflow IDs. For example, "auth" would match "auth-v2-1.json".
5736
+ * Now we verify that the suffix after the workflowId is a numeric phase.
5395
5737
  */
5396
5738
  async list(workflowId) {
5397
5739
  try {
5398
- await fs4.mkdir(this.checkpointDir, { recursive: true });
5399
- const files = await fs4.readdir(this.checkpointDir);
5400
- const matchingFiles = files.filter(
5401
- (file) => file.startsWith(workflowId) && file.endsWith(".json")
5402
- );
5740
+ await fs5.mkdir(this.checkpointDir, { recursive: true });
5741
+ const files = await fs5.readdir(this.checkpointDir);
5742
+ const prefix = `${workflowId}-`;
5743
+ const matchingFiles = files.filter((file) => {
5744
+ if (!file.startsWith(prefix) || !file.endsWith(".json")) {
5745
+ return false;
5746
+ }
5747
+ const middle = file.slice(prefix.length, -5);
5748
+ return /^\d+$/.test(middle);
5749
+ });
5403
5750
  const results = await Promise.all(
5404
5751
  matchingFiles.map(async (file) => {
5405
5752
  try {
5406
- const content = await fs4.readFile(
5753
+ const content = await fs5.readFile(
5407
5754
  path4.join(this.checkpointDir, file),
5408
5755
  "utf-8"
5409
5756
  );
@@ -5433,6 +5780,13 @@ var init_checkpoint_adapter = __esm({
5433
5780
  async savePhase(workflowId, phaseId, result, workflow) {
5434
5781
  let checkpoint = await this.load(workflowId);
5435
5782
  const phaseIndex = workflow.phases.findIndex((p) => p.id === phaseId);
5783
+ if (phaseIndex === -1) {
5784
+ logger.warn("Phase not found in workflow", {
5785
+ workflowId,
5786
+ phaseId,
5787
+ availablePhases: workflow.phases.map((p) => p.id)
5788
+ });
5789
+ }
5436
5790
  if (!checkpoint) {
5437
5791
  checkpoint = {
5438
5792
  id: `${workflowId}-${Date.now()}`,
@@ -5449,9 +5803,12 @@ var init_checkpoint_adapter = __esm({
5449
5803
  }
5450
5804
  if (result.success) {
5451
5805
  checkpoint.completedTasks.push(phaseId);
5452
- checkpoint.phase = phaseIndex + 1;
5806
+ if (phaseIndex >= 0) {
5807
+ checkpoint.phase = phaseIndex + 1;
5808
+ }
5809
+ const phaseLabel = phaseIndex >= 0 ? `Phase ${phaseIndex + 1}` : "Unknown Phase";
5453
5810
  checkpoint.context += `
5454
- [Phase ${phaseIndex + 1}: ${phaseId}]
5811
+ [${phaseLabel}: ${phaseId}]
5455
5812
  ${result.content.substring(0, 1e3)}
5456
5813
  `;
5457
5814
  if (result.tokensUsed && checkpoint.tokensUsed) {
@@ -5498,11 +5855,11 @@ ${result.content.substring(0, 1e3)}
5498
5855
  await this.sdkCheckpointManager.delete(workflowId);
5499
5856
  }
5500
5857
  async saveToFileSystem(checkpoint) {
5501
- await fs4.mkdir(this.checkpointDir, { recursive: true });
5858
+ await fs5.mkdir(this.checkpointDir, { recursive: true });
5502
5859
  const filename = `${checkpoint.workflowId}-${checkpoint.phase}.json`;
5503
5860
  const filepath = path4.join(this.checkpointDir, filename);
5504
5861
  const content = JSON.stringify(checkpoint, null, 2);
5505
- await fs4.writeFile(filepath, content, "utf-8");
5862
+ await this.atomicWrite(filepath, content);
5506
5863
  }
5507
5864
  async loadFromFileSystem(workflowId) {
5508
5865
  try {
@@ -5512,12 +5869,21 @@ ${result.content.substring(0, 1e3)}
5512
5869
  return null;
5513
5870
  }
5514
5871
  }
5872
+ /**
5873
+ * BUG FIX (v11.3.3): Use same precise matching as list() to avoid
5874
+ * deleting checkpoints for workflows with similar IDs.
5875
+ */
5515
5876
  async deleteFromFileSystem(workflowId) {
5516
5877
  try {
5517
- const files = await fs4.readdir(this.checkpointDir);
5878
+ const files = await fs5.readdir(this.checkpointDir);
5879
+ const prefix = `${workflowId}-`;
5518
5880
  for (const file of files) {
5519
- if (file.startsWith(workflowId) && file.endsWith(".json")) {
5520
- await fs4.unlink(path4.join(this.checkpointDir, file));
5881
+ if (!file.startsWith(prefix) || !file.endsWith(".json")) {
5882
+ continue;
5883
+ }
5884
+ const middle = file.slice(prefix.length, -5);
5885
+ if (/^\d+$/.test(middle)) {
5886
+ await fs5.unlink(path4.join(this.checkpointDir, file));
5521
5887
  }
5522
5888
  }
5523
5889
  } catch (error) {
@@ -5535,7 +5901,7 @@ ${result.content.substring(0, 1e3)}
5535
5901
  for (const checkpoint of toDelete) {
5536
5902
  try {
5537
5903
  const filename = `${checkpoint.workflowId}-${checkpoint.phase}.json`;
5538
- await fs4.unlink(path4.join(this.checkpointDir, filename));
5904
+ await fs5.unlink(path4.join(this.checkpointDir, filename));
5539
5905
  } catch {
5540
5906
  }
5541
5907
  }
@@ -5559,8 +5925,11 @@ ${result.content.substring(0, 1e3)}
5559
5925
  }
5560
5926
  }
5561
5927
  setupAutoSave() {
5928
+ let autoSaveRunning = false;
5562
5929
  this.autoSaveTimer = setInterval(async () => {
5930
+ if (autoSaveRunning) return;
5563
5931
  if (this.pendingCheckpoint) {
5932
+ autoSaveRunning = true;
5564
5933
  try {
5565
5934
  await this.save(
5566
5935
  this.pendingCheckpoint.workflowId,
@@ -5571,6 +5940,8 @@ ${result.content.substring(0, 1e3)}
5571
5940
  logger.warn("Auto-save failed", {
5572
5941
  error: error instanceof Error ? error.message : String(error)
5573
5942
  });
5943
+ } finally {
5944
+ autoSaveRunning = false;
5574
5945
  }
5575
5946
  }
5576
5947
  }, this.options.autoSaveInterval);
@@ -5584,6 +5955,7 @@ ${result.content.substring(0, 1e3)}
5584
5955
  /**
5585
5956
  * Cleanup resources
5586
5957
  * BUG FIX: Flush pending checkpoint before destroying to prevent data loss
5958
+ * BUG FIX: Add timeout to prevent hanging during cleanup
5587
5959
  */
5588
5960
  async destroy() {
5589
5961
  if (this.autoSaveTimer) {
@@ -5592,10 +5964,15 @@ ${result.content.substring(0, 1e3)}
5592
5964
  }
5593
5965
  if (this.pendingCheckpoint) {
5594
5966
  try {
5595
- await this.save(
5596
- this.pendingCheckpoint.workflowId,
5597
- this.pendingCheckpoint
5598
- );
5967
+ await Promise.race([
5968
+ this.save(
5969
+ this.pendingCheckpoint.workflowId,
5970
+ this.pendingCheckpoint
5971
+ ),
5972
+ new Promise(
5973
+ (_, reject) => setTimeout(() => reject(new Error("Checkpoint flush timed out")), FLUSH_TIMEOUT_MS)
5974
+ )
5975
+ ]);
5599
5976
  logger.debug("Pending checkpoint flushed on destroy", {
5600
5977
  workflowId: this.pendingCheckpoint.workflowId
5601
5978
  });
@@ -5609,12 +5986,44 @@ ${result.content.substring(0, 1e3)}
5609
5986
  this.sdkCheckpointManager = null;
5610
5987
  logger.debug("CheckpointAdapter destroyed");
5611
5988
  }
5989
+ /**
5990
+ * Serialize filesystem operations to avoid race conditions between auto-save and manual saves.
5991
+ */
5992
+ async withFsLock(fn) {
5993
+ const resultPromise = this.fsLock.then(fn, fn);
5994
+ this.fsLock = resultPromise.then(
5995
+ () => Promise.resolve(),
5996
+ () => Promise.resolve()
5997
+ );
5998
+ return resultPromise;
5999
+ }
6000
+ /**
6001
+ * Write checkpoint atomically to avoid partial files being read by list/load.
6002
+ * BUG FIX: Clean up temp file on rename failure to prevent resource leak.
6003
+ */
6004
+ async atomicWrite(filepath, content) {
6005
+ const tempFile = `${filepath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
6006
+ await fs5.writeFile(tempFile, content, "utf-8");
6007
+ try {
6008
+ await fs5.rename(tempFile, filepath);
6009
+ } catch (renameError) {
6010
+ try {
6011
+ await fs5.unlink(tempFile);
6012
+ } catch {
6013
+ }
6014
+ throw renameError;
6015
+ }
6016
+ }
5612
6017
  };
5613
6018
  }
5614
6019
  });
5615
6020
  function getInstructionsBridge(options) {
5616
6021
  if (!defaultBridge) {
5617
6022
  defaultBridge = new InstructionsBridge(options);
6023
+ } else if (options) {
6024
+ logger.warn("InstructionsBridge already initialized, ignoring new options", {
6025
+ hint: "Use resetInstructionsBridge() to recreate with new options"
6026
+ });
5618
6027
  }
5619
6028
  return defaultBridge;
5620
6029
  }
@@ -5646,8 +6055,9 @@ var init_instructions_bridge = __esm({
5646
6055
  * @returns Combined instructions from all sources
5647
6056
  */
5648
6057
  async getInstructions(agentName, additionalContext) {
6058
+ const cacheSignature = await this.buildCacheSignature(agentName);
5649
6059
  const cached = this.cache.get(agentName);
5650
- if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
6060
+ if (cached && cached.signature === cacheSignature && Date.now() - cached.timestamp < this.CACHE_TTL) {
5651
6061
  logger.debug("Using cached instructions", { agentName });
5652
6062
  if (additionalContext) {
5653
6063
  return {
@@ -5685,7 +6095,11 @@ var init_instructions_bridge = __esm({
5685
6095
  projectMemory: !!projectContext
5686
6096
  }
5687
6097
  };
5688
- this.cache.set(agentName, { instructions: combined, timestamp: Date.now() });
6098
+ this.cache.set(agentName, {
6099
+ instructions: combined,
6100
+ timestamp: Date.now(),
6101
+ signature: cacheSignature
6102
+ });
5689
6103
  logger.info("Instructions combined", {
5690
6104
  agentName,
5691
6105
  sources: combined.sources,
@@ -5710,13 +6124,13 @@ var init_instructions_bridge = __esm({
5710
6124
  if (additionalContext) {
5711
6125
  parts.push("## Additional Context\n\n" + additionalContext);
5712
6126
  }
5713
- let combined = parts.join("\n\n---\n\n");
6127
+ const combined = parts.join("\n\n---\n\n");
5714
6128
  if (combined.length > this.options.maxContextLength) {
5715
- combined = combined.substring(0, this.options.maxContextLength - 100) + "\n\n[Context truncated due to length limit]";
5716
6129
  logger.warn("Instructions truncated", {
5717
- originalLength: parts.join("\n\n---\n\n").length,
6130
+ originalLength: combined.length,
5718
6131
  truncatedTo: this.options.maxContextLength
5719
6132
  });
6133
+ return combined.substring(0, this.options.maxContextLength - 100) + "\n\n[Context truncated due to length limit]";
5720
6134
  }
5721
6135
  return combined;
5722
6136
  }
@@ -5726,7 +6140,7 @@ var init_instructions_bridge = __esm({
5726
6140
  async loadAgentProfile(agentName) {
5727
6141
  const profilePath = path4.join(this.options.agentsDir, `${agentName}.ax.yaml`);
5728
6142
  try {
5729
- const content = await fs4.readFile(profilePath, "utf-8");
6143
+ const content = await fs5.readFile(profilePath, "utf-8");
5730
6144
  const profile = this.parseYamlProfile(content);
5731
6145
  logger.debug("Agent profile loaded", { agentName, profilePath });
5732
6146
  return profile;
@@ -5761,7 +6175,7 @@ var init_instructions_bridge = __esm({
5761
6175
  }
5762
6176
  }
5763
6177
  try {
5764
- const content = await fs4.readFile(this.options.axCliCustomPath, "utf-8");
6178
+ const content = await fs5.readFile(this.options.axCliCustomPath, "utf-8");
5765
6179
  logger.debug("Custom instructions loaded from file", {
5766
6180
  path: this.options.axCliCustomPath
5767
6181
  });
@@ -5805,6 +6219,11 @@ var init_instructions_bridge = __esm({
5805
6219
  }
5806
6220
  /**
5807
6221
  * Parse YAML profile content (simple parser)
6222
+ *
6223
+ * BUG FIX (v11.3.3):
6224
+ * - Support keys with hyphens/underscores (e.g., display_name, my-key)
6225
+ * - Strip quotes from string values
6226
+ * - Handle empty values correctly
5808
6227
  */
5809
6228
  parseYamlProfile(content) {
5810
6229
  const lines = content.split("\n");
@@ -5813,27 +6232,33 @@ var init_instructions_bridge = __esm({
5813
6232
  let instructionsBuffer = [];
5814
6233
  let inInstructions = false;
5815
6234
  for (const line of lines) {
5816
- const match = line.match(/^(\w+):\s*(.*)$/);
6235
+ const match = line.match(/^([\w-]+):\s*(.*)$/);
5817
6236
  if (match) {
5818
- const [, key, value] = match;
5819
- if (inInstructions && key !== "instructions") {
6237
+ const [, rawKey, rawValue] = match;
6238
+ const key = rawKey ?? "";
6239
+ let value = rawValue ?? "";
6240
+ if (value && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
6241
+ value = value.slice(1, -1);
6242
+ }
6243
+ const normalizedKey = key.replace(/-/g, "_");
6244
+ if (inInstructions && normalizedKey !== "instructions") {
5820
6245
  profile.instructions = instructionsBuffer.join("\n").trim();
5821
6246
  instructionsBuffer = [];
5822
6247
  inInstructions = false;
5823
6248
  }
5824
- if (key === "instructions") {
6249
+ if (normalizedKey === "instructions") {
5825
6250
  inInstructions = true;
5826
- currentKey = key;
6251
+ currentKey = normalizedKey;
5827
6252
  if (value && value.startsWith("|")) ; else if (value) {
5828
6253
  profile.instructions = value;
5829
6254
  inInstructions = false;
5830
6255
  }
5831
- } else if (key === "expertise") {
6256
+ } else if (normalizedKey === "expertise") {
5832
6257
  profile.expertise = [];
5833
- currentKey = key;
5834
- } else if (key && value !== void 0) {
5835
- profile[key] = value;
5836
- currentKey = key;
6258
+ currentKey = normalizedKey;
6259
+ } else if (normalizedKey && value !== void 0) {
6260
+ profile[normalizedKey] = value;
6261
+ currentKey = normalizedKey;
5837
6262
  }
5838
6263
  } else if (inInstructions) {
5839
6264
  instructionsBuffer.push(line.replace(/^ /, ""));
@@ -5880,8 +6305,8 @@ ${profile.expertise.map((e) => `- ${e}`).join("\n")}
5880
6305
  ${profile.instructions}
5881
6306
  `;
5882
6307
  try {
5883
- await fs4.mkdir(path4.dirname(this.options.axCliCustomPath), { recursive: true });
5884
- await fs4.writeFile(this.options.axCliCustomPath, customContent, "utf-8");
6308
+ await fs5.mkdir(path4.dirname(this.options.axCliCustomPath), { recursive: true });
6309
+ await fs5.writeFile(this.options.axCliCustomPath, customContent, "utf-8");
5885
6310
  logger.info("Agent synced to ax-cli custom instructions", {
5886
6311
  agentName,
5887
6312
  path: this.options.axCliCustomPath
@@ -5923,6 +6348,26 @@ ${profile.instructions}
5923
6348
  this.cache.delete(agentName);
5924
6349
  logger.debug("Agent cache cleared", { agentName });
5925
6350
  }
6351
+ /**
6352
+ * Build a cache signature from underlying file mtimes to invalidate cache when
6353
+ * agent profiles or custom instructions change on disk.
6354
+ */
6355
+ async buildCacheSignature(agentName) {
6356
+ const profilePath = path4.join(this.options.agentsDir, `${agentName}.ax.yaml`);
6357
+ const [profileSig, customSig] = await Promise.all([
6358
+ this.getFileSignature(profilePath),
6359
+ this.getFileSignature(this.options.axCliCustomPath)
6360
+ ]);
6361
+ return [profileSig, customSig, this.options.includeProjectMemory ? "mem" : "nomem"].join("|");
6362
+ }
6363
+ async getFileSignature(filePath) {
6364
+ try {
6365
+ const stats = await fs5.stat(filePath);
6366
+ return `${stats.mtimeMs}:${stats.size}`;
6367
+ } catch {
6368
+ return "missing";
6369
+ }
6370
+ }
5926
6371
  };
5927
6372
  defaultBridge = null;
5928
6373
  }
@@ -5932,14 +6377,19 @@ ${profile.instructions}
5932
6377
  function getAxCliMCPManager(options) {
5933
6378
  if (!defaultManager2) {
5934
6379
  defaultManager2 = new AxCliMCPManager(options);
6380
+ } else if (options) {
6381
+ logger.warn("AxCliMCPManager already initialized, ignoring new options", {
6382
+ hint: "Use resetAxCliMCPManager() to recreate with new options"
6383
+ });
5935
6384
  }
5936
6385
  return defaultManager2;
5937
6386
  }
5938
- var AxCliMCPManager, defaultManager2;
6387
+ var SDK_NOT_AVAILABLE_ERROR, AxCliMCPManager, defaultManager2;
5939
6388
  var init_mcp_manager = __esm({
5940
6389
  "src/integrations/ax-cli-sdk/mcp-manager.ts"() {
5941
6390
  init_esm_shims();
5942
6391
  init_logger();
6392
+ SDK_NOT_AVAILABLE_ERROR = "ax-cli SDK not available";
5943
6393
  AxCliMCPManager = class {
5944
6394
  sdkAvailable = null;
5945
6395
  mcpModule = null;
@@ -5976,7 +6426,7 @@ var init_mcp_manager = __esm({
5976
6426
  async getTemplateNames() {
5977
6427
  try {
5978
6428
  if (!await this.isAvailable()) {
5979
- return { ok: false, error: new Error("ax-cli SDK not available") };
6429
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
5980
6430
  }
5981
6431
  const { MCPManager } = this.mcpModule;
5982
6432
  if (MCPManager?.getTemplateNames) {
@@ -6013,7 +6463,7 @@ var init_mcp_manager = __esm({
6013
6463
  async getTemplate(name) {
6014
6464
  try {
6015
6465
  if (!await this.isAvailable()) {
6016
- return { ok: false, error: new Error("ax-cli SDK not available") };
6466
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6017
6467
  }
6018
6468
  const { MCPManager } = this.mcpModule;
6019
6469
  if (MCPManager?.getTemplate) {
@@ -6037,7 +6487,7 @@ var init_mcp_manager = __esm({
6037
6487
  async getTemplatesByCategory(category) {
6038
6488
  try {
6039
6489
  if (!await this.isAvailable()) {
6040
- return { ok: false, error: new Error("ax-cli SDK not available") };
6490
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6041
6491
  }
6042
6492
  const { MCPManager } = this.mcpModule;
6043
6493
  if (MCPManager?.getTemplatesByCategory) {
@@ -6058,7 +6508,7 @@ var init_mcp_manager = __esm({
6058
6508
  async searchTemplates(keyword) {
6059
6509
  try {
6060
6510
  if (!await this.isAvailable()) {
6061
- return { ok: false, error: new Error("ax-cli SDK not available") };
6511
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6062
6512
  }
6063
6513
  const { MCPManager } = this.mcpModule;
6064
6514
  if (MCPManager?.searchTemplates) {
@@ -6079,7 +6529,7 @@ var init_mcp_manager = __esm({
6079
6529
  async generateConfigFromTemplate(templateName, env) {
6080
6530
  try {
6081
6531
  if (!await this.isAvailable()) {
6082
- return { ok: false, error: new Error("ax-cli SDK not available") };
6532
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6083
6533
  }
6084
6534
  const { MCPManager } = this.mcpModule;
6085
6535
  if (MCPManager?.generateConfigFromTemplate) {
@@ -6100,7 +6550,7 @@ var init_mcp_manager = __esm({
6100
6550
  async loadConfig() {
6101
6551
  try {
6102
6552
  if (!await this.isAvailable()) {
6103
- return { ok: false, error: new Error("ax-cli SDK not available") };
6553
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6104
6554
  }
6105
6555
  const { MCPManager } = this.mcpModule;
6106
6556
  if (MCPManager?.loadMCPConfig) {
@@ -6121,7 +6571,7 @@ var init_mcp_manager = __esm({
6121
6571
  async addServer(name, config) {
6122
6572
  try {
6123
6573
  if (!await this.isAvailable()) {
6124
- return { ok: false, error: new Error("ax-cli SDK not available") };
6574
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6125
6575
  }
6126
6576
  const { MCPManager } = this.mcpModule;
6127
6577
  if (MCPManager?.addMCPServer) {
@@ -6143,7 +6593,7 @@ var init_mcp_manager = __esm({
6143
6593
  async removeServer(name) {
6144
6594
  try {
6145
6595
  if (!await this.isAvailable()) {
6146
- return { ok: false, error: new Error("ax-cli SDK not available") };
6596
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6147
6597
  }
6148
6598
  const { MCPManager } = this.mcpModule;
6149
6599
  if (MCPManager?.removeMCPServer) {
@@ -6165,7 +6615,7 @@ var init_mcp_manager = __esm({
6165
6615
  async getPredefinedServers() {
6166
6616
  try {
6167
6617
  if (!await this.isAvailable()) {
6168
- return { ok: false, error: new Error("ax-cli SDK not available") };
6618
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6169
6619
  }
6170
6620
  const { MCPManager } = this.mcpModule;
6171
6621
  if (MCPManager?.PREDEFINED_SERVERS) {
@@ -6217,7 +6667,7 @@ var init_mcp_manager = __esm({
6217
6667
  async getServerStatus(name) {
6218
6668
  try {
6219
6669
  if (!await this.isAvailable()) {
6220
- return { ok: false, error: new Error("ax-cli SDK not available") };
6670
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6221
6671
  }
6222
6672
  const { MCPManagerV2 } = this.mcpModule;
6223
6673
  if (MCPManagerV2?.getServerStatus) {
@@ -6251,7 +6701,7 @@ var init_mcp_manager = __esm({
6251
6701
  async getAllServerStatuses() {
6252
6702
  try {
6253
6703
  if (!await this.isAvailable()) {
6254
- return { ok: false, error: new Error("ax-cli SDK not available") };
6704
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6255
6705
  }
6256
6706
  const { MCPManagerV2 } = this.mcpModule;
6257
6707
  if (MCPManagerV2?.getAllServerStatuses) {
@@ -6275,7 +6725,7 @@ var init_mcp_manager = __esm({
6275
6725
  async healthCheck() {
6276
6726
  try {
6277
6727
  if (!await this.isAvailable()) {
6278
- return { ok: false, error: new Error("ax-cli SDK not available") };
6728
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6279
6729
  }
6280
6730
  const { MCPManagerV2 } = this.mcpModule;
6281
6731
  if (MCPManagerV2?.healthCheck) {
@@ -6300,7 +6750,7 @@ var init_mcp_manager = __esm({
6300
6750
  async startServer(name) {
6301
6751
  try {
6302
6752
  if (!await this.isAvailable()) {
6303
- return { ok: false, error: new Error("ax-cli SDK not available") };
6753
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6304
6754
  }
6305
6755
  const { MCPManagerV2 } = this.mcpModule;
6306
6756
  if (MCPManagerV2?.startServer) {
@@ -6315,9 +6765,16 @@ var init_mcp_manager = __esm({
6315
6765
  }
6316
6766
  return { ok: false, error: new Error("startServer not available in SDK") };
6317
6767
  } catch (error) {
6768
+ const errorMessage = error instanceof Error ? error.message : String(error);
6769
+ this.serverStatuses.set(name, {
6770
+ name,
6771
+ status: "error",
6772
+ lastHealthCheck: /* @__PURE__ */ new Date(),
6773
+ error: errorMessage
6774
+ });
6318
6775
  return {
6319
6776
  ok: false,
6320
- error: error instanceof Error ? error : new Error(String(error))
6777
+ error: error instanceof Error ? error : new Error(errorMessage)
6321
6778
  };
6322
6779
  }
6323
6780
  }
@@ -6327,7 +6784,7 @@ var init_mcp_manager = __esm({
6327
6784
  async stopServer(name) {
6328
6785
  try {
6329
6786
  if (!await this.isAvailable()) {
6330
- return { ok: false, error: new Error("ax-cli SDK not available") };
6787
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6331
6788
  }
6332
6789
  const { MCPManagerV2 } = this.mcpModule;
6333
6790
  if (MCPManagerV2?.stopServer) {
@@ -6342,9 +6799,16 @@ var init_mcp_manager = __esm({
6342
6799
  }
6343
6800
  return { ok: false, error: new Error("stopServer not available in SDK") };
6344
6801
  } catch (error) {
6802
+ const errorMessage = error instanceof Error ? error.message : String(error);
6803
+ this.serverStatuses.set(name, {
6804
+ name,
6805
+ status: "error",
6806
+ lastHealthCheck: /* @__PURE__ */ new Date(),
6807
+ error: errorMessage
6808
+ });
6345
6809
  return {
6346
6810
  ok: false,
6347
- error: error instanceof Error ? error : new Error(String(error))
6811
+ error: error instanceof Error ? error : new Error(errorMessage)
6348
6812
  };
6349
6813
  }
6350
6814
  }
@@ -6354,7 +6818,7 @@ var init_mcp_manager = __esm({
6354
6818
  async restartServer(name) {
6355
6819
  try {
6356
6820
  if (!await this.isAvailable()) {
6357
- return { ok: false, error: new Error("ax-cli SDK not available") };
6821
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6358
6822
  }
6359
6823
  const { MCPManagerV2 } = this.mcpModule;
6360
6824
  if (MCPManagerV2?.restartServer) {
@@ -6373,9 +6837,16 @@ var init_mcp_manager = __esm({
6373
6837
  }
6374
6838
  return this.startServer(name);
6375
6839
  } catch (error) {
6840
+ const errorMessage = error instanceof Error ? error.message : String(error);
6841
+ this.serverStatuses.set(name, {
6842
+ name,
6843
+ status: "error",
6844
+ lastHealthCheck: /* @__PURE__ */ new Date(),
6845
+ error: errorMessage
6846
+ });
6376
6847
  return {
6377
6848
  ok: false,
6378
- error: error instanceof Error ? error : new Error(String(error))
6849
+ error: error instanceof Error ? error : new Error(errorMessage)
6379
6850
  };
6380
6851
  }
6381
6852
  }
@@ -6385,7 +6856,7 @@ var init_mcp_manager = __esm({
6385
6856
  async shutdown() {
6386
6857
  try {
6387
6858
  if (!await this.isAvailable()) {
6388
- return { ok: false, error: new Error("ax-cli SDK not available") };
6859
+ return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
6389
6860
  }
6390
6861
  const { MCPManagerV2 } = this.mcpModule;
6391
6862
  if (MCPManagerV2?.shutdown) {
@@ -6401,11 +6872,27 @@ var init_mcp_manager = __esm({
6401
6872
  return { ok: true, value: void 0 };
6402
6873
  }
6403
6874
  const serversResult = await this.listServers();
6404
- if (serversResult.ok) {
6405
- for (const name of serversResult.value) {
6406
- await this.stopServer(name);
6875
+ if (!serversResult.ok) {
6876
+ logger.warn("Cannot enumerate servers for shutdown", {
6877
+ error: serversResult.error.message
6878
+ });
6879
+ return serversResult;
6880
+ }
6881
+ const errors = [];
6882
+ for (const name of serversResult.value) {
6883
+ const stopResult = await this.stopServer(name);
6884
+ if (!stopResult.ok) {
6885
+ errors.push({
6886
+ name,
6887
+ error: stopResult.error.message
6888
+ });
6407
6889
  }
6408
6890
  }
6891
+ if (errors.length > 0) {
6892
+ const errorMessage = `Failed to stop ${errors.length} server(s): ${errors.map((e) => `${e.name} (${e.error})`).join(", ")}`;
6893
+ logger.warn("Partial MCP shutdown failure", { errors });
6894
+ return { ok: false, error: new Error(errorMessage) };
6895
+ }
6409
6896
  return { ok: true, value: void 0 };
6410
6897
  } catch (error) {
6411
6898
  return {
@@ -6420,7 +6907,7 @@ var init_mcp_manager = __esm({
6420
6907
  });
6421
6908
 
6422
6909
  // src/integrations/ax-cli-sdk/adapter.ts
6423
- var AxCliSdkAdapter;
6910
+ var DEFAULT_MAX_TOOL_ROUNDS, AxCliSdkAdapter;
6424
6911
  var init_adapter2 = __esm({
6425
6912
  "src/integrations/ax-cli-sdk/adapter.ts"() {
6426
6913
  init_esm_shims();
@@ -6431,10 +6918,13 @@ var init_adapter2 = __esm({
6431
6918
  init_instructions_bridge();
6432
6919
  init_mcp_manager();
6433
6920
  init_mcp_manager();
6921
+ DEFAULT_MAX_TOOL_ROUNDS = 400;
6434
6922
  AxCliSdkAdapter = class _AxCliSdkAdapter {
6435
6923
  agent = null;
6436
6924
  // Will type as LLMAgent after import
6437
6925
  agentConfig = null;
6926
+ initPromise = null;
6927
+ executionLock = Promise.resolve();
6438
6928
  reuseEnabled;
6439
6929
  streamingEnabled;
6440
6930
  sdkAvailable = null;
@@ -6457,6 +6947,9 @@ var init_adapter2 = __esm({
6457
6947
  /**
6458
6948
  * Get or create SubagentAdapter for parallel multi-agent execution
6459
6949
  *
6950
+ * BUG FIX: Log warning when options are provided but adapter already exists,
6951
+ * as the options will be ignored.
6952
+ *
6460
6953
  * @example
6461
6954
  * ```typescript
6462
6955
  * const subagents = adapter.getSubagentAdapter();
@@ -6470,6 +6963,10 @@ var init_adapter2 = __esm({
6470
6963
  if (!this.subagentAdapter) {
6471
6964
  this.subagentAdapter = new SubagentAdapter(options);
6472
6965
  logger.debug("SubagentAdapter created");
6966
+ } else if (options) {
6967
+ logger.warn("SubagentAdapter already exists, ignoring new options", {
6968
+ hint: "Call destroy() first to recreate with new options"
6969
+ });
6473
6970
  }
6474
6971
  return this.subagentAdapter;
6475
6972
  }
@@ -6495,6 +6992,9 @@ var init_adapter2 = __esm({
6495
6992
  /**
6496
6993
  * Get or create CheckpointAdapter for resumable workflows
6497
6994
  *
6995
+ * BUG FIX: Log warning when options are provided but adapter already exists,
6996
+ * as the options will be ignored.
6997
+ *
6498
6998
  * @example
6499
6999
  * ```typescript
6500
7000
  * const checkpoints = adapter.getCheckpointAdapter();
@@ -6506,6 +7006,10 @@ var init_adapter2 = __esm({
6506
7006
  if (!this.checkpointAdapter) {
6507
7007
  this.checkpointAdapter = new CheckpointAdapter(options);
6508
7008
  logger.debug("CheckpointAdapter created");
7009
+ } else if (options) {
7010
+ logger.warn("CheckpointAdapter already exists, ignoring new options", {
7011
+ hint: "Call destroy() first to recreate with new options"
7012
+ });
6509
7013
  }
6510
7014
  return this.checkpointAdapter;
6511
7015
  }
@@ -6545,6 +7049,9 @@ var init_adapter2 = __esm({
6545
7049
  /**
6546
7050
  * Get or create InstructionsBridge for unified agent instructions
6547
7051
  *
7052
+ * BUG FIX: Log warning when options are provided but bridge already exists,
7053
+ * as the options will be ignored.
7054
+ *
6548
7055
  * @example
6549
7056
  * ```typescript
6550
7057
  * const bridge = adapter.getInstructionsBridge();
@@ -6556,6 +7063,10 @@ var init_adapter2 = __esm({
6556
7063
  if (!this.instructionsBridge) {
6557
7064
  this.instructionsBridge = getInstructionsBridge(options);
6558
7065
  logger.debug("InstructionsBridge created");
7066
+ } else if (options) {
7067
+ logger.warn("InstructionsBridge already exists, ignoring new options", {
7068
+ hint: "Call destroy() first to recreate with new options"
7069
+ });
6559
7070
  }
6560
7071
  return this.instructionsBridge;
6561
7072
  }
@@ -6580,6 +7091,9 @@ var init_adapter2 = __esm({
6580
7091
  /**
6581
7092
  * Get or create MCP Manager for ax-cli MCP server management
6582
7093
  *
7094
+ * BUG FIX: Log warning when options are provided but manager already exists,
7095
+ * as the options will be ignored.
7096
+ *
6583
7097
  * @example
6584
7098
  * ```typescript
6585
7099
  * const mcp = adapter.getMCPManager();
@@ -6591,6 +7105,10 @@ var init_adapter2 = __esm({
6591
7105
  if (!this.mcpManager) {
6592
7106
  this.mcpManager = getAxCliMCPManager(options);
6593
7107
  logger.debug("AxCliMCPManager created");
7108
+ } else if (options) {
7109
+ logger.warn("AxCliMCPManager already exists, ignoring new options", {
7110
+ hint: "Call destroy() first to recreate with new options"
7111
+ });
6594
7112
  }
6595
7113
  return this.mcpManager;
6596
7114
  }
@@ -6636,21 +7154,35 @@ var init_adapter2 = __esm({
6636
7154
  * Performance: ~5ms overhead vs ~50-200ms for CLI spawning
6637
7155
  */
6638
7156
  async execute(prompt, options) {
7157
+ return this.runExclusive(() => this.executeInternal(prompt, options));
7158
+ }
7159
+ /**
7160
+ * Serialize executions to avoid chat history corruption and token mis-tracking
7161
+ * when the same adapter instance is used concurrently.
7162
+ */
7163
+ async runExclusive(fn) {
7164
+ const resultPromise = this.executionLock.then(fn, fn);
7165
+ this.executionLock = resultPromise.then(
7166
+ () => Promise.resolve(),
7167
+ () => Promise.resolve()
7168
+ );
7169
+ return resultPromise;
7170
+ }
7171
+ async executeInternal(prompt, options) {
6639
7172
  const startTime = Date.now();
6640
7173
  let totalTokens = 0;
6641
7174
  try {
6642
7175
  if (!await this.ensureSDKAvailable()) {
6643
7176
  throw new Error("ax-cli SDK not available. Install with: npm install @defai.digital/ax-cli");
6644
7177
  }
6645
- if (!this.agent || this.hasConfigChanged(options)) {
6646
- await this.initializeAgent(options);
6647
- }
7178
+ await this.ensureAgent(options);
6648
7179
  logger.debug("Executing via SDK (streaming mode for token tracking)", {
6649
7180
  promptLength: prompt.length,
6650
7181
  userWantsCallbacks: !!(options.onStream || options.onTool),
6651
7182
  note: "SDK handles custom instructions and project memory"
6652
7183
  });
6653
- const historyLengthBefore = this.agent.getChatHistory().length;
7184
+ const chatHistory = typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : null;
7185
+ const historyLengthBefore = Array.isArray(chatHistory) ? chatHistory.length : 0;
6654
7186
  for await (const chunk of this.agent.processUserMessageStream(prompt)) {
6655
7187
  if (chunk.type === "token_count" && chunk.tokenCount) {
6656
7188
  totalTokens += chunk.tokenCount;
@@ -6687,8 +7219,8 @@ var init_adapter2 = __esm({
6687
7219
  }
6688
7220
  }
6689
7221
  }
6690
- const fullHistory = this.agent.getChatHistory();
6691
- const result = fullHistory.slice(historyLengthBefore);
7222
+ const fullHistory = typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : [];
7223
+ const result = Array.isArray(fullHistory) ? fullHistory.slice(historyLengthBefore) : [];
6692
7224
  logger.debug("Chat history extraction", {
6693
7225
  historyLengthBefore,
6694
7226
  historyLengthAfter: fullHistory.length,
@@ -6728,10 +7260,11 @@ var init_adapter2 = __esm({
6728
7260
  * We do NOT pass credentials to the SDK - it manages its own configuration.
6729
7261
  */
6730
7262
  async initializeAgent(options) {
7263
+ await this.destroyAgentInstance();
6731
7264
  const { createAgent } = await import('@defai.digital/ax-cli/sdk');
6732
7265
  try {
6733
7266
  const config = {
6734
- maxToolRounds: options.maxToolRounds || 400
7267
+ maxToolRounds: options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS
6735
7268
  };
6736
7269
  logger.debug("Creating SDK agent (credentials from ax-cli settings)", {
6737
7270
  maxToolRounds: config.maxToolRounds,
@@ -6793,22 +7326,75 @@ var init_adapter2 = __esm({
6793
7326
  */
6794
7327
  hasConfigChanged(options) {
6795
7328
  if (!this.agentConfig) return true;
6796
- const changed = this.agentConfig.maxToolRounds !== (options.maxToolRounds || 400);
7329
+ const changed = this.agentConfig.maxToolRounds !== (options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS);
6797
7330
  if (changed) {
6798
7331
  logger.debug("Agent config changed, will reinitialize", {
6799
7332
  oldMaxToolRounds: this.agentConfig.maxToolRounds,
6800
- newMaxToolRounds: options.maxToolRounds || 400,
7333
+ newMaxToolRounds: options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS,
6801
7334
  note: "SDK credentials managed by ax-cli setup"
6802
7335
  });
6803
7336
  }
6804
7337
  return changed;
6805
7338
  }
7339
+ /**
7340
+ * Ensure agent is initialized exactly once even if execute() is called
7341
+ * concurrently. Reinitializes when config changes.
7342
+ *
7343
+ * BUG FIX: If config changes while initialization is in progress, we need to
7344
+ * wait for current init to complete, then reinitialize with new config.
7345
+ *
7346
+ * BUG FIX (v11.3.3): Track initialization errors to prevent repeated init
7347
+ * attempts from creating a retry storm. If init fails, remember the error
7348
+ * and rethrow it for subsequent callers until reset.
7349
+ */
7350
+ initError = null;
7351
+ async ensureAgent(options) {
7352
+ if (this.agent && !this.hasConfigChanged(options)) {
7353
+ return;
7354
+ }
7355
+ if (this.initError && !this.agent) {
7356
+ throw this.initError;
7357
+ }
7358
+ if (this.initPromise) {
7359
+ await this.initPromise;
7360
+ if (this.agent && !this.hasConfigChanged(options)) {
7361
+ return;
7362
+ }
7363
+ if (this.initError) {
7364
+ throw this.initError;
7365
+ }
7366
+ }
7367
+ this.initError = null;
7368
+ this.initPromise = this.initializeAgent(options).catch((error) => {
7369
+ this.initError = error instanceof Error ? error : new Error(String(error));
7370
+ throw error;
7371
+ }).finally(() => {
7372
+ this.initPromise = null;
7373
+ });
7374
+ await this.initPromise;
7375
+ }
6806
7376
  /**
6807
7377
  * Set up streaming event handlers (for initialization)
7378
+ *
7379
+ * BUG FIX (v11.3.3): Check if removeAllListeners exists before calling it.
7380
+ * Not all SDK agent implementations extend EventEmitter, and calling
7381
+ * a non-existent method would cause an error. Use optional chaining and
7382
+ * type guards to safely handle this.
6808
7383
  */
6809
7384
  setupEventHandlers() {
6810
7385
  if (!this.agent) return;
6811
7386
  try {
7387
+ if (typeof this.agent.removeAllListeners === "function") {
7388
+ this.agent.removeAllListeners("stream");
7389
+ this.agent.removeAllListeners("tool");
7390
+ this.agent.removeAllListeners("error");
7391
+ } else if (typeof this.agent.off === "function") {
7392
+ logger.debug("Agent uses off() instead of removeAllListeners(), skipping listener cleanup");
7393
+ }
7394
+ if (typeof this.agent.on !== "function") {
7395
+ logger.debug("Agent does not support event subscription, skipping event handlers");
7396
+ return;
7397
+ }
6812
7398
  this.agent.on("stream", (chunk) => {
6813
7399
  logger.debug("Stream chunk received", {
6814
7400
  type: chunk.type,
@@ -6831,50 +7417,6 @@ var init_adapter2 = __esm({
6831
7417
  });
6832
7418
  }
6833
7419
  }
6834
- /**
6835
- * Set up streaming callbacks for execution
6836
- */
6837
- setupStreamingCallbacks(options) {
6838
- if (!this.agent) return;
6839
- try {
6840
- this.agent.removeAllListeners("stream");
6841
- this.agent.removeAllListeners("tool");
6842
- if (options.onStream) {
6843
- this.agent.on("stream", (chunk) => {
6844
- try {
6845
- options.onStream({
6846
- type: chunk.type || "content",
6847
- content: chunk.content,
6848
- tool: chunk.tool,
6849
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
6850
- });
6851
- } catch (error) {
6852
- logger.warn("Stream callback error", {
6853
- error: error instanceof Error ? error.message : String(error)
6854
- });
6855
- }
6856
- });
6857
- }
6858
- if (options.onTool) {
6859
- this.agent.on("tool", (data) => {
6860
- try {
6861
- options.onTool({
6862
- name: data.name,
6863
- args: data.args
6864
- });
6865
- } catch (error) {
6866
- logger.warn("Tool callback error", {
6867
- error: error instanceof Error ? error.message : String(error)
6868
- });
6869
- }
6870
- });
6871
- }
6872
- } catch (error) {
6873
- logger.warn("Failed to setup streaming callbacks", {
6874
- error: error instanceof Error ? error.message : String(error)
6875
- });
6876
- }
6877
- }
6878
7420
  /**
6879
7421
  * Extract token usage from various sources with priority:
6880
7422
  * 1. SDK events (token_count emissions) - most accurate
@@ -6898,9 +7440,9 @@ var init_adapter2 = __esm({
6898
7440
  }
6899
7441
  const usage = usageObject || {};
6900
7442
  const actualTokens = {
6901
- prompt: usage.prompt_tokens || usage.prompt || usage.input || usage.promptTokens || 0,
6902
- completion: usage.completion_tokens || usage.completion || usage.output || usage.completionTokens || 0,
6903
- total: usage.total_tokens || usage.total || usage.totalTokens || 0
7443
+ prompt: usage.prompt_tokens ?? usage.prompt ?? usage.input ?? usage.promptTokens ?? 0,
7444
+ completion: usage.completion_tokens ?? usage.completion ?? usage.output ?? usage.completionTokens ?? 0,
7445
+ total: usage.total_tokens ?? usage.total ?? usage.totalTokens ?? 0
6904
7446
  };
6905
7447
  if (actualTokens.total > 0) {
6906
7448
  logger.info(`Using token counts from ${sourceName || "usage object"}`, {
@@ -6964,12 +7506,12 @@ var init_adapter2 = __esm({
6964
7506
  cached: false
6965
7507
  };
6966
7508
  }
6967
- const content = typeof result === "string" ? result : result.content || result.text || "";
7509
+ const content = typeof result === "string" ? result : result?.content || result?.text || "";
6968
7510
  const tokensUsed = this.extractTokenUsage(
6969
7511
  prompt,
6970
7512
  content,
6971
7513
  totalTokensFromEvents,
6972
- result.usage || result.tokens,
7514
+ result?.usage || result?.tokens,
6973
7515
  "response.usage (fallback path)"
6974
7516
  );
6975
7517
  return {
@@ -7058,21 +7600,7 @@ var init_adapter2 = __esm({
7058
7600
  * Cleanup resources
7059
7601
  */
7060
7602
  async destroy() {
7061
- if (this.agent) {
7062
- try {
7063
- const result = this.agent.dispose();
7064
- if (result instanceof Promise) {
7065
- await result;
7066
- }
7067
- this.agent = null;
7068
- this.agentConfig = null;
7069
- logger.debug("SDK agent destroyed");
7070
- } catch (error) {
7071
- logger.warn("Error during agent cleanup", {
7072
- error: error instanceof Error ? error.message : String(error)
7073
- });
7074
- }
7075
- }
7603
+ await this.destroyAgentInstance();
7076
7604
  if (this.subagentAdapter) {
7077
7605
  try {
7078
7606
  await this.subagentAdapter.destroy();
@@ -7110,8 +7638,43 @@ var init_adapter2 = __esm({
7110
7638
  }
7111
7639
  }
7112
7640
  if (this.mcpManager) {
7113
- this.mcpManager = null;
7114
- logger.debug("MCP Manager cleared");
7641
+ try {
7642
+ const shutdownResult = await this.mcpManager.shutdown();
7643
+ if (!shutdownResult.ok) {
7644
+ logger.warn("Error while shutting down MCP servers", {
7645
+ error: shutdownResult.error instanceof Error ? shutdownResult.error.message : String(shutdownResult.error)
7646
+ });
7647
+ }
7648
+ } catch (error) {
7649
+ logger.warn("Error during MCP Manager cleanup", {
7650
+ error: error instanceof Error ? error.message : String(error)
7651
+ });
7652
+ } finally {
7653
+ this.mcpManager = null;
7654
+ logger.debug("MCP Manager cleared");
7655
+ }
7656
+ }
7657
+ }
7658
+ /**
7659
+ * Dispose the primary agent without touching ancillary adapters.
7660
+ * BUG FIX (v11.3.3): Also clear initError to allow retry after destroy.
7661
+ */
7662
+ async destroyAgentInstance() {
7663
+ this.initError = null;
7664
+ if (!this.agent) return;
7665
+ try {
7666
+ const result = this.agent.dispose();
7667
+ if (result instanceof Promise) {
7668
+ await result;
7669
+ }
7670
+ } catch (error) {
7671
+ logger.warn("Error during agent cleanup", {
7672
+ error: error instanceof Error ? error.message : String(error)
7673
+ });
7674
+ } finally {
7675
+ this.agent = null;
7676
+ this.agentConfig = null;
7677
+ logger.debug("SDK agent destroyed");
7115
7678
  }
7116
7679
  }
7117
7680
  };
@@ -7158,10 +7721,22 @@ var init_hybrid_adapter2 = __esm({
7158
7721
  }
7159
7722
  throw new Error(`No adapter available for mode: ${this.mode}`);
7160
7723
  } catch (error) {
7161
- if (this.mode === "auto" && this.activeMode === "sdk" && this.cliAdapter) {
7724
+ if (this.mode === "auto" && this.activeMode === "sdk") {
7162
7725
  logger.warn("SDK execution failed, falling back to CLI", {
7163
7726
  error: error instanceof Error ? error.message : String(error)
7164
7727
  });
7728
+ if (this.sdkAdapter) {
7729
+ const sdkAdapterToClean = this.sdkAdapter;
7730
+ this.sdkAdapter = null;
7731
+ sdkAdapterToClean.destroy().catch((cleanupError) => {
7732
+ logger.warn("Error during SDK adapter cleanup on fallback", {
7733
+ error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
7734
+ });
7735
+ });
7736
+ }
7737
+ if (!this.cliAdapter) {
7738
+ this.initializeCli();
7739
+ }
7165
7740
  this.activeMode = "cli";
7166
7741
  return await this.executeCli(prompt, options);
7167
7742
  }
@@ -7230,15 +7805,25 @@ var init_hybrid_adapter2 = __esm({
7230
7805
  *
7231
7806
  * @param required - If true, throws if SDK unavailable
7232
7807
  * @returns true if SDK is available, false otherwise
7808
+ *
7809
+ * BUG FIX (v11.3.3): Clean up SDK adapter if isAvailable() returns false.
7810
+ * Previously the adapter was just set to null without calling destroy(),
7811
+ * which could leak resources initialized in the constructor.
7233
7812
  */
7234
7813
  async initializeSdk(required) {
7235
7814
  if (this.sdkAdapter) {
7236
7815
  return true;
7237
7816
  }
7817
+ let adapter = null;
7238
7818
  try {
7239
- this.sdkAdapter = new AxCliSdkAdapter(this.sdkOptions);
7240
- const available = await this.sdkAdapter.isAvailable();
7819
+ adapter = new AxCliSdkAdapter(this.sdkOptions);
7820
+ const available = await adapter.isAvailable();
7241
7821
  if (!available) {
7822
+ await adapter.destroy().catch((cleanupError) => {
7823
+ logger.warn("Error cleaning up unavailable SDK adapter", {
7824
+ error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
7825
+ });
7826
+ });
7242
7827
  if (required) {
7243
7828
  throw new Error(
7244
7829
  "ax-cli SDK not available. Install with: npm install @defai.digital/ax-cli"
@@ -7247,14 +7832,21 @@ var init_hybrid_adapter2 = __esm({
7247
7832
  logger.warn("SDK not available", {
7248
7833
  hint: "Install with: npm install @defai.digital/ax-cli"
7249
7834
  });
7250
- this.sdkAdapter = null;
7251
7835
  return false;
7252
7836
  }
7837
+ this.sdkAdapter = adapter;
7253
7838
  logger.info("SDK adapter initialized", {
7254
7839
  version: await this.sdkAdapter.getVersion()
7255
7840
  });
7256
7841
  return true;
7257
7842
  } catch (error) {
7843
+ if (adapter) {
7844
+ await adapter.destroy().catch((cleanupError) => {
7845
+ logger.warn("Error cleaning up SDK adapter after error", {
7846
+ error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
7847
+ });
7848
+ });
7849
+ }
7258
7850
  logger.error("Failed to initialize SDK adapter", {
7259
7851
  error: error instanceof Error ? error.message : String(error)
7260
7852
  });
@@ -7303,6 +7895,9 @@ var init_hybrid_adapter2 = __esm({
7303
7895
  }
7304
7896
  /**
7305
7897
  * Get command name
7898
+ *
7899
+ * BUG FIX: Handle 'auto' mode correctly - return 'ax-cli-auto' to indicate
7900
+ * the actual command hasn't been determined yet (will be sdk or cli after initialization)
7306
7901
  */
7307
7902
  getCommand() {
7308
7903
  if (this.activeMode === "sdk") {
@@ -7310,7 +7905,13 @@ var init_hybrid_adapter2 = __esm({
7310
7905
  } else if (this.activeMode === "cli") {
7311
7906
  return "ax-cli";
7312
7907
  }
7313
- return this.mode === "sdk" ? "ax-cli-sdk" : "ax-cli";
7908
+ if (this.mode === "sdk") {
7909
+ return "ax-cli-sdk";
7910
+ } else if (this.mode === "cli") {
7911
+ return "ax-cli";
7912
+ } else {
7913
+ return "ax-cli-auto";
7914
+ }
7314
7915
  }
7315
7916
  /**
7316
7917
  * Get display name
@@ -7331,9 +7932,22 @@ var init_hybrid_adapter2 = __esm({
7331
7932
  }
7332
7933
  /**
7333
7934
  * Force switch to CLI mode (for debugging or fallback)
7935
+ *
7936
+ * BUG FIX: Destroy SDK adapter when switching to CLI to prevent resource leaks
7937
+ * (memory, event listeners) from the unused SDK adapter.
7334
7938
  */
7335
- switchToCliMode() {
7939
+ async switchToCliMode() {
7336
7940
  logger.info("Forcing switch to CLI mode");
7941
+ if (this.sdkAdapter) {
7942
+ try {
7943
+ await this.sdkAdapter.destroy();
7944
+ } catch (error) {
7945
+ logger.warn("Error destroying SDK adapter during mode switch", {
7946
+ error: error instanceof Error ? error.message : String(error)
7947
+ });
7948
+ }
7949
+ this.sdkAdapter = null;
7950
+ }
7337
7951
  this.initializeCli();
7338
7952
  this.activeMode = "cli";
7339
7953
  }
@@ -7353,13 +7967,22 @@ var init_hybrid_adapter2 = __esm({
7353
7967
  }
7354
7968
  /**
7355
7969
  * Cleanup resources
7970
+ * BUG FIX: Use Promise.allSettled to ensure cleanup completes even if SDK destroy fails
7356
7971
  */
7357
7972
  async destroy() {
7358
7973
  const destroyPromises = [];
7359
7974
  if (this.sdkAdapter) {
7360
7975
  destroyPromises.push(this.sdkAdapter.destroy());
7361
7976
  }
7362
- await Promise.all(destroyPromises);
7977
+ const results = await Promise.allSettled(destroyPromises);
7978
+ results.forEach((result, index) => {
7979
+ if (result.status === "rejected") {
7980
+ logger.warn("Adapter cleanup failed", {
7981
+ index,
7982
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
7983
+ });
7984
+ }
7985
+ });
7363
7986
  this.sdkAdapter = null;
7364
7987
  this.cliAdapter = null;
7365
7988
  this.activeMode = null;
@@ -7375,7 +7998,7 @@ __export(ax_cli_provider_exports, {
7375
7998
  AxCliProvider: () => AxCliProvider,
7376
7999
  GlmProvider: () => GlmProvider
7377
8000
  });
7378
- var SDK_ADAPTER_UNAVAILABLE_ERROR, AxCliProvider, GlmProvider;
8001
+ var SDK_ADAPTER_UNAVAILABLE_ERROR, DEFAULT_MAX_TOOL_ROUNDS2, AxCliProvider, GlmProvider;
7379
8002
  var init_ax_cli_provider = __esm({
7380
8003
  "src/providers/ax-cli-provider.ts"() {
7381
8004
  init_esm_shims();
@@ -7383,6 +8006,7 @@ var init_ax_cli_provider = __esm({
7383
8006
  init_hybrid_adapter2();
7384
8007
  init_logger();
7385
8008
  SDK_ADAPTER_UNAVAILABLE_ERROR = 'SDK adapter not available. Ensure mode="sdk" or "auto" with SDK installed.';
8009
+ DEFAULT_MAX_TOOL_ROUNDS2 = 400;
7386
8010
  AxCliProvider = class extends BaseProvider {
7387
8011
  adapter;
7388
8012
  config;
@@ -7413,7 +8037,7 @@ var init_ax_cli_provider = __esm({
7413
8037
  const options = {
7414
8038
  model: request.model || axCliConfig.model,
7415
8039
  // Optional model override
7416
- maxToolRounds: axCliConfig.maxToolRounds || 400,
8040
+ maxToolRounds: axCliConfig.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS2,
7417
8041
  timeout: this.config.timeout,
7418
8042
  // Note: apiKey and baseUrl are ignored by SDK adapter (v3.7.0+)
7419
8043
  // SDK loads credentials from ax-cli setup (~/.ax-cli/config.json)
@@ -7441,18 +8065,31 @@ var init_ax_cli_provider = __esm({
7441
8065
  error: error instanceof Error ? error.message : String(error),
7442
8066
  model: options.model || "(from ax-cli setup)"
7443
8067
  });
7444
- throw error;
8068
+ throw this.handleError(error);
7445
8069
  }
7446
8070
  }
7447
8071
  /**
7448
8072
  * Check if ax-cli is available
7449
8073
  *
8074
+ * BUG FIX: Update health status like BaseProvider.isAvailable() does.
8075
+ * Previously only returned the availability boolean without updating
8076
+ * the provider's health state, which meant consecutiveFailures,
8077
+ * consecutiveSuccesses, and errorRate weren't tracked for this provider.
8078
+ *
7450
8079
  * @returns True if ax-cli is installed and accessible
7451
8080
  */
7452
8081
  async isAvailable() {
7453
8082
  try {
7454
8083
  const available = await this.adapter.isAvailable();
7455
- if (!available) {
8084
+ this.health.available = available;
8085
+ this.health.errorRate = available ? 0 : 1;
8086
+ this.health.lastCheck = Date.now();
8087
+ if (available) {
8088
+ this.health.consecutiveFailures = 0;
8089
+ this.health.consecutiveSuccesses++;
8090
+ } else {
8091
+ this.health.consecutiveFailures++;
8092
+ this.health.consecutiveSuccesses = 0;
7456
8093
  logger.warn("ax-cli is not available", {
7457
8094
  provider: this.name,
7458
8095
  command: this.adapter.getCommand()
@@ -7460,6 +8097,11 @@ var init_ax_cli_provider = __esm({
7460
8097
  }
7461
8098
  return available;
7462
8099
  } catch (error) {
8100
+ this.health.available = false;
8101
+ this.health.errorRate = 1;
8102
+ this.health.lastCheck = Date.now();
8103
+ this.health.consecutiveFailures++;
8104
+ this.health.consecutiveSuccesses = 0;
7463
8105
  logger.error("Failed to check ax-cli availability", {
7464
8106
  error: error instanceof Error ? error.message : String(error)
7465
8107
  });
@@ -7499,9 +8141,11 @@ var init_ax_cli_provider = __esm({
7499
8141
  }
7500
8142
  /**
7501
8143
  * Force switch to CLI mode (for debugging or fallback)
8144
+ *
8145
+ * This method is async because it needs to clean up SDK adapter resources.
7502
8146
  */
7503
- switchToCliMode() {
7504
- this.adapter.switchToCliMode();
8147
+ async switchToCliMode() {
8148
+ await this.adapter.switchToCliMode();
7505
8149
  }
7506
8150
  // ==========================================
7507
8151
  // SDK Advanced Features (v10.4.0)
@@ -7520,7 +8164,7 @@ var init_ax_cli_provider = __esm({
7520
8164
  * ]);
7521
8165
  * ```
7522
8166
  */
7523
- async executeParallelTasks(tasks, options) {
8167
+ async executeParallelTasks(tasks) {
7524
8168
  const sdkAdapter = this.adapter.getSdkAdapter();
7525
8169
  if (!sdkAdapter) {
7526
8170
  throw new Error(SDK_ADAPTER_UNAVAILABLE_ERROR);
@@ -7649,6 +8293,28 @@ var init_ax_cli_provider = __esm({
7649
8293
  getMockResponse() {
7650
8294
  return "Mock ax-cli response for testing";
7651
8295
  }
8296
+ /**
8297
+ * Cleanup provider resources
8298
+ *
8299
+ * BUG FIX (v11.3.3): AxCliProvider was missing a destroy() method, which meant
8300
+ * the underlying HybridAxCliAdapter (and its SDK adapter, subagent adapter,
8301
+ * checkpoint adapter, etc.) would never be cleaned up. This could cause:
8302
+ * - Memory leaks from cached SDK agents
8303
+ * - Unflushed checkpoint data
8304
+ * - Orphaned MCP server connections
8305
+ *
8306
+ * Call this method when the provider is no longer needed.
8307
+ */
8308
+ async destroy() {
8309
+ try {
8310
+ await this.adapter.destroy();
8311
+ logger.info("AxCliProvider destroyed");
8312
+ } catch (error) {
8313
+ logger.warn("Error during AxCliProvider cleanup", {
8314
+ error: error instanceof Error ? error.message : String(error)
8315
+ });
8316
+ }
8317
+ }
7652
8318
  };
7653
8319
  GlmProvider = AxCliProvider;
7654
8320
  }
@@ -8626,11 +9292,11 @@ var VALIDATION_LIMITS = {
8626
9292
  MAX_PORT: 65535
8627
9293
  // Maximum port number
8628
9294
  };
8629
- function isValidRelativePath(path7) {
8630
- if (!path7 || typeof path7 !== "string") {
9295
+ function isValidRelativePath(path8) {
9296
+ if (!path8 || typeof path8 !== "string") {
8631
9297
  return false;
8632
9298
  }
8633
- const normalizedPath = path7.replace(/\\/g, "/");
9299
+ const normalizedPath = path8.replace(/\\/g, "/");
8634
9300
  if (normalizedPath.startsWith("/")) {
8635
9301
  return false;
8636
9302
  }
@@ -9022,7 +9688,7 @@ var PRECOMPILED_CONFIG = {
9022
9688
  "providers": {
9023
9689
  "claude-code": {
9024
9690
  "enabled": true,
9025
- "priority": 1,
9691
+ "priority": 3,
9026
9692
  "timeout": 27e5,
9027
9693
  "command": "claude",
9028
9694
  "healthCheck": {
@@ -9082,7 +9748,7 @@ var PRECOMPILED_CONFIG = {
9082
9748
  },
9083
9749
  "openai": {
9084
9750
  "enabled": true,
9085
- "priority": 3,
9751
+ "priority": 1,
9086
9752
  "timeout": 27e5,
9087
9753
  "command": "codex",
9088
9754
  "healthCheck": {
@@ -9320,7 +9986,7 @@ var PRECOMPILED_CONFIG = {
9320
9986
  "enableFreeTierPrioritization": true,
9321
9987
  "enableWorkloadAwareRouting": true
9322
9988
  },
9323
- "version": "11.3.1"
9989
+ "version": "11.3.3"
9324
9990
  };
9325
9991
 
9326
9992
  // src/core/config/schemas.ts
@@ -9347,7 +10013,7 @@ var safeNameSchema = z.string().min(1).max(VALIDATION_LIMITS.MAX_NAME_LENGTH).re
9347
10013
  "Name must be alphanumeric with dash/underscore only"
9348
10014
  ).describe("Safe identifier name");
9349
10015
  var relativePathSchema = z.string().min(1).refine(
9350
- (path7) => !path7.includes("..") && !path7.startsWith("/"),
10016
+ (path8) => !path8.includes("..") && !path8.startsWith("/"),
9351
10017
  "Path must be relative (no ../, no absolute paths)"
9352
10018
  ).describe("Relative path within project");
9353
10019
  var fileExtensionSchema = z.string().regex(
@@ -9687,16 +10353,16 @@ async function loadConfigUncached(projectDir) {
9687
10353
  });
9688
10354
  return config;
9689
10355
  }
9690
- async function loadConfigFile(path7) {
10356
+ async function loadConfigFile(path8) {
9691
10357
  try {
9692
- const content = await readFile(path7, "utf-8");
10358
+ const content = await readFile(path8, "utf-8");
9693
10359
  if (content.length > VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE) {
9694
10360
  throw ConfigError.parseError(
9695
10361
  new Error(`Config file too large (max ${VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE / 1024}KB, got ${Math.ceil(content.length / 1024)}KB)`),
9696
- path7
10362
+ path8
9697
10363
  );
9698
10364
  }
9699
- const ext = extname(path7).toLowerCase();
10365
+ const ext = extname(path8).toLowerCase();
9700
10366
  let userConfig;
9701
10367
  try {
9702
10368
  if (ext === ".yaml" || ext === ".yml") {
@@ -9705,7 +10371,7 @@ async function loadConfigFile(path7) {
9705
10371
  userConfig = JSON.parse(content);
9706
10372
  }
9707
10373
  } catch (parseError) {
9708
- throw ConfigError.parseError(parseError, path7);
10374
+ throw ConfigError.parseError(parseError, path8);
9709
10375
  }
9710
10376
  const config = mergeConfig(DEFAULT_CONFIG, userConfig);
9711
10377
  if (config.execution && userConfig.execution?.maxConcurrentAgents === void 0) {
@@ -9721,35 +10387,35 @@ async function loadConfigFile(path7) {
9721
10387
  if (validationErrors.length > 0) {
9722
10388
  throw ConfigError.invalid(
9723
10389
  validationErrors.join("; "),
9724
- { path: path7, errors: validationErrors }
10390
+ { path: path8, errors: validationErrors }
9725
10391
  );
9726
10392
  }
9727
- logger.info("Config loaded successfully", { path: normalizePath(path7), format: ext });
10393
+ logger.info("Config loaded successfully", { path: normalizePath(path8), format: ext });
9728
10394
  return config;
9729
10395
  } catch (error) {
9730
10396
  if (error instanceof ConfigError) {
9731
10397
  throw error;
9732
10398
  }
9733
10399
  if (error.code === "ENOENT") {
9734
- throw ConfigError.notFound(path7);
10400
+ throw ConfigError.notFound(path8);
9735
10401
  }
9736
10402
  if (error.code === "EACCES") {
9737
10403
  throw new ConfigError(
9738
- `Permission denied reading config: ${path7}`,
10404
+ `Permission denied reading config: ${path8}`,
9739
10405
  "E1002" /* CONFIG_PARSE_ERROR */,
9740
10406
  [
9741
10407
  "Check file permissions",
9742
10408
  "Run with appropriate user privileges",
9743
10409
  "Verify the file is accessible"
9744
10410
  ],
9745
- { path: path7, error: error.message }
10411
+ { path: path8, error: error.message }
9746
10412
  );
9747
10413
  }
9748
10414
  throw new ConfigError(
9749
10415
  `Failed to load config: ${error.message}`,
9750
10416
  "E1002" /* CONFIG_PARSE_ERROR */,
9751
10417
  ["Check file format and permissions"],
9752
- { path: path7, originalError: error.message }
10418
+ { path: path8, originalError: error.message }
9753
10419
  );
9754
10420
  }
9755
10421
  }
@@ -9765,8 +10431,8 @@ function validateConfigWithZod(config) {
9765
10431
  return ["Configuration validation failed with unknown error structure"];
9766
10432
  }
9767
10433
  return result.error.issues.map((err) => {
9768
- const path7 = err.path.join(".");
9769
- return `${path7}: ${err.message}`;
10434
+ const path8 = err.path.join(".");
10435
+ return `${path8}: ${err.message}`;
9770
10436
  });
9771
10437
  }
9772
10438
  function validateConfig(config) {
@@ -10152,16 +10818,16 @@ function validateConfig(config) {
10152
10818
  }
10153
10819
  return errors;
10154
10820
  }
10155
- async function saveConfigFile(path7, config) {
10821
+ async function saveConfigFile(path8, config) {
10156
10822
  try {
10157
10823
  const validationErrors = validateConfig(config);
10158
10824
  if (validationErrors.length > 0) {
10159
10825
  throw ConfigError.invalid(
10160
10826
  validationErrors.join("; "),
10161
- { path: path7, errors: validationErrors }
10827
+ { path: path8, errors: validationErrors }
10162
10828
  );
10163
10829
  }
10164
- const ext = extname(path7).toLowerCase();
10830
+ const ext = extname(path8).toLowerCase();
10165
10831
  let content;
10166
10832
  if (ext === ".yaml" || ext === ".yml") {
10167
10833
  content = dump(config, {
@@ -10173,30 +10839,30 @@ async function saveConfigFile(path7, config) {
10173
10839
  } else {
10174
10840
  content = JSON.stringify(config, null, 2);
10175
10841
  }
10176
- await writeFile(path7, content, "utf-8");
10842
+ await writeFile(path8, content, "utf-8");
10177
10843
  configCache.clear();
10178
- logger.info("Config saved successfully", { path: normalizePath(path7), format: ext });
10844
+ logger.info("Config saved successfully", { path: normalizePath(path8), format: ext });
10179
10845
  } catch (error) {
10180
10846
  if (error instanceof ConfigError) {
10181
10847
  throw error;
10182
10848
  }
10183
10849
  if (error.code === "EACCES") {
10184
10850
  throw new ConfigError(
10185
- `Permission denied writing config: ${path7}`,
10851
+ `Permission denied writing config: ${path8}`,
10186
10852
  "E1002" /* CONFIG_PARSE_ERROR */,
10187
10853
  [
10188
10854
  "Check file permissions",
10189
10855
  "Run with appropriate user privileges",
10190
10856
  "Verify the directory is writable"
10191
10857
  ],
10192
- { path: path7, error: error.message }
10858
+ { path: path8, error: error.message }
10193
10859
  );
10194
10860
  }
10195
10861
  throw new ConfigError(
10196
10862
  `Failed to save config: ${error.message}`,
10197
10863
  "E1002" /* CONFIG_PARSE_ERROR */,
10198
10864
  ["Check file path and permissions"],
10199
- { path: path7, originalError: error.message }
10865
+ { path: path8, originalError: error.message }
10200
10866
  );
10201
10867
  }
10202
10868
  }
@@ -10946,10 +11612,10 @@ var configCommand = {
10946
11612
  } else {
10947
11613
  const projectConfig = resolve(process.cwd(), "ax.config.json");
10948
11614
  const hiddenConfig = resolve(process.cwd(), ".automatosx", "config.json");
10949
- const fs7 = await import('fs');
10950
- if (fs7.existsSync(projectConfig)) {
11615
+ const fs8 = await import('fs');
11616
+ if (fs8.existsSync(projectConfig)) {
10951
11617
  configPath = projectConfig;
10952
- } else if (fs7.existsSync(hiddenConfig)) {
11618
+ } else if (fs8.existsSync(hiddenConfig)) {
10953
11619
  configPath = hiddenConfig;
10954
11620
  } else {
10955
11621
  configPath = projectConfig;
@@ -10996,9 +11662,9 @@ var configCommand = {
10996
11662
  }
10997
11663
  }
10998
11664
  };
10999
- async function checkExists(path7) {
11665
+ async function checkExists(path8) {
11000
11666
  try {
11001
- await access$1(path7, constants.F_OK);
11667
+ await access$1(path8, constants.F_OK);
11002
11668
  return true;
11003
11669
  } catch {
11004
11670
  return false;
@@ -11029,7 +11695,7 @@ async function validateConfigFile(config, verbose) {
11029
11695
  }
11030
11696
  console.log();
11031
11697
  }
11032
- async function resetConfig(path7, verbose) {
11698
+ async function resetConfig(path8, verbose) {
11033
11699
  const { createRequire } = await import('module');
11034
11700
  const require2 = createRequire(import.meta.url);
11035
11701
  let version = "11.2.6";
@@ -11044,14 +11710,14 @@ async function resetConfig(path7, verbose) {
11044
11710
  // Users should rely on IDE JSON Schema plugins that fetch from npm package
11045
11711
  version
11046
11712
  };
11047
- await saveConfigFile(path7, config);
11713
+ await saveConfigFile(path8, config);
11048
11714
  printSuccess("Configuration reset to defaults");
11049
11715
  if (verbose) {
11050
11716
  console.log(chalk5.gray(`
11051
- Config file: ${path7}
11717
+ Config file: ${path8}
11052
11718
  `));
11053
11719
  }
11054
- logger.info("Configuration reset", { path: path7 });
11720
+ logger.info("Configuration reset", { path: path8 });
11055
11721
  }
11056
11722
  async function listConfig(config, verbose) {
11057
11723
  console.log(chalk5.bold.cyan("\n\u{1F4CB} AutomatosX Configuration\n"));
@@ -11130,7 +11796,7 @@ async function getConfig(config, key, verbose) {
11130
11796
  }
11131
11797
  }
11132
11798
  }
11133
- async function setConfig(path7, config, key, value, verbose) {
11799
+ async function setConfig(path8, config, key, value, verbose) {
11134
11800
  let parsedValue = value;
11135
11801
  try {
11136
11802
  parsedValue = JSON.parse(value);
@@ -11149,21 +11815,21 @@ async function setConfig(path7, config, key, value, verbose) {
11149
11815
  console.log();
11150
11816
  process.exit(1);
11151
11817
  }
11152
- await saveConfigFile(path7, config);
11818
+ await saveConfigFile(path8, config);
11153
11819
  printSuccess(`Configuration updated: ${key} = ${value}`);
11154
11820
  if (verbose) {
11155
11821
  console.log(chalk5.gray(`
11156
- Config file: ${path7}`));
11822
+ Config file: ${path8}`));
11157
11823
  console.log(chalk5.gray("Configuration validated successfully\n"));
11158
11824
  }
11159
11825
  logger.info("Configuration updated", { key, value });
11160
11826
  }
11161
- function getNestedValue(obj, path7) {
11162
- if (!path7 || !path7.trim()) return void 0;
11163
- return path7.split(".").filter(Boolean).reduce((current, key) => current?.[key], obj);
11827
+ function getNestedValue(obj, path8) {
11828
+ if (!path8 || !path8.trim()) return void 0;
11829
+ return path8.split(".").filter(Boolean).reduce((current, key) => current?.[key], obj);
11164
11830
  }
11165
- function setNestedValue(obj, path7, value) {
11166
- const keys = path7.split(".");
11831
+ function setNestedValue(obj, path8, value) {
11832
+ const keys = path8.split(".");
11167
11833
  const lastKey = keys.pop();
11168
11834
  if (!lastKey) return false;
11169
11835
  const target = keys.reduce((current, key) => {
@@ -13263,7 +13929,7 @@ var setupCommand = {
13263
13929
  let version = "11.2.6";
13264
13930
  try {
13265
13931
  const packageJson = JSON.parse(
13266
- await import('fs/promises').then((fs7) => fs7.readFile(join(packageRoot, "package.json"), "utf-8"))
13932
+ await import('fs/promises').then((fs8) => fs8.readFile(join(packageRoot, "package.json"), "utf-8"))
13267
13933
  );
13268
13934
  version = packageJson.version;
13269
13935
  } catch {
@@ -13375,6 +14041,9 @@ var setupCommand = {
13375
14041
  console.log(chalk5.cyan("\u{1F504} Installing workflow templates..."));
13376
14042
  const workflowCount = await copyWorkflowTemplates(automatosxDir, packageRoot);
13377
14043
  console.log(chalk5.green(` \u2713 ${workflowCount} workflow templates installed`));
14044
+ console.log(chalk5.cyan("\u{1F501} Installing iterate mode configuration..."));
14045
+ await copyIterateConfig(automatosxDir, packageRoot);
14046
+ console.log(chalk5.green(" \u2713 Iterate mode patterns and templates installed"));
13378
14047
  console.log(chalk5.cyan("\u2699\uFE0F Generating configuration..."));
13379
14048
  await createDefaultConfig(configPath, argv.force ?? false, version);
13380
14049
  createdResources.push(configPath);
@@ -13583,9 +14252,9 @@ var setupCommand = {
13583
14252
  }
13584
14253
  }
13585
14254
  };
13586
- async function checkExists2(path7) {
14255
+ async function checkExists2(path8) {
13587
14256
  try {
13588
- await access$1(path7, constants.F_OK);
14257
+ await access$1(path8, constants.F_OK);
13589
14258
  return true;
13590
14259
  } catch {
13591
14260
  return false;
@@ -13606,8 +14275,12 @@ async function createDirectoryStructure(baseDir) {
13606
14275
  // v5.1: Session persistence
13607
14276
  // v5.2: Removed 'workspaces' - automatosx/PRD and automatosx/tmp created on-demand
13608
14277
  join(baseDir, "logs"),
13609
- join(baseDir, "workflows")
14278
+ join(baseDir, "workflows"),
13610
14279
  // v11.0.0: Workflow templates directory
14280
+ join(baseDir, "iterate"),
14281
+ // v11.4.0: Iterate mode patterns and templates
14282
+ join(baseDir, "state")
14283
+ // v11.3.0: Mode state persistence
13611
14284
  ];
13612
14285
  for (const dir of dirs) {
13613
14286
  await mkdir(dir, { recursive: true, mode: 493 });
@@ -13723,6 +14396,167 @@ async function copyWorkflowTemplates(baseDir, packageRoot) {
13723
14396
  return 4;
13724
14397
  }
13725
14398
  }
14399
+ async function copyIterateConfig(baseDir, packageRoot) {
14400
+ const iterateDir = join(baseDir, "iterate");
14401
+ const examplesIterateDir = join(packageRoot, "examples/iterate");
14402
+ try {
14403
+ if (await checkExists2(examplesIterateDir)) {
14404
+ const files = await readdir(examplesIterateDir);
14405
+ for (const file of files) {
14406
+ if (file.endsWith(".yaml") || file.endsWith(".yml")) {
14407
+ await copyFile(join(examplesIterateDir, file), join(iterateDir, file));
14408
+ }
14409
+ }
14410
+ return;
14411
+ }
14412
+ } catch {
14413
+ }
14414
+ await createDefaultIterateConfig(iterateDir);
14415
+ }
14416
+ async function createDefaultIterateConfig(iterateDir) {
14417
+ const patternsYaml = `# AutomatosX Iterate Mode - Pattern Library
14418
+ # Production patterns for response classification
14419
+ #
14420
+ # Classification Types:
14421
+ # - confirmation_prompt: AI asking for permission to proceed (auto-respond YES)
14422
+ # - genuine_question: AI asking for user input/decision (PAUSE)
14423
+ # - status_update: AI reporting progress (NO-OP)
14424
+ # - completion_signal: AI indicating task complete (acknowledge)
14425
+ # - blocking_request: AI needs external input (PAUSE)
14426
+ # - error_signal: AI encountered error (PAUSE for recovery)
14427
+ # - rate_limit_or_context: Provider limits hit (RETRY with different provider)
14428
+
14429
+ version: "1.0.0"
14430
+ updatedAt: "${(/* @__PURE__ */ new Date()).toISOString()}"
14431
+ description: "Production pattern library for iterate mode classifier"
14432
+
14433
+ patterns:
14434
+ confirmation_prompt:
14435
+ - pattern: "\\\\b(should I|shall I|do you want me to|would you like me to)\\\\b.*(proceed|continue|start|begin|go ahead)"
14436
+ priority: 10
14437
+ confidence: 0.95
14438
+ - pattern: "\\\\b(ready to|prepared to)\\\\b.*(proceed|continue|start|begin|implement)"
14439
+ priority: 9
14440
+ confidence: 0.9
14441
+ - pattern: "\\\\b(continue|proceed|go ahead)\\\\s*\\\\?"
14442
+ priority: 8
14443
+ confidence: 0.85
14444
+ - pattern: "\\\\b(is that|does that|sound)\\\\s+(okay|ok|good|correct|right)\\\\s*\\\\?"
14445
+ priority: 7
14446
+ confidence: 0.8
14447
+
14448
+ genuine_question:
14449
+ - pattern: "\\\\b(which|what)\\\\b.*(approach|method|option|implementation|strategy|framework|library)\\\\b.*(should|would|prefer|recommend)"
14450
+ priority: 10
14451
+ confidence: 0.95
14452
+ - pattern: "\\\\b(option [A-Z]|choice \\\\d|alternative \\\\d)\\\\b"
14453
+ priority: 10
14454
+ confidence: 0.95
14455
+ - pattern: "\\\\bwould you prefer\\\\b"
14456
+ priority: 10
14457
+ confidence: 0.95
14458
+ - pattern: "\\\\bcould you (clarify|explain|specify|provide)\\\\b"
14459
+ priority: 8
14460
+ confidence: 0.85
14461
+
14462
+ status_update:
14463
+ - pattern: "\\\\b(working on|implementing|creating|building|setting up|configuring)\\\\b"
14464
+ priority: 10
14465
+ confidence: 0.9
14466
+ - pattern: "\\\\b(analyzing|reviewing|examining|checking|looking at)\\\\b"
14467
+ priority: 9
14468
+ confidence: 0.85
14469
+ - pattern: "\\\\bI('ll| will| am going to)\\\\b.*(now|first|next|then)\\\\b"
14470
+ priority: 8
14471
+ confidence: 0.8
14472
+
14473
+ completion_signal:
14474
+ - pattern: "\\\\b(all (tasks|items|changes|updates)|everything)\\\\s+(completed|done|finished|implemented)"
14475
+ priority: 10
14476
+ confidence: 0.95
14477
+ - pattern: "\\\\bsuccessfully (completed|implemented|created|fixed|resolved)"
14478
+ priority: 9
14479
+ confidence: 0.9
14480
+ - pattern: "\\\\blet me know if (you need|there('s| is)) anything else"
14481
+ priority: 7
14482
+ confidence: 0.8
14483
+
14484
+ blocking_request:
14485
+ - pattern: "\\\\b(API key|credentials|password|token|secret)\\\\s+(needed|required|missing)"
14486
+ priority: 10
14487
+ confidence: 0.95
14488
+ - pattern: "\\\\bplease (provide|enter|specify|supply)\\\\b.*(key|password|token|credential)"
14489
+ priority: 10
14490
+ confidence: 0.95
14491
+ - pattern: "\\\\b(need|require|waiting for)\\\\s+(your|user)\\\\s+(input|approval|confirmation|decision)"
14492
+ priority: 10
14493
+ confidence: 0.95
14494
+
14495
+ error_signal:
14496
+ - pattern: "\\\\b(error|failed|failure|exception)\\\\s*:"
14497
+ priority: 10
14498
+ confidence: 0.95
14499
+ - pattern: "\\\\b(cannot|can't|couldn't|unable to)\\\\s+(find|locate|access|connect|read|write)"
14500
+ priority: 9
14501
+ confidence: 0.9
14502
+ - pattern: "\\\\b(test|build|compilation)\\\\s+(failed|error)"
14503
+ priority: 9
14504
+ confidence: 0.9
14505
+
14506
+ rate_limit_or_context:
14507
+ - pattern: "\\\\brate limit\\\\s*(exceeded|reached|hit)"
14508
+ priority: 10
14509
+ confidence: 0.95
14510
+ - pattern: "\\\\bcontext (window|limit)\\\\s*(exceeded|full|reached)"
14511
+ priority: 10
14512
+ confidence: 0.95
14513
+ - pattern: "\\\\btoo many requests"
14514
+ priority: 10
14515
+ confidence: 0.95
14516
+ `;
14517
+ const templatesYaml = `# AutomatosX Iterate Mode - Template Library
14518
+ # Production templates for auto-response generation
14519
+
14520
+ version: "1.0.0"
14521
+ updatedAt: "${(/* @__PURE__ */ new Date()).toISOString()}"
14522
+ description: "Production template library for iterate mode auto-responder"
14523
+
14524
+ templates:
14525
+ confirmation_prompt:
14526
+ - template: "Yes, please proceed."
14527
+ priority: 10
14528
+ provider: null
14529
+ - template: "Yes, continue with that approach."
14530
+ priority: 9
14531
+ provider: null
14532
+ - template: "Sounds good, go ahead."
14533
+ priority: 8
14534
+ provider: null
14535
+ - template: "Proceed."
14536
+ priority: 7
14537
+ provider: null
14538
+ - template: "Continue."
14539
+ priority: 7
14540
+ provider: null
14541
+
14542
+ completion_signal:
14543
+ - template: "Thank you."
14544
+ priority: 10
14545
+ provider: null
14546
+ - template: "Great work."
14547
+ priority: 9
14548
+ provider: null
14549
+
14550
+ status_update: []
14551
+ genuine_question: []
14552
+ blocking_request: []
14553
+ error_signal: []
14554
+ rate_limit_or_context: []
14555
+ `;
14556
+ await writeFile(join(iterateDir, "patterns.yaml"), patternsYaml, "utf-8");
14557
+ await writeFile(join(iterateDir, "templates.yaml"), templatesYaml, "utf-8");
14558
+ logger.info("Created default iterate mode configuration", { iterateDir });
14559
+ }
13726
14560
  async function createDefaultWorkflowTemplates(targetDir) {
13727
14561
  const authFlow = `# Authentication Implementation Workflow
13728
14562
  # Usage: ax run backend "implement user login" --workflow auth-flow
@@ -14932,11 +15766,11 @@ async function detectExpressRoutes(projectDir) {
14932
15766
  while ((match = routeRegex.exec(content)) !== null) {
14933
15767
  if (match[1] && match[2]) {
14934
15768
  const method = match[1].toUpperCase();
14935
- const path7 = match[2];
15769
+ const path8 = match[2];
14936
15770
  endpoints.push({
14937
15771
  method,
14938
- path: path7,
14939
- group: path7.split("/")[1] || "api"
15772
+ path: path8,
15773
+ group: path8.split("/")[1] || "api"
14940
15774
  });
14941
15775
  }
14942
15776
  }
@@ -16041,12 +16875,12 @@ var listCommand = {
16041
16875
  };
16042
16876
  async function listAgents(pathResolver, format) {
16043
16877
  const agentsDir = pathResolver.getAgentsDirectory();
16044
- const { existsSync: existsSync26 } = await import('fs');
16878
+ const { existsSync: existsSync27 } = await import('fs');
16045
16879
  const projectDir = await detectProjectRoot();
16046
16880
  const examplesDir = join(projectDir, "examples", "agents");
16047
16881
  try {
16048
16882
  const agentFiles = [];
16049
- if (existsSync26(agentsDir)) {
16883
+ if (existsSync27(agentsDir)) {
16050
16884
  const files = await readdir(agentsDir);
16051
16885
  for (const file of files) {
16052
16886
  if (file.endsWith(".yaml") || file.endsWith(".yml")) {
@@ -16058,7 +16892,7 @@ async function listAgents(pathResolver, format) {
16058
16892
  }
16059
16893
  }
16060
16894
  }
16061
- if (existsSync26(examplesDir)) {
16895
+ if (existsSync27(examplesDir)) {
16062
16896
  const files = await readdir(examplesDir);
16063
16897
  for (const file of files) {
16064
16898
  if (file.endsWith(".yaml") || file.endsWith(".yml")) {
@@ -17930,18 +18764,18 @@ var ProviderSessionManager = class {
17930
18764
  };
17931
18765
  var providerSessionInstances = /* @__PURE__ */ new Map();
17932
18766
  async function getProviderSession(workspacePath) {
17933
- let path7;
18767
+ let path8;
17934
18768
  {
17935
18769
  if (process.env.NODE_ENV === "test" || process.env.VITEST) {
17936
- path7 = process.cwd();
18770
+ path8 = process.cwd();
17937
18771
  } else {
17938
- path7 = await detectProjectRoot();
18772
+ path8 = await detectProjectRoot();
17939
18773
  }
17940
18774
  }
17941
- if (!providerSessionInstances.has(path7)) {
17942
- providerSessionInstances.set(path7, new ProviderSessionManager(path7));
18775
+ if (!providerSessionInstances.has(path8)) {
18776
+ providerSessionInstances.set(path8, new ProviderSessionManager(path8));
17943
18777
  }
17944
- return providerSessionInstances.get(path7);
18778
+ return providerSessionInstances.get(path8);
17945
18779
  }
17946
18780
 
17947
18781
  // src/core/router/router.ts
@@ -19711,6 +20545,9 @@ var MemoryManager = class _MemoryManager {
19711
20545
  if (!Number.isInteger(offsetValue) || offsetValue < 0) {
19712
20546
  throw new Error(`Invalid offset value: ${options.offset}. Must be a non-negative integer.`);
19713
20547
  }
20548
+ if (!limitClause) {
20549
+ limitClause = "LIMIT -1";
20550
+ }
19714
20551
  offsetClause = `OFFSET ${offsetValue}`;
19715
20552
  }
19716
20553
  const sql = `
@@ -20056,14 +20893,21 @@ var MemoryManager = class _MemoryManager {
20056
20893
  this.initialized = false;
20057
20894
  this.entryCount = 0;
20058
20895
  this.statements = {};
20059
- const { rename: rename4 } = await import('fs/promises');
20060
- await rename4(tempPath, this.config.dbPath);
20896
+ const { rename: rename6 } = await import('fs/promises');
20897
+ await rename6(tempPath, this.config.dbPath);
20061
20898
  this.db = new Database2(this.config.dbPath);
20062
20899
  this.db.pragma("journal_mode = WAL");
20063
20900
  this.db.pragma(`busy_timeout = ${this.config.busyTimeout}`);
20064
20901
  await this.initialize();
20065
20902
  logger.info("Database restored successfully (atomic operation)", { srcPath: normalizePath(srcPath) });
20066
20903
  } catch (error) {
20904
+ try {
20905
+ DatabaseFactory.close(this.db);
20906
+ } finally {
20907
+ this.initialized = false;
20908
+ this.entryCount = 0;
20909
+ this.statements = {};
20910
+ }
20067
20911
  throw new MemoryError(
20068
20912
  `Failed to restore database: ${error.message}`,
20069
20913
  "DATABASE_ERROR",
@@ -20614,6 +21458,10 @@ var SessionManager = class _SessionManager {
20614
21458
  MAX_TASKS_PER_SESSION = 1e3;
20615
21459
  /** UUID v4 validation regex (static for performance) */
20616
21460
  static UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
21461
+ /** Tracks whether manager has been destroyed to prevent late saves */
21462
+ destroyed = false;
21463
+ /** Last persistence error (reported during flush) */
21464
+ lastSaveError;
20617
21465
  /**
20618
21466
  * Validate session ID format (must be valid UUID v4)
20619
21467
  *
@@ -21208,6 +22056,7 @@ var SessionManager = class _SessionManager {
21208
22056
  * ```
21209
22057
  */
21210
22058
  async destroy() {
22059
+ this.destroyed = true;
21211
22060
  if (this.saveTimeout) {
21212
22061
  clearTimeout(this.saveTimeout);
21213
22062
  this.saveTimeout = void 0;
@@ -21218,9 +22067,12 @@ var SessionManager = class _SessionManager {
21218
22067
  logger.error("Error flushing save during destroy", {
21219
22068
  error: error.message
21220
22069
  });
22070
+ this.lastSaveError = void 0;
21221
22071
  }
22072
+ const sessionCount = this.activeSessions.size;
22073
+ this.activeSessions.clear();
21222
22074
  logger.debug("SessionManager destroyed", {
21223
- sessions: this.activeSessions.size
22075
+ sessions: sessionCount
21224
22076
  });
21225
22077
  }
21226
22078
  /**
@@ -21245,11 +22097,21 @@ var SessionManager = class _SessionManager {
21245
22097
  this.pendingSave = void 0;
21246
22098
  });
21247
22099
  await this.pendingSave;
22100
+ if (this.lastSaveError) {
22101
+ const err = this.lastSaveError;
22102
+ this.lastSaveError = void 0;
22103
+ throw err;
22104
+ }
21248
22105
  return;
21249
22106
  }
21250
22107
  if (this.pendingSave) {
21251
22108
  try {
21252
22109
  await this.pendingSave;
22110
+ if (this.lastSaveError) {
22111
+ const err = this.lastSaveError;
22112
+ this.lastSaveError = void 0;
22113
+ throw err;
22114
+ }
21253
22115
  } catch (err) {
21254
22116
  throw err;
21255
22117
  }
@@ -21414,7 +22276,7 @@ var SessionManager = class _SessionManager {
21414
22276
  * @private
21415
22277
  */
21416
22278
  saveToFile() {
21417
- if (!this.persistencePath) {
22279
+ if (!this.persistencePath || this.destroyed) {
21418
22280
  return;
21419
22281
  }
21420
22282
  if (this.saveTimeout) {
@@ -21427,18 +22289,18 @@ var SessionManager = class _SessionManager {
21427
22289
  }
21428
22290
  this.saveNeeded = false;
21429
22291
  const executeNextSave = async () => {
21430
- await this.doSave();
22292
+ try {
22293
+ await this.doSave();
22294
+ this.lastSaveError = void 0;
22295
+ } catch (err) {
22296
+ this.lastSaveError = err;
22297
+ }
21431
22298
  if (this.saveNeeded) {
21432
22299
  this.saveNeeded = false;
21433
22300
  return executeNextSave();
21434
22301
  }
21435
22302
  };
21436
- this.pendingSave = executeNextSave().catch((err) => {
21437
- logger.error("Debounced save failed", {
21438
- error: err.message
21439
- });
21440
- throw err;
21441
- }).finally(() => {
22303
+ this.pendingSave = executeNextSave().finally(() => {
21442
22304
  this.pendingSave = void 0;
21443
22305
  });
21444
22306
  }, 100);
@@ -23759,9 +24621,9 @@ var DependencyGraphBuilder = class {
23759
24621
  detectCycles(graph) {
23760
24622
  const visiting = /* @__PURE__ */ new Set();
23761
24623
  const visited = /* @__PURE__ */ new Set();
23762
- const visit = (nodeName, path7) => {
24624
+ const visit = (nodeName, path8) => {
23763
24625
  if (visiting.has(nodeName)) {
23764
- throw new Error(`Circular dependency detected: ${[...path7, nodeName].join(" \u2192 ")}`);
24626
+ throw new Error(`Circular dependency detected: ${[...path8, nodeName].join(" \u2192 ")}`);
23765
24627
  }
23766
24628
  if (visited.has(nodeName)) {
23767
24629
  return;
@@ -23776,7 +24638,7 @@ var DependencyGraphBuilder = class {
23776
24638
  }
23777
24639
  visiting.add(nodeName);
23778
24640
  for (const dependency of node.dependencies) {
23779
- visit(dependency, [...path7, nodeName]);
24641
+ visit(dependency, [...path8, nodeName]);
23780
24642
  }
23781
24643
  visiting.delete(nodeName);
23782
24644
  visited.add(nodeName);
@@ -25509,59 +26371,59 @@ var DANGEROUS_PATH_PATTERNS = [
25509
26371
  // Common data drive (Windows, alt format)
25510
26372
  ];
25511
26373
  var SUSPICIOUS_PATH_CHARS = /[<>:|"]/;
25512
- function validatePathParameter(path7, paramName, projectRoot = process.cwd()) {
25513
- if (!path7 || path7.trim() === "") {
26374
+ function validatePathParameter(path8, paramName, projectRoot = process.cwd()) {
26375
+ if (!path8 || path8.trim() === "") {
25514
26376
  throw new ValidationError(
25515
26377
  `Invalid ${paramName}: path cannot be empty`,
25516
26378
  -32602 /* InvalidParams */,
25517
- { path: path7, paramName }
26379
+ { path: path8, paramName }
25518
26380
  );
25519
26381
  }
25520
26382
  for (const pattern of DANGEROUS_PATH_PATTERNS) {
25521
- if (path7.includes(pattern)) {
26383
+ if (path8.includes(pattern)) {
25522
26384
  throw new ValidationError(
25523
26385
  `Invalid ${paramName}: path contains dangerous pattern "${pattern}"`,
25524
26386
  -32602 /* InvalidParams */,
25525
- { path: path7, paramName, pattern }
26387
+ { path: path8, paramName, pattern }
25526
26388
  );
25527
26389
  }
25528
26390
  }
25529
- if (isAbsolute(path7)) {
26391
+ if (isAbsolute(path8)) {
25530
26392
  throw new ValidationError(
25531
26393
  `Invalid ${paramName}: absolute paths are not allowed`,
25532
26394
  -32602 /* InvalidParams */,
25533
- { path: path7, paramName }
26395
+ { path: path8, paramName }
25534
26396
  );
25535
26397
  }
25536
26398
  try {
25537
- const resolvedPath = resolve(projectRoot, path7);
26399
+ const resolvedPath = resolve(projectRoot, path8);
25538
26400
  const normalizedRoot = resolve(projectRoot);
25539
26401
  if (!resolvedPath.startsWith(normalizedRoot + sep) && resolvedPath !== normalizedRoot) {
25540
26402
  throw new ValidationError(
25541
26403
  `Invalid ${paramName}: path escapes project boundary`,
25542
26404
  -32602 /* InvalidParams */,
25543
- { path: path7, paramName, projectRoot, resolvedPath }
26405
+ { path: path8, paramName, projectRoot, resolvedPath }
25544
26406
  );
25545
26407
  }
25546
26408
  } catch (error) {
25547
26409
  throw new ValidationError(
25548
26410
  `Invalid ${paramName}: path resolution failed`,
25549
26411
  -32602 /* InvalidParams */,
25550
- { path: path7, paramName, error: String(error) }
26412
+ { path: path8, paramName, error: String(error) }
25551
26413
  );
25552
26414
  }
25553
- if (path7.includes("\0")) {
26415
+ if (path8.includes("\0")) {
25554
26416
  throw new ValidationError(
25555
26417
  `Invalid ${paramName}: path contains null byte`,
25556
26418
  -32602 /* InvalidParams */,
25557
- { path: path7, paramName }
26419
+ { path: path8, paramName }
25558
26420
  );
25559
26421
  }
25560
- if (SUSPICIOUS_PATH_CHARS.test(path7)) {
26422
+ if (SUSPICIOUS_PATH_CHARS.test(path8)) {
25561
26423
  throw new ValidationError(
25562
26424
  `Invalid ${paramName}: path contains invalid characters`,
25563
26425
  -32602 /* InvalidParams */,
25564
- { path: path7, paramName }
26426
+ { path: path8, paramName }
25565
26427
  );
25566
26428
  }
25567
26429
  }
@@ -26257,8 +27119,8 @@ function createMemoryExportHandler(deps) {
26257
27119
  return async (input) => {
26258
27120
  logger.info("[MCP] memory_export called", { input });
26259
27121
  try {
26260
- const { path: path7 } = input;
26261
- const absolutePath = resolveExportPath(deps.pathResolver, path7);
27122
+ const { path: path8 } = input;
27123
+ const absolutePath = resolveExportPath(deps.pathResolver, path8);
26262
27124
  const exported = await deps.memoryManager.exportToJSON(absolutePath);
26263
27125
  const result = {
26264
27126
  success: true,
@@ -26294,8 +27156,8 @@ function createMemoryImportHandler(deps) {
26294
27156
  return async (input) => {
26295
27157
  logger.info("[MCP] memory_import called", { input });
26296
27158
  try {
26297
- const { path: path7 } = input;
26298
- const absolutePath = resolveImportPath(deps.pathResolver, path7);
27159
+ const { path: path8 } = input;
27160
+ const absolutePath = resolveImportPath(deps.pathResolver, path8);
26299
27161
  const imported = await deps.memoryManager.importFromJSON(absolutePath);
26300
27162
  const result = {
26301
27163
  success: true,
@@ -26848,7 +27710,8 @@ var McpClient = class extends EventEmitter {
26848
27710
  * Send JSON-RPC request and wait for response
26849
27711
  */
26850
27712
  async sendRequest(method, params, timeout) {
26851
- if (!this.process?.stdin) {
27713
+ const stdin = this.process?.stdin;
27714
+ if (!stdin) {
26852
27715
  throw new Error("MCP client not connected");
26853
27716
  }
26854
27717
  const id = this.nextRequestId++;
@@ -26870,7 +27733,18 @@ var McpClient = class extends EventEmitter {
26870
27733
  timeout: timeoutHandle
26871
27734
  });
26872
27735
  const message = JSON.stringify(request) + "\n";
26873
- this.process.stdin.write(message);
27736
+ try {
27737
+ stdin.write(message);
27738
+ } catch (error) {
27739
+ const pending = this.pendingRequests.get(id);
27740
+ if (pending) {
27741
+ clearTimeout(pending.timeout);
27742
+ this.pendingRequests.delete(id);
27743
+ }
27744
+ const err = error instanceof Error ? error : new Error(String(error));
27745
+ reject(err);
27746
+ return;
27747
+ }
26874
27748
  logger.debug("[MCP Client] Sent request", {
26875
27749
  id,
26876
27750
  method,
@@ -26880,6 +27754,10 @@ var McpClient = class extends EventEmitter {
26880
27754
  }
26881
27755
  /**
26882
27756
  * Send JSON-RPC notification (no response expected)
27757
+ *
27758
+ * BUG FIX: Handle write errors gracefully. If the process has exited or the
27759
+ * pipe is broken, write() could throw an uncaught exception that would crash
27760
+ * the application.
26883
27761
  */
26884
27762
  sendNotification(method, params) {
26885
27763
  if (!this.process?.stdin) {
@@ -26891,7 +27769,14 @@ var McpClient = class extends EventEmitter {
26891
27769
  params
26892
27770
  };
26893
27771
  const message = JSON.stringify(notification) + "\n";
26894
- this.process.stdin.write(message);
27772
+ try {
27773
+ this.process.stdin.write(message);
27774
+ } catch (error) {
27775
+ logger.debug("[MCP Client] Failed to send notification (pipe may be closed)", {
27776
+ method,
27777
+ error: error instanceof Error ? error.message : String(error)
27778
+ });
27779
+ }
26895
27780
  }
26896
27781
  /**
26897
27782
  * Handle incoming JSON-RPC message
@@ -27242,6 +28127,11 @@ var McpClientPool = class extends EventEmitter {
27242
28127
  }
27243
28128
  /**
27244
28129
  * Check if pool has available capacity for provider
28130
+ *
28131
+ * BUG FIX: Account for pendingConnections to avoid reporting capacity
28132
+ * when concurrent connection creations are in progress. Previously,
28133
+ * hasCapacity() could return true even when all slots were about to be
28134
+ * filled by in-flight createConnection() calls, causing over-allocation.
27245
28135
  */
27246
28136
  hasCapacity(provider) {
27247
28137
  const pool = this.pools.get(provider);
@@ -27255,7 +28145,8 @@ var McpClientPool = class extends EventEmitter {
27255
28145
  connectedCount++;
27256
28146
  }
27257
28147
  }
27258
- return connectedCount < this.config.maxConnectionsPerProvider;
28148
+ const totalAllocated = connectedCount + pool.pendingConnections;
28149
+ return totalAllocated < this.config.maxConnectionsPerProvider;
27259
28150
  }
27260
28151
  // ============================================
27261
28152
  // Private Methods
@@ -27378,6 +28269,10 @@ var McpClientPool = class extends EventEmitter {
27378
28269
  }
27379
28270
  }
27380
28271
  for (const pooledClient of toRemove) {
28272
+ if (pooledClient.inUse) {
28273
+ logger.debug("[MCP Pool] Skipping idle client removal - now in use", { provider });
28274
+ continue;
28275
+ }
27381
28276
  logger.debug("[MCP Pool] Closing idle connection", {
27382
28277
  provider,
27383
28278
  idleTimeMs: now - pooledClient.lastUsed
@@ -29606,20 +30501,20 @@ var LifecycleLogger = class {
29606
30501
  }
29607
30502
  /**
29608
30503
  * Rotate log file if it exceeds size limit
30504
+ *
30505
+ * BUG FIX: Previously used dynamic imports for fs/fs.promises which are already
30506
+ * imported at module level, causing unnecessary overhead. Also had a TOCTOU race
30507
+ * between existsSync and statSync - now uses async stat() which handles ENOENT
30508
+ * gracefully without the race condition.
29609
30509
  */
29610
30510
  async rotateLogIfNeeded(logPath) {
29611
- if (!existsSync(logPath)) {
29612
- return;
29613
- }
29614
30511
  try {
29615
- const { statSync: statSync5 } = await import('fs');
29616
- const stats = statSync5(logPath);
30512
+ const stats = await stat(logPath);
29617
30513
  const sizeMB = stats.size / (1024 * 1024);
29618
30514
  if (sizeMB > this.maxLogSizeMB) {
29619
30515
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
29620
30516
  const archivePath = logPath.replace(".jsonl", `-${timestamp}.jsonl`);
29621
- const { rename: rename4 } = await import('fs/promises');
29622
- await rename4(logPath, archivePath);
30517
+ await rename(logPath, archivePath);
29623
30518
  logger.info("LifecycleLogger: Rotated log file", {
29624
30519
  oldPath: logPath,
29625
30520
  newPath: archivePath,
@@ -29627,6 +30522,9 @@ var LifecycleLogger = class {
29627
30522
  });
29628
30523
  }
29629
30524
  } catch (error) {
30525
+ if (error.code === "ENOENT") {
30526
+ return;
30527
+ }
29630
30528
  logger.error("LifecycleLogger: Failed to rotate log file", { error });
29631
30529
  }
29632
30530
  }
@@ -29655,6 +30553,8 @@ var MetricsCollector = class {
29655
30553
  failedRequestCounts = /* @__PURE__ */ new Map();
29656
30554
  responseTimes = /* @__PURE__ */ new Map();
29657
30555
  restartCounts = /* @__PURE__ */ new Map();
30556
+ /** Track which servers have had their first start recorded */
30557
+ serverStarted = /* @__PURE__ */ new Set();
29658
30558
  constructor(options = {}) {
29659
30559
  this.enabled = options.enabled ?? true;
29660
30560
  this.collectionIntervalMs = options.collectionIntervalMs ?? 1e4;
@@ -29693,15 +30593,27 @@ var MetricsCollector = class {
29693
30593
  }
29694
30594
  /**
29695
30595
  * Record server start
30596
+ *
30597
+ * BUG FIX: Previously incremented restartCount on every start including the first one,
30598
+ * so a server that never restarted would show restartCount: 1. Now correctly tracks
30599
+ * only actual restarts (starts after the first one).
29696
30600
  */
29697
30601
  recordServerStart(serverName) {
29698
30602
  if (!this.enabled) return;
29699
- const currentRestarts = this.restartCounts.get(serverName) || 0;
29700
- this.restartCounts.set(serverName, currentRestarts + 1);
29701
- logger.debug("MetricsCollector: Recorded server start", {
29702
- serverName,
29703
- restartCount: currentRestarts + 1
29704
- });
30603
+ if (this.serverStarted.has(serverName)) {
30604
+ const currentRestarts = this.restartCounts.get(serverName) || 0;
30605
+ this.restartCounts.set(serverName, currentRestarts + 1);
30606
+ logger.debug("MetricsCollector: Recorded server restart", {
30607
+ serverName,
30608
+ restartCount: currentRestarts + 1
30609
+ });
30610
+ } else {
30611
+ this.serverStarted.add(serverName);
30612
+ this.restartCounts.set(serverName, 0);
30613
+ logger.debug("MetricsCollector: Recorded server first start", {
30614
+ serverName
30615
+ });
30616
+ }
29705
30617
  }
29706
30618
  /**
29707
30619
  * Record request
@@ -29834,6 +30746,7 @@ var MetricsCollector = class {
29834
30746
  this.failedRequestCounts.delete(serverName);
29835
30747
  this.responseTimes.delete(serverName);
29836
30748
  this.restartCounts.delete(serverName);
30749
+ this.serverStarted.delete(serverName);
29837
30750
  logger.info("MetricsCollector: Cleared history for server", { serverName });
29838
30751
  } else {
29839
30752
  this.metricsHistory.clear();
@@ -29841,6 +30754,7 @@ var MetricsCollector = class {
29841
30754
  this.failedRequestCounts.clear();
29842
30755
  this.responseTimes.clear();
29843
30756
  this.restartCounts.clear();
30757
+ this.serverStarted.clear();
29844
30758
  logger.info("MetricsCollector: Cleared all history");
29845
30759
  }
29846
30760
  }
@@ -29926,6 +30840,8 @@ var ResourceEnforcer = class {
29926
30840
  violationHandlers = [];
29927
30841
  violationTimestamps = /* @__PURE__ */ new Map();
29928
30842
  monitoredServers = /* @__PURE__ */ new Map();
30843
+ /** Track throttle resume timers to prevent leaks on stop() */
30844
+ throttleTimers = /* @__PURE__ */ new Map();
29929
30845
  constructor(options) {
29930
30846
  this.defaultLimits = options.defaultLimits;
29931
30847
  this.serverLimits = options.serverLimits || /* @__PURE__ */ new Map();
@@ -29952,13 +30868,20 @@ var ResourceEnforcer = class {
29952
30868
  }
29953
30869
  /**
29954
30870
  * Stop enforcing resource limits
30871
+ *
30872
+ * BUG FIX: Now also clears all pending throttle resume timers to prevent
30873
+ * them from executing after stop() is called.
29955
30874
  */
29956
30875
  stop() {
29957
30876
  if (this.checkInterval) {
29958
30877
  clearInterval(this.checkInterval);
29959
30878
  this.checkInterval = void 0;
29960
- logger.info("ResourceEnforcer: Stopped enforcement");
29961
30879
  }
30880
+ for (const timer of this.throttleTimers.values()) {
30881
+ clearTimeout(timer);
30882
+ }
30883
+ this.throttleTimers.clear();
30884
+ logger.info("ResourceEnforcer: Stopped enforcement");
29962
30885
  }
29963
30886
  /**
29964
30887
  * Register server for monitoring
@@ -30108,19 +31031,34 @@ var ResourceEnforcer = class {
30108
31031
  }
30109
31032
  /**
30110
31033
  * Throttle process temporarily
31034
+ *
31035
+ * BUG FIX: Now tracks the resume timer so it can be cleaned up on stop().
31036
+ * Also checks if process is still running before attempting to resume.
30111
31037
  */
30112
31038
  async throttleProcess(serverProcess) {
31039
+ const serverName = serverProcess.config.name;
30113
31040
  try {
30114
31041
  logger.info("ResourceEnforcer: Throttling process", {
30115
- serverName: serverProcess.config.name,
31042
+ serverName,
30116
31043
  pid: serverProcess.process.pid
30117
31044
  });
30118
31045
  serverProcess.process.kill("SIGSTOP");
30119
- setTimeout(() => {
31046
+ const existingTimer = this.throttleTimers.get(serverName);
31047
+ if (existingTimer) {
31048
+ clearTimeout(existingTimer);
31049
+ }
31050
+ const resumeTimer = setTimeout(() => {
31051
+ this.throttleTimers.delete(serverName);
31052
+ if (!serverProcess.running || serverProcess.process.killed) {
31053
+ logger.debug("ResourceEnforcer: Skipping resume - process no longer running", {
31054
+ serverName
31055
+ });
31056
+ return;
31057
+ }
30120
31058
  try {
30121
31059
  serverProcess.process.kill("SIGCONT");
30122
31060
  logger.info("ResourceEnforcer: Resumed process", {
30123
- serverName: serverProcess.config.name
31061
+ serverName
30124
31062
  });
30125
31063
  } catch (error) {
30126
31064
  logger.error("ResourceEnforcer: Failed to resume process", {
@@ -30128,6 +31066,7 @@ var ResourceEnforcer = class {
30128
31066
  });
30129
31067
  }
30130
31068
  }, 1e3);
31069
+ this.throttleTimers.set(serverName, resumeTimer);
30131
31070
  } catch (error) {
30132
31071
  logger.error("ResourceEnforcer: Failed to throttle process", {
30133
31072
  error: error.message
@@ -30890,7 +31829,7 @@ var GeminiMCPManager = class {
30890
31829
  };
30891
31830
  var defaultManager = null;
30892
31831
  function getDefaultGeminiMCPManager(config) {
30893
- if (!defaultManager || config) {
31832
+ if (!defaultManager) {
30894
31833
  defaultManager = new GeminiMCPManager(
30895
31834
  {
30896
31835
  // Default empty config
@@ -31402,9 +32341,9 @@ var ProgressIndicator = class {
31402
32341
  // src/cli/commands/memory.ts
31403
32342
  var DEFAULT_DB_PATH = ".automatosx/memory/memory.db";
31404
32343
  function getMemoryManager(dbPath) {
31405
- const path7 = dbPath || DEFAULT_DB_PATH;
32344
+ const path8 = dbPath || DEFAULT_DB_PATH;
31406
32345
  return new LazyMemoryManager({
31407
- dbPath: resolve(path7),
32346
+ dbPath: resolve(path8),
31408
32347
  maxEntries: 1e5,
31409
32348
  autoCleanup: false,
31410
32349
  trackAccess: true
@@ -34405,14 +35344,14 @@ var IterateClassifier = class {
34405
35344
  * **Phase 1 (Week 1)**: Skeleton only (no-op)
34406
35345
  * **Phase 2 (Week 2)**: Full implementation with YAML loading and validation
34407
35346
  */
34408
- async loadPatterns(path7) {
34409
- logger.debug("Loading pattern library", { path: path7 });
34410
- if (!existsSync(path7)) {
34411
- logger.warn("Pattern library file not found", { path: path7 });
35347
+ async loadPatterns(path8) {
35348
+ logger.debug("Loading pattern library", { path: path8 });
35349
+ if (!existsSync(path8)) {
35350
+ logger.warn("Pattern library file not found", { path: path8 });
34412
35351
  return;
34413
35352
  }
34414
35353
  try {
34415
- const fileContent = await readFile(path7, "utf-8");
35354
+ const fileContent = await readFile(path8, "utf-8");
34416
35355
  const parsed = load(fileContent);
34417
35356
  if (!parsed || typeof parsed !== "object") {
34418
35357
  throw new Error("Invalid pattern library format: not an object");
@@ -34455,7 +35394,7 @@ var IterateClassifier = class {
34455
35394
  });
34456
35395
  } catch (error) {
34457
35396
  logger.error("Failed to load pattern library", {
34458
- path: path7,
35397
+ path: path8,
34459
35398
  error: error.message
34460
35399
  });
34461
35400
  throw error;
@@ -34612,7 +35551,8 @@ var IterateClassifier = class {
34612
35551
  checkProviderMarkers(message, provider) {
34613
35552
  const providerLower = provider.toLowerCase();
34614
35553
  if (providerLower.includes("claude")) {
34615
- if (message.includes("<THOUGHT>") || message.includes("<thinking>")) {
35554
+ const lowerMessage = message.toLowerCase();
35555
+ if (lowerMessage.includes("<thinking>") || lowerMessage.includes("</thinking>") || lowerMessage.includes("<thought>") || lowerMessage.includes("</thought>")) {
34616
35556
  return { type: "status_update", confidence: 0.85 };
34617
35557
  }
34618
35558
  if (message.includes("Let me break this") || message.includes("I'll break this")) {
@@ -34809,14 +35749,14 @@ var IterateAutoResponder = class {
34809
35749
  * **Phase 1 (Week 1)**: Skeleton only (no-op)
34810
35750
  * **Phase 3 (Week 3)**: Full implementation with YAML loading and validation
34811
35751
  */
34812
- async loadTemplates(path7) {
34813
- logger.debug("Loading template library", { path: path7 });
34814
- if (!existsSync(path7)) {
34815
- logger.warn("Template library file not found", { path: path7 });
35752
+ async loadTemplates(path8) {
35753
+ logger.debug("Loading template library", { path: path8 });
35754
+ if (!existsSync(path8)) {
35755
+ logger.warn("Template library file not found", { path: path8 });
34816
35756
  return;
34817
35757
  }
34818
35758
  try {
34819
- const fileContent = await readFile(path7, "utf-8");
35759
+ const fileContent = await readFile(path8, "utf-8");
34820
35760
  const parsed = load(fileContent);
34821
35761
  if (!parsed || typeof parsed !== "object") {
34822
35762
  throw new Error("Invalid template library format: not an object");
@@ -34858,7 +35798,7 @@ var IterateAutoResponder = class {
34858
35798
  });
34859
35799
  } catch (error) {
34860
35800
  logger.error("Failed to load template library", {
34861
- path: path7,
35801
+ path: path8,
34862
35802
  error: error.message
34863
35803
  });
34864
35804
  throw error;
@@ -35022,6 +35962,16 @@ var IterateModeController = class {
35022
35962
  * @param config - Iterate mode configuration
35023
35963
  * @param sessionManager - Session manager for state persistence (optional)
35024
35964
  */
35965
+ /**
35966
+ * Flag indicating if patterns/templates have been loaded
35967
+ * @private
35968
+ */
35969
+ initialized = false;
35970
+ /**
35971
+ * Promise for initialization (to avoid race conditions)
35972
+ * @private
35973
+ */
35974
+ initPromise;
35025
35975
  constructor(config, sessionManager, statusRenderer) {
35026
35976
  this.config = config;
35027
35977
  this.sessionManager = sessionManager;
@@ -35039,6 +35989,58 @@ var IterateModeController = class {
35039
35989
  strictness: config.classifier.strictness,
35040
35990
  hasStatusRenderer: !!statusRenderer
35041
35991
  });
35992
+ this.initPromise = this.loadPatternsAndTemplates();
35993
+ }
35994
+ /**
35995
+ * Load pattern and template libraries
35996
+ *
35997
+ * Automatically loads patterns.yaml and templates.yaml from the configured paths.
35998
+ * If files don't exist, uses fallback patterns.
35999
+ *
36000
+ * @private
36001
+ */
36002
+ async loadPatternsAndTemplates() {
36003
+ if (this.initialized) {
36004
+ return;
36005
+ }
36006
+ const patternsPath = this.config.classifier.patternLibraryPath;
36007
+ const templatesPath = patternsPath.replace("patterns.yaml", "templates.yaml");
36008
+ if (existsSync(patternsPath)) {
36009
+ try {
36010
+ await this.classifier.loadPatterns(patternsPath);
36011
+ logger.info("Loaded iterate patterns", { path: patternsPath });
36012
+ } catch (error) {
36013
+ logger.warn("Failed to load iterate patterns, using defaults", {
36014
+ path: patternsPath,
36015
+ error: error.message
36016
+ });
36017
+ }
36018
+ } else {
36019
+ logger.warn("Iterate patterns file not found, using defaults", { path: patternsPath });
36020
+ }
36021
+ if (existsSync(templatesPath)) {
36022
+ try {
36023
+ await this.responder.loadTemplates(templatesPath);
36024
+ logger.info("Loaded iterate templates", { path: templatesPath });
36025
+ } catch (error) {
36026
+ logger.warn("Failed to load iterate templates, using defaults", {
36027
+ path: templatesPath,
36028
+ error: error.message
36029
+ });
36030
+ }
36031
+ } else {
36032
+ logger.warn("Iterate templates file not found, using defaults", { path: templatesPath });
36033
+ }
36034
+ this.initialized = true;
36035
+ }
36036
+ /**
36037
+ * Ensure patterns and templates are loaded before use
36038
+ * @private
36039
+ */
36040
+ async ensureInitialized() {
36041
+ if (!this.initialized && this.initPromise) {
36042
+ await this.initPromise;
36043
+ }
35042
36044
  }
35043
36045
  /**
35044
36046
  * Execute agent with iterate mode enabled
@@ -35057,6 +36059,7 @@ var IterateModeController = class {
35057
36059
  * **Phase 3 (Week 3)**: Full implementation with orchestration loop
35058
36060
  */
35059
36061
  async executeWithIterate(context, options) {
36062
+ await this.ensureInitialized();
35060
36063
  const sessionId = this.sessionManager ? (await this.sessionManager.createSession(context.task, context.agent.name)).id : randomUUID();
35061
36064
  this.initializeState(sessionId);
35062
36065
  logger.info("Iterate mode execution started", {
@@ -35159,17 +36162,16 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35159
36162
  */
35160
36163
  async handleResponse(response) {
35161
36164
  const startTime = Date.now();
36165
+ await this.ensureInitialized();
35162
36166
  if (!this.state) {
35163
- logger.warn("handleResponse called without initialized state - returning skeleton response");
35164
- return {
35165
- type: "continue",
35166
- reason: "Skeleton implementation - state not initialized"
35167
- };
36167
+ logger.warn("handleResponse called without initialized state - initializing now");
36168
+ this.initializeState(randomUUID());
35168
36169
  }
36170
+ const state = this.state;
35169
36171
  logger.debug("Handling response", {
35170
36172
  hasContent: !!response.content,
35171
36173
  contentLength: response.content?.length || 0,
35172
- iterations: this.state.totalIterations
36174
+ iterations: state.totalIterations
35173
36175
  });
35174
36176
  const classification = await this.classifier.classify(
35175
36177
  response.content || "",
@@ -35180,12 +36182,12 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35180
36182
  tokenCount: response.tokensUsed?.total
35181
36183
  }
35182
36184
  );
35183
- this.state.classificationHistory.push(classification);
36185
+ state.classificationHistory.push(classification);
35184
36186
  const maxHistorySize = this.config.classifier.contextWindowMessages * 2;
35185
- if (this.state.classificationHistory.length > maxHistorySize) {
35186
- this.state.classificationHistory = this.state.classificationHistory.slice(-maxHistorySize);
36187
+ if (state.classificationHistory.length > maxHistorySize) {
36188
+ state.classificationHistory = state.classificationHistory.slice(-maxHistorySize);
35187
36189
  logger.debug("Trimmed classification history to prevent memory leak", {
35188
- newSize: this.state.classificationHistory.length,
36190
+ newSize: state.classificationHistory.length,
35189
36191
  maxSize: maxHistorySize
35190
36192
  });
35191
36193
  }
@@ -35229,8 +36231,18 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35229
36231
  context: `Maximum iterations reached`
35230
36232
  };
35231
36233
  }
36234
+ const safetyCheck = this.checkSafetyGuards(response.content || "");
36235
+ if (!safetyCheck.safe && this.config.notifications.pauseOnHighRiskOperation) {
36236
+ const dangerousOps = safetyCheck.detectedOperations.map((op) => `${op.type}: ${op.match}`).join(", ");
36237
+ return {
36238
+ type: "pause",
36239
+ reason: "Dangerous operation detected",
36240
+ pauseReason: "high_risk_operation",
36241
+ context: `Detected: ${dangerousOps}`
36242
+ };
36243
+ }
35232
36244
  const action = this.determineAction(classification, response);
35233
- if (action.type === "continue" && action.response === void 0) {
36245
+ if ((action.type === "continue" || action.type === "stop") && action.response === void 0) {
35234
36246
  const autoResponse = await this.responder.generateResponse(
35235
36247
  classification,
35236
36248
  {
@@ -35258,7 +36270,7 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35258
36270
  this.emitEvent({
35259
36271
  type: "iterate.classification",
35260
36272
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35261
- sessionId: this.state.sessionId,
36273
+ sessionId: state.sessionId,
35262
36274
  payload: {
35263
36275
  classification,
35264
36276
  action: action.type,
@@ -35317,8 +36329,8 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35317
36329
  }
35318
36330
  if (type === "completion_signal") {
35319
36331
  return {
35320
- type: "continue",
35321
- reason: "Completion signal - acknowledging",
36332
+ type: "stop",
36333
+ reason: "Completion signal - task finished",
35322
36334
  metadata: { confidence }
35323
36335
  };
35324
36336
  }
@@ -35538,9 +36550,8 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35538
36550
  // v8.5.8 - Deprecated, always 0 (cost tracking removed)
35539
36551
  classificationBreakdown: breakdown,
35540
36552
  avgClassificationLatencyMs: Math.round(avgLatency),
35541
- safetyChecks: {
36553
+ safetyChecks: this.state.metadata.safetyChecks || {
35542
36554
  total: 0,
35543
- // TODO: Track safety checks
35544
36555
  allowed: 0,
35545
36556
  paused: 0,
35546
36557
  blocked: 0
@@ -35552,19 +36563,42 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35552
36563
  /**
35553
36564
  * Emit telemetry event
35554
36565
  *
36566
+ * Writes events to JSONL telemetry file for observability.
36567
+ *
35555
36568
  * @param event - Telemetry event to emit
35556
36569
  * @private
35557
- *
35558
- * **Phase 1 (Week 1)**: Logs only
35559
- * **Phase 4 (Week 4)**: Full telemetry with file logging
35560
36570
  */
35561
36571
  emitEvent(event) {
35562
- if (this.config.telemetry.emitMetrics) {
35563
- logger.debug("Iterate event (skeleton)", {
35564
- type: event.type,
35565
- sessionId: event.sessionId
35566
- });
36572
+ if (!this.config.telemetry.emitMetrics) {
36573
+ return;
36574
+ }
36575
+ logger.debug("Iterate event", {
36576
+ type: event.type,
36577
+ sessionId: event.sessionId
36578
+ });
36579
+ const patternsPath = this.config.classifier.patternLibraryPath;
36580
+ const iterateDir = dirname(patternsPath);
36581
+ const baseDir = dirname(iterateDir);
36582
+ const logDir = join(baseDir, "logs");
36583
+ const logPath = join(logDir, `iterate-trace-${event.sessionId}.jsonl`);
36584
+ const line = JSON.stringify(event) + "\n";
36585
+ if (!existsSync(logDir)) {
36586
+ try {
36587
+ mkdirSync(logDir, { recursive: true });
36588
+ } catch {
36589
+ }
35567
36590
  }
36591
+ appendFile(logPath, line).catch((error) => {
36592
+ if (!this.state?.metadata.telemetryErrorLogged) {
36593
+ logger.warn("Failed to write iterate telemetry", {
36594
+ path: logPath,
36595
+ error: error.message
36596
+ });
36597
+ if (this.state) {
36598
+ this.state.metadata.telemetryErrorLogged = true;
36599
+ }
36600
+ }
36601
+ });
35568
36602
  }
35569
36603
  /**
35570
36604
  * Check if time budget exceeded
@@ -35771,6 +36805,175 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
35771
36805
  timestamp: c.timestamp
35772
36806
  }));
35773
36807
  }
36808
+ /**
36809
+ * Check for dangerous operations in response content
36810
+ *
36811
+ * Detects potentially destructive commands and operations that should
36812
+ * pause execution for user confirmation.
36813
+ *
36814
+ * @param content - Response content to check
36815
+ * @returns Safety check result with detected operations
36816
+ */
36817
+ checkSafetyGuards(content) {
36818
+ if (!this.config.safety.enableDangerousOperationGuard) {
36819
+ return { safe: true, detectedOperations: [] };
36820
+ }
36821
+ const detectedOperations = [];
36822
+ const dangerousPatterns = [
36823
+ // File deletion (HIGH risk)
36824
+ {
36825
+ pattern: /\brm\s+-rf\s+[\/~]/i,
36826
+ type: "fileDelete",
36827
+ risk: "HIGH"
36828
+ },
36829
+ {
36830
+ pattern: /\brm\s+-rf\s+\./i,
36831
+ type: "fileDelete",
36832
+ risk: "HIGH"
36833
+ },
36834
+ {
36835
+ pattern: /\brmdir\s+.*--ignore-fail-on-non-empty/i,
36836
+ type: "fileDelete",
36837
+ risk: "MEDIUM"
36838
+ },
36839
+ // Git force operations (HIGH risk)
36840
+ {
36841
+ pattern: /\bgit\s+push\s+.*--force/i,
36842
+ type: "gitForce",
36843
+ risk: "HIGH"
36844
+ },
36845
+ {
36846
+ pattern: /\bgit\s+push\s+-f\b/i,
36847
+ type: "gitForce",
36848
+ risk: "HIGH"
36849
+ },
36850
+ {
36851
+ pattern: /\bgit\s+reset\s+--hard/i,
36852
+ type: "gitForce",
36853
+ risk: "HIGH"
36854
+ },
36855
+ {
36856
+ pattern: /\bgit\s+clean\s+-fd/i,
36857
+ type: "gitForce",
36858
+ risk: "MEDIUM"
36859
+ },
36860
+ // Database operations (HIGH risk)
36861
+ {
36862
+ pattern: /\bDROP\s+(TABLE|DATABASE|SCHEMA)\b/i,
36863
+ type: "databaseDrop",
36864
+ risk: "HIGH"
36865
+ },
36866
+ {
36867
+ pattern: /\bTRUNCATE\s+TABLE\b/i,
36868
+ type: "databaseTruncate",
36869
+ risk: "HIGH"
36870
+ },
36871
+ {
36872
+ pattern: /\bDELETE\s+FROM\s+\w+\s*(;|$|WHERE\s+1\s*=\s*1)/i,
36873
+ type: "databaseDelete",
36874
+ risk: "HIGH"
36875
+ },
36876
+ // Secrets/credentials in code (HIGH risk)
36877
+ {
36878
+ pattern: /\b(password|secret|api_key|apikey|api-key|token)\s*[=:]\s*["'][^"']{8,}["']/i,
36879
+ type: "secretsInCode",
36880
+ risk: "HIGH"
36881
+ },
36882
+ {
36883
+ pattern: /\b(AWS_SECRET|PRIVATE_KEY|AUTH_TOKEN)\s*[=:]/i,
36884
+ type: "secretsInCode",
36885
+ risk: "HIGH"
36886
+ },
36887
+ // Shell commands (MEDIUM risk based on config)
36888
+ {
36889
+ pattern: /\bsudo\s+/i,
36890
+ type: "shellCommands",
36891
+ risk: "MEDIUM"
36892
+ },
36893
+ {
36894
+ pattern: /\bchmod\s+777\b/i,
36895
+ type: "shellCommands",
36896
+ risk: "MEDIUM"
36897
+ },
36898
+ {
36899
+ pattern: /\bcurl\s+.*\|\s*(bash|sh)\b/i,
36900
+ type: "shellCommands",
36901
+ risk: "HIGH"
36902
+ },
36903
+ {
36904
+ pattern: /\bwget\s+.*\|\s*(bash|sh)\b/i,
36905
+ type: "shellCommands",
36906
+ risk: "HIGH"
36907
+ },
36908
+ // Package installation (MEDIUM risk)
36909
+ {
36910
+ pattern: /\bnpm\s+install\s+--global\b/i,
36911
+ type: "packageInstall",
36912
+ risk: "MEDIUM"
36913
+ },
36914
+ {
36915
+ pattern: /\bpip\s+install\s+--user\b/i,
36916
+ type: "packageInstall",
36917
+ risk: "LOW"
36918
+ },
36919
+ // Writing outside workspace (HIGH risk)
36920
+ {
36921
+ pattern: /\b(write|create|save)\s+.*[\/~](etc|usr|bin|lib|opt)\//i,
36922
+ type: "writeOutsideWorkspace",
36923
+ risk: "HIGH"
36924
+ },
36925
+ {
36926
+ pattern: /\becho\s+.*>\s*[\/~](etc|usr|bin)/i,
36927
+ type: "writeOutsideWorkspace",
36928
+ risk: "HIGH"
36929
+ }
36930
+ ];
36931
+ for (const { pattern, type, risk } of dangerousPatterns) {
36932
+ const match = content.match(pattern);
36933
+ if (match) {
36934
+ const configuredRisk = this.config.safety.dangerousOperations[type];
36935
+ if (configuredRisk) {
36936
+ detectedOperations.push({
36937
+ type,
36938
+ risk,
36939
+ match: match[0].substring(0, 100)
36940
+ // Truncate long matches
36941
+ });
36942
+ }
36943
+ }
36944
+ }
36945
+ const riskTolerance = this.config.safety.riskTolerance;
36946
+ const isSafe = detectedOperations.every((op) => {
36947
+ if (riskTolerance === "permissive") {
36948
+ return op.risk !== "HIGH";
36949
+ } else if (riskTolerance === "balanced") {
36950
+ return op.risk === "LOW";
36951
+ } else {
36952
+ return false;
36953
+ }
36954
+ });
36955
+ if (detectedOperations.length > 0) {
36956
+ logger.warn("Dangerous operations detected", {
36957
+ count: detectedOperations.length,
36958
+ operations: detectedOperations,
36959
+ safe: isSafe,
36960
+ riskTolerance
36961
+ });
36962
+ if (this.state) {
36963
+ this.state.metadata.safetyChecks = this.state.metadata.safetyChecks || { total: 0, allowed: 0, paused: 0, blocked: 0 };
36964
+ this.state.metadata.safetyChecks.total++;
36965
+ if (isSafe) {
36966
+ this.state.metadata.safetyChecks.allowed++;
36967
+ } else {
36968
+ this.state.metadata.safetyChecks.paused++;
36969
+ }
36970
+ }
36971
+ }
36972
+ return {
36973
+ safe: isSafe || detectedOperations.length === 0,
36974
+ detectedOperations
36975
+ };
36976
+ }
35774
36977
  };
35775
36978
 
35776
36979
  // src/cli/renderers/iterate-status-renderer.ts
@@ -37538,7 +38741,10 @@ Result: ${result.stages.map((s) => s.output).join("\n\n")}`;
37538
38741
  writeOutsideWorkspace: "HIGH",
37539
38742
  secretsInCode: "HIGH",
37540
38743
  shellCommands: "MEDIUM",
37541
- packageInstall: "MEDIUM"
38744
+ packageInstall: "MEDIUM",
38745
+ databaseDrop: "HIGH",
38746
+ databaseTruncate: "HIGH",
38747
+ databaseDelete: "HIGH"
37542
38748
  },
37543
38749
  enableTimeTracking: true,
37544
38750
  enableIterationTracking: true
@@ -38670,11 +39876,11 @@ async function getCurrentVersion() {
38670
39876
  return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
38671
39877
  } catch (error) {
38672
39878
  const { readFile: readFile24 } = await import('fs/promises');
38673
- const { dirname: dirname15, join: join50 } = await import('path');
39879
+ const { dirname: dirname16, join: join52 } = await import('path');
38674
39880
  const { fileURLToPath: fileURLToPath5 } = await import('url');
38675
39881
  const __filename3 = fileURLToPath5(import.meta.url);
38676
- const __dirname4 = dirname15(__filename3);
38677
- const pkgPath = join50(__dirname4, "../../../package.json");
39882
+ const __dirname4 = dirname16(__filename3);
39883
+ const pkgPath = join52(__dirname4, "../../../package.json");
38678
39884
  const content = await readFile24(pkgPath, "utf-8");
38679
39885
  const pkg = JSON.parse(content);
38680
39886
  return pkg.version;
@@ -40744,8 +41950,8 @@ var ConfigManager = class {
40744
41950
  * @returns User configuration or empty object if not found
40745
41951
  */
40746
41952
  async readUserConfig() {
40747
- const path7 = getUserConfigPath();
40748
- return this.readConfig(path7, "user");
41953
+ const path8 = getUserConfigPath();
41954
+ return this.readConfig(path8, "user");
40749
41955
  }
40750
41956
  /**
40751
41957
  * Read project-level Gemini CLI configuration
@@ -40753,8 +41959,8 @@ var ConfigManager = class {
40753
41959
  * @returns Project configuration or empty object if not found
40754
41960
  */
40755
41961
  async readProjectConfig() {
40756
- const path7 = getProjectConfigPath();
40757
- return this.readConfig(path7, "project");
41962
+ const path8 = getProjectConfigPath();
41963
+ return this.readConfig(path8, "project");
40758
41964
  }
40759
41965
  /**
40760
41966
  * Read configuration from a specific path with caching
@@ -40764,7 +41970,7 @@ var ConfigManager = class {
40764
41970
  * @returns Configuration object
40765
41971
  * @private
40766
41972
  */
40767
- async readConfig(path7, scope) {
41973
+ async readConfig(path8, scope) {
40768
41974
  const cached = this.cache.get(scope);
40769
41975
  if (cached && Date.now() - cached.timestamp < this.ttl) {
40770
41976
  return cached.data;
@@ -40775,13 +41981,13 @@ var ConfigManager = class {
40775
41981
  }
40776
41982
  const readOperation = (async () => {
40777
41983
  try {
40778
- const exists = await fileExists(path7);
41984
+ const exists = await fileExists(path8);
40779
41985
  if (!exists) {
40780
41986
  const emptyConfig = {};
40781
41987
  this.cache.set(scope, { data: emptyConfig, timestamp: Date.now() });
40782
41988
  return emptyConfig;
40783
41989
  }
40784
- const config = await readJsonFile(path7);
41990
+ const config = await readJsonFile(path8);
40785
41991
  this.cache.set(scope, { data: config, timestamp: Date.now() });
40786
41992
  return config;
40787
41993
  } catch (error) {
@@ -40790,8 +41996,8 @@ var ConfigManager = class {
40790
41996
  }
40791
41997
  throw new GeminiCLIError(
40792
41998
  "INVALID_CONFIG" /* INVALID_CONFIG */,
40793
- `Failed to read configuration from ${path7}`,
40794
- { path: path7, originalError: error }
41999
+ `Failed to read configuration from ${path8}`,
42000
+ { path: path8, originalError: error }
40795
42001
  );
40796
42002
  } finally {
40797
42003
  this.pendingReads.delete(scope);
@@ -41461,8 +42667,8 @@ var CommandTranslator = class {
41461
42667
  paths.push(getProjectCommandsPath());
41462
42668
  }
41463
42669
  }
41464
- for (const path7 of paths) {
41465
- const discovered = await this.scanDirectory(path7);
42670
+ for (const path8 of paths) {
42671
+ const discovered = await this.scanDirectory(path8);
41466
42672
  commands.push(...discovered);
41467
42673
  }
41468
42674
  return commands;
@@ -41503,8 +42709,8 @@ var CommandTranslator = class {
41503
42709
  const results = [];
41504
42710
  for (const name of commandNames) {
41505
42711
  try {
41506
- const path7 = await this.importCommand(name, outputDir, options);
41507
- results.push(path7);
42712
+ const path8 = await this.importCommand(name, outputDir, options);
42713
+ results.push(path8);
41508
42714
  } catch (error) {
41509
42715
  if (error instanceof GeminiCLIError) {
41510
42716
  console.error(`Failed to import ${name}: ${error.message}`);
@@ -42836,7 +44042,7 @@ var SpecSchemaValidator = class {
42836
44042
  * Convert Ajv error to ValidationIssue
42837
44043
  */
42838
44044
  convertAjvError(error) {
42839
- const path7 = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
44045
+ const path8 = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
42840
44046
  const keyword = error.keyword;
42841
44047
  let message = error.message || "Validation error";
42842
44048
  let suggestion;
@@ -42887,7 +44093,7 @@ var SpecSchemaValidator = class {
42887
44093
  ruleId: `SCHEMA-${keyword.toUpperCase()}`,
42888
44094
  severity: "error",
42889
44095
  message,
42890
- path: path7 || void 0,
44096
+ path: path8 || void 0,
42891
44097
  suggestion
42892
44098
  };
42893
44099
  }
@@ -45706,10 +46912,10 @@ async function handleReset() {
45706
46912
  `));
45707
46913
  }
45708
46914
  async function handleTrace(workspacePath, argv) {
45709
- const { existsSync: existsSync26, readFileSync: readFileSync9, watchFile } = await import('fs');
45710
- const { join: join50 } = await import('path');
45711
- const traceFile = join50(workspacePath, ".automatosx/logs/router.trace.jsonl");
45712
- if (!existsSync26(traceFile)) {
46915
+ const { existsSync: existsSync27, readFileSync: readFileSync9, watchFile } = await import('fs');
46916
+ const { join: join52 } = await import('path');
46917
+ const traceFile = join52(workspacePath, ".automatosx/logs/router.trace.jsonl");
46918
+ if (!existsSync27(traceFile)) {
45713
46919
  console.log(chalk5.yellow("\n\u26A0\uFE0F No trace log found\n"));
45714
46920
  console.log(chalk5.gray(`Expected location: ${traceFile}
45715
46921
  `));
@@ -48617,13 +49823,13 @@ Time since last change: ${timeSinceLastChange}ms`
48617
49823
  init_logger();
48618
49824
  var flagManagerInstances = /* @__PURE__ */ new Map();
48619
49825
  function getFlagManager(workspacePath) {
48620
- const path7 = process.cwd();
48621
- if (!flagManagerInstances.has(path7)) {
48622
- const manager = new FeatureFlagManager(path7);
49826
+ const path8 = process.cwd();
49827
+ if (!flagManagerInstances.has(path8)) {
49828
+ const manager = new FeatureFlagManager(path8);
48623
49829
  initializeFlags(manager);
48624
- flagManagerInstances.set(path7, manager);
49830
+ flagManagerInstances.set(path8, manager);
48625
49831
  }
48626
- return flagManagerInstances.get(path7);
49832
+ return flagManagerInstances.get(path8);
48627
49833
  }
48628
49834
  function initializeFlags(manager) {
48629
49835
  if (!manager.hasStorage()) {
@@ -48944,9 +50150,9 @@ var cliCommand = {
48944
50150
  // src/cli/commands/uninstall.ts
48945
50151
  init_esm_shims();
48946
50152
  init_logger();
48947
- async function fileExists2(path7) {
50153
+ async function fileExists2(path8) {
48948
50154
  try {
48949
- await access$1(path7);
50155
+ await access$1(path8);
48950
50156
  return true;
48951
50157
  } catch {
48952
50158
  return false;
@@ -49519,7 +50725,6 @@ var OrchestrationInstructionInjector = class {
49519
50725
  providers = /* @__PURE__ */ new Map();
49520
50726
  budgetManager;
49521
50727
  config;
49522
- lastInjectionTime = 0;
49523
50728
  constructor(config) {
49524
50729
  this.config = {
49525
50730
  ...DEFAULT_ORCHESTRATION_CONFIG,
@@ -49612,7 +50817,6 @@ var OrchestrationInstructionInjector = class {
49612
50817
  allocation = this.budgetManager.allocateBudget(activeInstructions);
49613
50818
  }
49614
50819
  const formattedText = this.formatInstructions(allocation.included);
49615
- this.lastInjectionTime = Date.now();
49616
50820
  const duration = Date.now() - startTime;
49617
50821
  logger.debug("Instruction injection complete", {
49618
50822
  providers: this.providers.size,
@@ -50226,7 +51430,7 @@ var SessionInstructionProvider = class {
50226
51430
  if (!this.config.enabled) {
50227
51431
  return false;
50228
51432
  }
50229
- if (!context.sessionId && !this.stateProvider) {
51433
+ if (!context.sessionId && !this.stateProvider && !context.parentAgent) {
50230
51434
  return false;
50231
51435
  }
50232
51436
  const turnsSinceReminder = context.turnCount - this.lastReminderTurn;
@@ -50238,7 +51442,7 @@ var SessionInstructionProvider = class {
50238
51442
  async getInstructions(context) {
50239
51443
  const instructions = [];
50240
51444
  const state = await this.getSessionState(context);
50241
- if (!state && !context.sessionId) {
51445
+ if (!state && !context.sessionId && !context.parentAgent) {
50242
51446
  return instructions;
50243
51447
  }
50244
51448
  const content = this.formatSessionContext(context, state);
@@ -51190,7 +52394,6 @@ var AgentInstructionInjector = class {
51190
52394
  var OrchestrationService = class {
51191
52395
  config;
51192
52396
  injector;
51193
- tokenBudgetManager;
51194
52397
  workflowModeManager;
51195
52398
  agentInjector;
51196
52399
  todoProvider;
@@ -51204,7 +52407,6 @@ var OrchestrationService = class {
51204
52407
  ...serviceConfig
51205
52408
  };
51206
52409
  this.injector = new OrchestrationInstructionInjector(this.config);
51207
- this.tokenBudgetManager = new TokenBudgetManager(this.config.tokenBudget);
51208
52410
  this.workflowModeManager = new WorkflowModeManager();
51209
52411
  this.agentInjector = new AgentInstructionInjector({
51210
52412
  enabled: this.config.agentTemplates?.enabled ?? true,
@@ -51381,7 +52583,7 @@ ${content}
51381
52583
  * Get debug information about current orchestration state
51382
52584
  */
51383
52585
  getDebugInfo() {
51384
- const budgetConfig = this.tokenBudgetManager.getConfig();
52586
+ const budgetConfig = this.injector.getBudgetManager().getConfig();
51385
52587
  const providers = this.injector.getProviders().map((p) => p.name);
51386
52588
  return {
51387
52589
  turnCount: this.turnCount,
@@ -51429,9 +52631,6 @@ ${content}
51429
52631
  sessionIntegration: updates.sessionIntegration ? { ...this.config.sessionIntegration, ...updates.sessionIntegration } : this.config.sessionIntegration,
51430
52632
  agentTemplates: updates.agentTemplates ? { ...this.config.agentTemplates, ...updates.agentTemplates } : this.config.agentTemplates
51431
52633
  };
51432
- if (updates.tokenBudget) {
51433
- this.tokenBudgetManager.updateConfig(this.config.tokenBudget);
51434
- }
51435
52634
  this.injector.updateConfig(updates);
51436
52635
  if (updates.todoIntegration && this.todoProvider) {
51437
52636
  const current = this.todoProvider.getConfig();