nexus-agents 2.28.0 → 2.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +11 -10
  2. package/dist/adaptive-memory-RST6DZYR.js +15 -0
  3. package/dist/chunk-2UR7YN6T.js +700 -0
  4. package/dist/chunk-2UR7YN6T.js.map +1 -0
  5. package/dist/chunk-2UUUKVNR.js +61 -0
  6. package/dist/chunk-2UUUKVNR.js.map +1 -0
  7. package/dist/chunk-3EVVQ32X.js +838 -0
  8. package/dist/chunk-3EVVQ32X.js.map +1 -0
  9. package/dist/{chunk-UGNLR4NZ.js → chunk-3GXDN4AX.js} +2 -2
  10. package/dist/{chunk-LKSTILEE.js → chunk-4AGPR6XZ.js} +194 -1461
  11. package/dist/chunk-4AGPR6XZ.js.map +1 -0
  12. package/dist/chunk-4HA5PAL7.js +246 -0
  13. package/dist/chunk-4HA5PAL7.js.map +1 -0
  14. package/dist/chunk-633WH2ML.js +127 -0
  15. package/dist/chunk-633WH2ML.js.map +1 -0
  16. package/dist/chunk-7SKAKA4I.js +90 -0
  17. package/dist/chunk-7SKAKA4I.js.map +1 -0
  18. package/dist/chunk-AP2FD37C.js +127 -0
  19. package/dist/chunk-AP2FD37C.js.map +1 -0
  20. package/dist/chunk-BC3M4VLP.js +359 -0
  21. package/dist/chunk-BC3M4VLP.js.map +1 -0
  22. package/dist/chunk-BQ4YXGGQ.js +127 -0
  23. package/dist/chunk-BQ4YXGGQ.js.map +1 -0
  24. package/dist/chunk-C2C5ONFR.js +195 -0
  25. package/dist/chunk-C2C5ONFR.js.map +1 -0
  26. package/dist/chunk-CGWRJ4EM.js +12661 -0
  27. package/dist/chunk-CGWRJ4EM.js.map +1 -0
  28. package/dist/chunk-CLYZ7FWP.js +30 -0
  29. package/dist/chunk-CLYZ7FWP.js.map +1 -0
  30. package/dist/chunk-ED6VQWNG.js +63 -0
  31. package/dist/chunk-ED6VQWNG.js.map +1 -0
  32. package/dist/{chunk-L2SHSW4T.js → chunk-ELIFTCYM.js} +2705 -2473
  33. package/dist/chunk-ELIFTCYM.js.map +1 -0
  34. package/dist/chunk-FYJVXQHX.js +944 -0
  35. package/dist/chunk-FYJVXQHX.js.map +1 -0
  36. package/dist/{chunk-QZEAD6AG.js → chunk-FZFZ77UJ.js} +19434 -38182
  37. package/dist/chunk-FZFZ77UJ.js.map +1 -0
  38. package/dist/chunk-IECE6DBS.js +1585 -0
  39. package/dist/chunk-IECE6DBS.js.map +1 -0
  40. package/dist/chunk-LLGUX44Z.js +356 -0
  41. package/dist/chunk-LLGUX44Z.js.map +1 -0
  42. package/dist/chunk-MRU6L7YJ.js +931 -0
  43. package/dist/chunk-MRU6L7YJ.js.map +1 -0
  44. package/dist/chunk-N5SY7V45.js +44 -0
  45. package/dist/chunk-N5SY7V45.js.map +1 -0
  46. package/dist/chunk-OOIPRRPX.js +3790 -0
  47. package/dist/chunk-OOIPRRPX.js.map +1 -0
  48. package/dist/chunk-SRECH7OQ.js +354 -0
  49. package/dist/chunk-SRECH7OQ.js.map +1 -0
  50. package/dist/{chunk-YSDUVCCZ.js → chunk-VKRMXD62.js} +6 -6
  51. package/dist/{chunk-E7EX2KQJ.js → chunk-XU3NADFE.js} +2 -2
  52. package/dist/chunk-YSTJEMQX.js +122 -0
  53. package/dist/chunk-YSTJEMQX.js.map +1 -0
  54. package/dist/cli-circuit-breaker-5FAODXVY.js +13 -0
  55. package/dist/cli.d.ts +2 -2
  56. package/dist/cli.js +1485 -683
  57. package/dist/cli.js.map +1 -1
  58. package/dist/codebase-search-CZUA37RU.js +9 -0
  59. package/dist/composite-router-FNW7ZWL7.js +17 -0
  60. package/dist/composite-router-FNW7ZWL7.js.map +1 -0
  61. package/dist/consensus-vote-757YULIP.js +25 -0
  62. package/dist/consensus-vote-757YULIP.js.map +1 -0
  63. package/dist/{dist-H5XNXVAV.js → dist-CV74KUT7.js} +1521 -972
  64. package/dist/dist-CV74KUT7.js.map +1 -0
  65. package/dist/{doctor-deep-BDE2PHVX.js → doctor-deep-LMCEAFU4.js} +4 -3
  66. package/dist/doctor-deep-LMCEAFU4.js.map +1 -0
  67. package/dist/expert-bridge-L2D4OXOR.js +10 -0
  68. package/dist/expert-bridge-L2D4OXOR.js.map +1 -0
  69. package/dist/expert-config-A5CHKUGI.js +22 -0
  70. package/dist/expert-config-A5CHKUGI.js.map +1 -0
  71. package/dist/factory-ELEDP2WD.js +17 -0
  72. package/dist/factory-ELEDP2WD.js.map +1 -0
  73. package/dist/factory-IDTIBX6B.js +14 -0
  74. package/dist/factory-IDTIBX6B.js.map +1 -0
  75. package/dist/index.d.ts +1814 -777
  76. package/dist/index.js +785 -355
  77. package/dist/index.js.map +1 -1
  78. package/dist/issue-triage-SJPKJLXH.js +15 -0
  79. package/dist/issue-triage-SJPKJLXH.js.map +1 -0
  80. package/dist/learning-persistence-WMWZJZ35.js +16 -0
  81. package/dist/learning-persistence-WMWZJZ35.js.map +1 -0
  82. package/dist/mcp-config-2OXIOMJ6.js +12 -0
  83. package/dist/mcp-config-2OXIOMJ6.js.map +1 -0
  84. package/dist/mobimem-5S3VLNSU.js +13 -0
  85. package/dist/mobimem-5S3VLNSU.js.map +1 -0
  86. package/dist/{model-capabilities-types-CSWO27YN.d.ts → model-capabilities-types-B57GZryc.d.ts} +1 -1
  87. package/dist/repo-analyze-HWMXSK5C.js +24 -0
  88. package/dist/repo-analyze-HWMXSK5C.js.map +1 -0
  89. package/dist/repo-security-plan-MUFDGWSQ.js +17 -0
  90. package/dist/repo-security-plan-MUFDGWSQ.js.map +1 -0
  91. package/dist/research-helpers-synthesize-OBQJ5BGX.js +10 -0
  92. package/dist/research-helpers-synthesize-OBQJ5BGX.js.map +1 -0
  93. package/dist/routing-memory-3QBQTS4A.js +13 -0
  94. package/dist/routing-memory-3QBQTS4A.js.map +1 -0
  95. package/dist/session-memory-VXWLOFRC.js +22 -0
  96. package/dist/session-memory-VXWLOFRC.js.map +1 -0
  97. package/dist/{setup-command-SS7LMN7Y.js → setup-command-E6MXO5RZ.js} +9 -5
  98. package/dist/setup-command-E6MXO5RZ.js.map +1 -0
  99. package/dist/setup-config-O5F3AZBL.js +10 -0
  100. package/dist/setup-config-O5F3AZBL.js.map +1 -0
  101. package/dist/shared-memory-AEO2HJLC.js +8 -0
  102. package/dist/shared-memory-AEO2HJLC.js.map +1 -0
  103. package/dist/symbol-extractor-UEBANFSN.js +10 -0
  104. package/dist/symbol-extractor-UEBANFSN.js.map +1 -0
  105. package/dist/weather-report-MUGSIOU5.js +15 -0
  106. package/dist/weather-report-MUGSIOU5.js.map +1 -0
  107. package/package.json +18 -19
  108. package/dist/chunk-L2SHSW4T.js.map +0 -1
  109. package/dist/chunk-LKSTILEE.js.map +0 -1
  110. package/dist/chunk-QZEAD6AG.js.map +0 -1
  111. package/dist/dist-H5XNXVAV.js.map +0 -1
  112. package/dist/setup-config-DSMOOLVW.js +0 -9
  113. /package/dist/{doctor-deep-BDE2PHVX.js.map → adaptive-memory-RST6DZYR.js.map} +0 -0
  114. /package/dist/{chunk-UGNLR4NZ.js.map → chunk-3GXDN4AX.js.map} +0 -0
  115. /package/dist/{chunk-YSDUVCCZ.js.map → chunk-VKRMXD62.js.map} +0 -0
  116. /package/dist/{chunk-E7EX2KQJ.js.map → chunk-XU3NADFE.js.map} +0 -0
  117. /package/dist/{setup-command-SS7LMN7Y.js.map → cli-circuit-breaker-5FAODXVY.js.map} +0 -0
  118. /package/dist/{setup-config-DSMOOLVW.js.map → codebase-search-CZUA37RU.js.map} +0 -0
@@ -1,5 +1,8 @@
1
1
  import {
2
- BACKOFF_CONFIG,
2
+ CliCircuitBreaker,
3
+ DEFAULT_CIRCUIT_BREAKER_CONFIG
4
+ } from "./chunk-LLGUX44Z.js";
5
+ import {
3
6
  CLI_SUBPROCESS_TIMEOUTS,
4
7
  CLI_TIMEOUTS,
5
8
  CLI_VERSION_REQUIREMENTS,
@@ -7,14 +10,8 @@ import {
7
10
  DEFAULT_CAPABILITIES,
8
11
  DEFAULT_MODEL_CAPABILITIES,
9
12
  DEFAULT_MODEL_PER_CLI,
10
- ErrorCode,
11
- LEARNING_DIR,
12
- NexusError,
13
- OUTCOMES_FILE,
14
- RULES_FILE,
15
13
  buildModelInfo,
16
14
  clampPercent,
17
- colors,
18
15
  createLogger,
19
16
  detectTaskCategory,
20
17
  err,
@@ -25,36 +22,17 @@ import {
25
22
  getOutcomeStore,
26
23
  getRandomProvider,
27
24
  getTimeProvider,
28
- isPersistenceEnabled,
29
- ok,
30
- symbols,
31
- writeLine
32
- } from "./chunk-L2SHSW4T.js";
25
+ isRateLimitText,
26
+ ok
27
+ } from "./chunk-ELIFTCYM.js";
33
28
 
34
- // src/version.ts
35
- var VERSION = true ? "2.28.0" : "dev";
29
+ // src/cli-adapters/subprocess-adapter.ts
30
+ import { spawn } from "child_process";
36
31
 
37
- // src/utils/async-utils.ts
38
- function sleep(ms) {
39
- return new Promise((resolve) => setTimeout(resolve, ms));
40
- }
41
- var delay = sleep;
42
- async function withTimeout(promise, timeoutMs, errorMessage) {
43
- let timeoutId;
44
- const timeoutPromise = new Promise((_, reject) => {
45
- timeoutId = setTimeout(() => {
46
- reject(new Error(errorMessage));
47
- }, timeoutMs);
48
- });
49
- try {
50
- const result = await Promise.race([promise, timeoutPromise]);
51
- if (timeoutId !== void 0) clearTimeout(timeoutId);
52
- return { ok: true, value: result };
53
- } catch (error) {
54
- if (timeoutId !== void 0) clearTimeout(timeoutId);
55
- return { ok: false, error: getErrorMessage(error) };
56
- }
57
- }
32
+ // src/cli-adapters/base-adapter.ts
33
+ import { exec } from "child_process";
34
+ import { promisify } from "util";
35
+ import semver from "semver";
58
36
 
59
37
  // src/cli-adapters/cli-timeout-helpers.ts
60
38
  function estimateTaskComplexity(taskDescription) {
@@ -75,7 +53,19 @@ function estimateTaskComplexity(taskDescription) {
75
53
  if (complexIndicators.some((indicator) => lower.includes(indicator))) {
76
54
  return "complex";
77
55
  }
78
- const simpleIndicators = ["single", "quick", "one function", "simple", "small", "brief", "short"];
56
+ const simpleIndicators = [
57
+ "single",
58
+ "quick",
59
+ "one function",
60
+ "simple",
61
+ "small",
62
+ "brief",
63
+ "short",
64
+ "run tests",
65
+ "test suite",
66
+ "exploration",
67
+ "explore"
68
+ ];
79
69
  if (simpleIndicators.some((indicator) => lower.includes(indicator))) {
80
70
  return "simple";
81
71
  }
@@ -123,11 +113,6 @@ function getTimeoutForTaskAuto(cli, taskDescription) {
123
113
  return getAdaptiveTimeout(cli, taskDescription);
124
114
  }
125
115
 
126
- // src/cli-adapters/base-adapter.ts
127
- import { exec } from "child_process";
128
- import { promisify } from "util";
129
- import semver from "semver";
130
-
131
116
  // src/cli-adapters/capacity-tracker.ts
132
117
  var DEFAULT_TOKEN_LIMITS = {
133
118
  claude: 1e5,
@@ -258,6 +243,98 @@ function createCapacityTracker(cli) {
258
243
  return new CapacityTracker(getDefaultConfig(cli));
259
244
  }
260
245
 
246
+ // src/utils/async-utils.ts
247
+ function sleep(ms) {
248
+ return new Promise((resolve) => setTimeout(resolve, ms));
249
+ }
250
+ var delay = sleep;
251
+ async function withTimeout(promise, timeoutMs, errorMessage) {
252
+ let timeoutId;
253
+ const timeoutPromise = new Promise((_, reject) => {
254
+ timeoutId = setTimeout(() => {
255
+ reject(new Error(errorMessage));
256
+ }, timeoutMs);
257
+ });
258
+ try {
259
+ const result = await Promise.race([promise, timeoutPromise]);
260
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
261
+ return { ok: true, value: result };
262
+ } catch (error) {
263
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
264
+ return { ok: false, error: getErrorMessage(error) };
265
+ }
266
+ }
267
+
268
+ // src/cli-adapters/cli-retry-loop.ts
269
+ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
270
+ "TIMEOUT",
271
+ "RATE_LIMITED",
272
+ "CONNECTION_ERROR"
273
+ ]);
274
+ function calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs) {
275
+ const exponentialDelay = baseDelayMs * Math.pow(2, attempt - 1);
276
+ const jitter = getRandomProvider().random() * 0.3 * exponentialDelay;
277
+ const delayMs = exponentialDelay + jitter;
278
+ return Math.min(delayMs, maxDelayMs);
279
+ }
280
+ function isRetryableError(code) {
281
+ return RETRYABLE_ERROR_CODES.has(code);
282
+ }
283
+ function categorizeError(error) {
284
+ switch (error.code) {
285
+ case "TIMEOUT":
286
+ return "timeout";
287
+ case "RATE_LIMITED":
288
+ return "rate_limit";
289
+ case "NOT_AUTHENTICATED":
290
+ return "authentication";
291
+ case "CONNECTION_ERROR":
292
+ return "connection";
293
+ default:
294
+ return "unknown";
295
+ }
296
+ }
297
+ async function executeCliRetryLoop(executeFn, config) {
298
+ const maxAttempts = config.allowRetry ? config.maxRetries + 1 : 1;
299
+ let lastError;
300
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
301
+ const result = await executeFn();
302
+ if (result.ok) {
303
+ return ok({ response: result.value, retryCount: attempt - 1 });
304
+ }
305
+ lastError = result.error;
306
+ if (config.circuitBreaker !== void 0 && config.circuitBreaker !== null) {
307
+ config.circuitBreaker.recordFailure(categorizeError(lastError));
308
+ }
309
+ if (!shouldRetry(lastError, attempt, maxAttempts, config.circuitBreaker)) {
310
+ return err(lastError);
311
+ }
312
+ const delayMs = calculateBackoffDelay(attempt, config.baseDelayMs, config.maxDelayMs);
313
+ config.logger.debug("Retrying CLI execution", {
314
+ cli: config.cli,
315
+ attempt,
316
+ nextAttempt: attempt + 1,
317
+ delayMs: Math.round(delayMs)
318
+ });
319
+ await delay(delayMs);
320
+ }
321
+ return err(
322
+ lastError ?? {
323
+ code: "UNKNOWN",
324
+ message: "Max retries exceeded",
325
+ cli: config.cli,
326
+ retryable: false
327
+ }
328
+ );
329
+ }
330
+ function shouldRetry(error, attempt, maxAttempts, circuitBreaker) {
331
+ if (attempt >= maxAttempts) return false;
332
+ if (!error.retryable) return false;
333
+ if (!isRetryableError(error.code)) return false;
334
+ if (circuitBreaker?.getState() === "open") return false;
335
+ return true;
336
+ }
337
+
261
338
  // src/cli-adapters/base-adapter.ts
262
339
  var execAsync = promisify(exec);
263
340
  var DEFAULT_CAPACITY_FALLBACK = 1e5;
@@ -276,8 +353,8 @@ var BaseCliAdapter = class {
276
353
  initialized = false;
277
354
  lastHealthCheck;
278
355
  cachedVersion;
279
- constructor(logger6) {
280
- this.logger = logger6 ?? createLogger({ component: "cli-adapter" });
356
+ constructor(logger5) {
357
+ this.logger = logger5 ?? createLogger({ component: "cli-adapter" });
281
358
  }
282
359
  /**
283
360
  * Initializes the capacity tracker.
@@ -323,47 +400,33 @@ var BaseCliAdapter = class {
323
400
  return getTimeoutForTaskAuto(this.name, task.content);
324
401
  }
325
402
  /**
326
- * Executes task with retry logic.
403
+ * Executes task with retry logic via shared retry loop.
327
404
  */
328
405
  async executeWithRetry(task, opts) {
329
- let lastError;
330
- const maxAttempts = opts.allowRetry ? opts.maxRetries + 1 : 1;
331
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
332
- const result = await this.executeTask(task, opts);
333
- if (result.ok) {
334
- this.recordUsage(result.value);
335
- this.logger.info("Task executed successfully", {
336
- cli: this.name,
337
- attempt,
338
- durationMs: result.value.durationMs,
339
- tokensUsed: result.value.usage?.totalTokens
340
- });
341
- return result;
342
- }
343
- lastError = result.error;
344
- if (this.isTerminalAttempt(result.error, attempt, maxAttempts)) {
345
- this.logger.warn("Task execution failed", {
346
- cli: this.name,
347
- attempt,
348
- error: result.error.message,
349
- retryable: result.error.retryable
350
- });
351
- return result;
352
- }
353
- this.logger.debug("Retrying task execution", {
406
+ const result = await executeCliRetryLoop(() => this.executeTask(task, opts), {
407
+ maxRetries: opts.maxRetries,
408
+ allowRetry: opts.allowRetry,
409
+ baseDelayMs: 1e3,
410
+ maxDelayMs: 16e3,
411
+ cli: this.name,
412
+ logger: this.logger
413
+ });
414
+ if (result.ok) {
415
+ this.recordUsage(result.value.response);
416
+ this.logger.info("Task executed successfully", {
354
417
  cli: this.name,
355
- attempt,
356
- nextAttempt: attempt + 1
418
+ attempt: result.value.retryCount + 1,
419
+ durationMs: result.value.response.durationMs,
420
+ tokensUsed: result.value.response.usage?.totalTokens
357
421
  });
358
- await this.delay(Math.pow(BACKOFF_CONFIG.exponentBase, attempt) * BACKOFF_CONFIG.baseDelayMs);
422
+ return { ok: true, value: result.value.response };
359
423
  }
360
- return err(lastError ?? this.createError("UNKNOWN", "Unknown error"));
361
- }
362
- /**
363
- * Checks if this attempt should be the final one.
364
- */
365
- isTerminalAttempt(error, attempt, maxAttempts) {
366
- return !error.retryable || attempt === maxAttempts;
424
+ this.logger.warn("Task execution failed", {
425
+ cli: this.name,
426
+ error: result.error.message,
427
+ retryable: result.error.retryable
428
+ });
429
+ return result;
367
430
  }
368
431
  /**
369
432
  * Performs a health check.
@@ -518,19 +581,39 @@ var BaseCliAdapter = class {
518
581
  }
519
582
  };
520
583
 
521
- // src/cli-adapters/subprocess-adapter.ts
522
- import { spawn } from "child_process";
523
- var RATE_LIMIT_PATTERNS = [
524
- "rate limit",
525
- "429",
526
- "too many requests",
527
- "quota exceeded",
528
- "usage limit"
584
+ // src/security/output-sanitizer.ts
585
+ var REDACTED_KEY_PLACEHOLDER = "[REDACTED_KEY]";
586
+ var KEY_PATTERNS = [
587
+ // Anthropic: sk-ant-api03-... (at least 20 chars after prefix)
588
+ /sk-ant-[A-Za-z0-9_-]{20,}/g,
589
+ // OpenAI project: sk-proj-... (at least 20 chars after prefix)
590
+ /sk-proj-[A-Za-z0-9_-]{20,}/g,
591
+ // Generic OpenAI: sk-... (at least 20 chars after prefix)
592
+ /sk-[A-Za-z0-9_-]{20,}/g,
593
+ // Google AI / Gemini: AIzaSy... (at least 30 chars total)
594
+ /AIzaSy[A-Za-z0-9_-]{24,}/g,
595
+ // GitHub PAT: ghp_...
596
+ /ghp_[A-Za-z0-9]{20,}/g,
597
+ // GitHub OAuth: gho_...
598
+ /gho_[A-Za-z0-9]{20,}/g,
599
+ // GitLab PAT: glpat-...
600
+ /glpat-[A-Za-z0-9_-]{10,}/g,
601
+ // npm token: npm_...
602
+ /npm_[A-Za-z0-9]{20,}/g,
603
+ // PyPI token: pypi-...
604
+ /pypi-[A-Za-z0-9_-]{20,}/g
529
605
  ];
530
- function isRateLimitOutput(stdout) {
531
- const lower = stdout.toLowerCase();
532
- return RATE_LIMIT_PATTERNS.some((pattern) => lower.includes(pattern));
606
+ function sanitizeOutput(text) {
607
+ if (text === "") return text;
608
+ let result = text;
609
+ for (const pattern of KEY_PATTERNS) {
610
+ pattern.lastIndex = 0;
611
+ result = result.replace(pattern, REDACTED_KEY_PLACEHOLDER);
612
+ }
613
+ return result;
533
614
  }
615
+
616
+ // src/cli-adapters/subprocess-adapter.ts
534
617
  var PLAINTEXT_FALLBACK_MIN_LENGTH = 30;
535
618
  function tryPlaintextFallback(stdout) {
536
619
  const trimmed = stdout.trim();
@@ -572,12 +655,11 @@ var STDERR_CONNECTION_PATTERNS = [
572
655
  "eaddrinuse",
573
656
  "address already in use"
574
657
  ];
575
- var STDERR_RATE_LIMIT_PATTERNS = ["rate limit", "quota exceeded", "429", "too many requests"];
576
658
  var STDERR_TIMEOUT_PATTERNS = ["timeout", "timed out", "etimedout"];
577
659
  function classifyStderrError(stderr) {
578
660
  const lower = stderr.toLowerCase();
579
661
  if (STDERR_CONNECTION_PATTERNS.some((p) => lower.includes(p))) return "CONNECTION_ERROR";
580
- if (STDERR_RATE_LIMIT_PATTERNS.some((p) => lower.includes(p))) return "RATE_LIMITED";
662
+ if (isRateLimitText(stderr)) return "RATE_LIMITED";
581
663
  if (STDERR_TIMEOUT_PATTERNS.some((p) => lower.includes(p))) return "TIMEOUT";
582
664
  return "EXECUTION_ERROR";
583
665
  }
@@ -731,6 +813,8 @@ var SubprocessCliAdapter = class extends BaseCliAdapter {
731
813
  }
732
814
  /** Classify a subprocess close event into a Result. */
733
815
  classifyCloseResult(code, state, startTime) {
816
+ state.stdout = sanitizeOutput(state.stdout);
817
+ state.stderr = sanitizeOutput(state.stderr);
734
818
  if (code !== 0 && state.stdout === "") {
735
819
  if (state.stderr !== "") {
736
820
  const msg2 = state.stderr;
@@ -757,7 +841,7 @@ var SubprocessCliAdapter = class extends BaseCliAdapter {
757
841
  }
758
842
  const text = this.parser.extractResponse(stdout);
759
843
  if (text === null) {
760
- if (isRateLimitOutput(stdout)) {
844
+ if (isRateLimitText(stdout)) {
761
845
  const snippet2 = stdout.slice(0, 500).trim();
762
846
  return err(this.createError("RATE_LIMITED", snippet2));
763
847
  }
@@ -800,7 +884,7 @@ var SubprocessCliAdapter = class extends BaseCliAdapter {
800
884
  if (error.message.includes("ENOENT")) {
801
885
  return err(this.createError("NOT_FOUND", `${this.name} CLI not found`, error));
802
886
  }
803
- return err(this.createError("EXECUTION_ERROR", error.message, error));
887
+ return err(this.createError("EXECUTION_ERROR", sanitizeOutput(error.message), error));
804
888
  }
805
889
  /**
806
890
  * Initializes the adapter and capacity tracker.
@@ -1305,345 +1389,6 @@ var ResilientGeminiParser = class {
1305
1389
  }
1306
1390
  };
1307
1391
 
1308
- // src/cli-adapters/circuit-breaker-types.ts
1309
- var CircuitErrorCode = {
1310
- CIRCUIT_OPEN: "CIRCUIT_OPEN",
1311
- CIRCUIT_HALF_OPEN_REJECTED: "CIRCUIT_HALF_OPEN_REJECTED",
1312
- EXECUTION_FAILED: "EXECUTION_FAILED"
1313
- };
1314
- var CircuitError = class extends NexusError {
1315
- circuitErrorCode;
1316
- cliName;
1317
- circuitState;
1318
- failureCategory;
1319
- constructor(message, options) {
1320
- const baseOptions = {
1321
- code: ErrorCode.INTERNAL_ERROR,
1322
- context: {
1323
- circuitErrorCode: options.circuitErrorCode,
1324
- cliName: options.cliName,
1325
- circuitState: options.circuitState
1326
- }
1327
- };
1328
- if (options.cause !== void 0) {
1329
- baseOptions.cause = options.cause;
1330
- }
1331
- if (options.failureCategory !== void 0) {
1332
- baseOptions.context["failureCategory"] = options.failureCategory;
1333
- }
1334
- super(message, baseOptions);
1335
- this.name = "CircuitError";
1336
- this.circuitErrorCode = options.circuitErrorCode;
1337
- this.cliName = options.cliName;
1338
- this.circuitState = options.circuitState;
1339
- if (options.failureCategory !== void 0) {
1340
- this.failureCategory = options.failureCategory;
1341
- }
1342
- }
1343
- };
1344
- var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
1345
- failureThreshold: 5,
1346
- resetTimeoutMs: 3e4,
1347
- halfOpenSuccessThreshold: 2,
1348
- countTimeoutsAsFailures: true,
1349
- countAuthFailuresAsFailures: false,
1350
- countRateLimitsAsFailures: true,
1351
- halfOpenMaxRequests: 3
1352
- };
1353
- var TIMEOUT_PATTERNS = ["timeout", "timed out"];
1354
- var AUTH_PATTERNS = ["auth", "unauthorized", "forbidden", "oauth"];
1355
- var RATE_LIMIT_PATTERNS2 = ["rate limit", "too many requests", "429"];
1356
- var CONNECTION_PATTERNS = [
1357
- "connection",
1358
- "econnrefused",
1359
- "enotfound",
1360
- "mcp",
1361
- "eaddrinuse",
1362
- "address already in use"
1363
- ];
1364
- var CRASH_PATTERNS = ["crash", "exited", "killed", "sigterm", "sigkill"];
1365
- function matchesPatterns(text, patterns) {
1366
- return patterns.some((pattern) => text.includes(pattern));
1367
- }
1368
- function categorizeError(error) {
1369
- if (!(error instanceof Error)) {
1370
- return "unknown";
1371
- }
1372
- const message = error.message.toLowerCase();
1373
- const name = error.name.toLowerCase();
1374
- const combined = `${message} ${name}`;
1375
- if (matchesPatterns(combined, TIMEOUT_PATTERNS)) return "timeout";
1376
- if (matchesPatterns(combined, AUTH_PATTERNS)) return "authentication";
1377
- if (matchesPatterns(combined, RATE_LIMIT_PATTERNS2)) return "rate_limit";
1378
- if (matchesPatterns(combined, CONNECTION_PATTERNS)) return "connection";
1379
- if (matchesPatterns(combined, CRASH_PATTERNS)) return "crash";
1380
- return "unknown";
1381
- }
1382
-
1383
- // src/cli-adapters/circuit-breaker.ts
1384
- var CliCircuitBreaker = class {
1385
- constructor(cliName, config = DEFAULT_CIRCUIT_BREAKER_CONFIG) {
1386
- this.cliName = cliName;
1387
- this.config = config;
1388
- this.lastStateChange = getTimeProvider().now();
1389
- }
1390
- state = "closed";
1391
- failureCount = 0;
1392
- successCount = 0;
1393
- lastFailureTime = null;
1394
- lastStateChange;
1395
- halfOpenRequests = 0;
1396
- listeners = /* @__PURE__ */ new Set();
1397
- /**
1398
- * Executes a function with circuit breaker protection.
1399
- */
1400
- async execute(fn) {
1401
- const canExecute = this.canExecute();
1402
- if (!canExecute.ok) {
1403
- return canExecute;
1404
- }
1405
- try {
1406
- const result = await fn();
1407
- this.onSuccess();
1408
- return ok(result);
1409
- } catch (error) {
1410
- const category = categorizeError(error);
1411
- if (this.shouldCountFailure(category)) {
1412
- this.onFailure(category);
1413
- }
1414
- return err(this.createExecutionError(error, category));
1415
- }
1416
- }
1417
- getState() {
1418
- this.checkStateTransition();
1419
- return this.state;
1420
- }
1421
- getSnapshot() {
1422
- this.checkStateTransition();
1423
- return {
1424
- state: this.state,
1425
- failureCount: this.failureCount,
1426
- successCount: this.successCount,
1427
- lastFailureTime: this.lastFailureTime,
1428
- lastStateChange: this.lastStateChange,
1429
- halfOpenRequests: this.halfOpenRequests,
1430
- config: this.config
1431
- };
1432
- }
1433
- reset() {
1434
- const previousState = this.state;
1435
- this.state = "closed";
1436
- this.failureCount = 0;
1437
- this.successCount = 0;
1438
- this.lastFailureTime = null;
1439
- this.halfOpenRequests = 0;
1440
- this.lastStateChange = getTimeProvider().now();
1441
- if (previousState !== "closed") {
1442
- this.emitStateChange(previousState, "closed", "Manual reset");
1443
- }
1444
- }
1445
- recordFailure(category) {
1446
- if (this.shouldCountFailure(category)) {
1447
- this.onFailure(category);
1448
- }
1449
- }
1450
- recordSuccess() {
1451
- this.onSuccess();
1452
- }
1453
- addStateChangeListener(listener) {
1454
- this.listeners.add(listener);
1455
- }
1456
- removeStateChangeListener(listener) {
1457
- this.listeners.delete(listener);
1458
- }
1459
- // -------------------------------------------------------------------------
1460
- // Private Methods
1461
- // -------------------------------------------------------------------------
1462
- canExecute() {
1463
- this.checkStateTransition();
1464
- if (this.state === "closed") {
1465
- return ok(true);
1466
- }
1467
- if (this.state === "open") {
1468
- return err(
1469
- new CircuitError(`Circuit is open for CLI: ${this.cliName}`, {
1470
- circuitErrorCode: CircuitErrorCode.CIRCUIT_OPEN,
1471
- cliName: this.cliName,
1472
- circuitState: this.state
1473
- })
1474
- );
1475
- }
1476
- if (this.halfOpenRequests >= this.config.halfOpenMaxRequests) {
1477
- return err(
1478
- new CircuitError(`Circuit half-open request limit reached for CLI: ${this.cliName}`, {
1479
- circuitErrorCode: CircuitErrorCode.CIRCUIT_HALF_OPEN_REJECTED,
1480
- cliName: this.cliName,
1481
- circuitState: this.state
1482
- })
1483
- );
1484
- }
1485
- this.halfOpenRequests++;
1486
- return ok(true);
1487
- }
1488
- checkStateTransition() {
1489
- if (this.state === "open" && this.lastFailureTime !== null) {
1490
- const elapsed = getTimeProvider().now() - this.lastFailureTime;
1491
- if (elapsed >= this.config.resetTimeoutMs) {
1492
- this.transitionTo("half-open", "Reset timeout elapsed");
1493
- }
1494
- }
1495
- }
1496
- onSuccess() {
1497
- if (this.state === "closed") {
1498
- this.failureCount = 0;
1499
- } else if (this.state === "half-open") {
1500
- this.successCount++;
1501
- if (this.successCount >= this.config.halfOpenSuccessThreshold) {
1502
- const reason = `${String(this.successCount)} consecutive successes in half-open state`;
1503
- this.transitionTo("closed", reason);
1504
- }
1505
- }
1506
- }
1507
- onFailure(category) {
1508
- this.failureCount++;
1509
- this.lastFailureTime = getTimeProvider().now();
1510
- if (this.state === "closed" && this.failureCount >= this.config.failureThreshold) {
1511
- const reason = `Failure threshold (${String(this.config.failureThreshold)}) exceeded`;
1512
- this.transitionTo("open", reason);
1513
- } else if (this.state === "half-open") {
1514
- this.transitionTo("open", `Failure during half-open recovery (category: ${category})`);
1515
- }
1516
- }
1517
- transitionTo(newState, reason) {
1518
- const previousState = this.state;
1519
- this.state = newState;
1520
- this.lastStateChange = getTimeProvider().now();
1521
- if (newState === "closed") {
1522
- this.failureCount = 0;
1523
- this.successCount = 0;
1524
- this.halfOpenRequests = 0;
1525
- } else if (newState === "half-open") {
1526
- this.successCount = 0;
1527
- this.halfOpenRequests = 0;
1528
- }
1529
- this.emitStateChange(previousState, newState, reason);
1530
- }
1531
- emitStateChange(previousState, newState, reason) {
1532
- const event = {
1533
- cliName: this.cliName,
1534
- previousState,
1535
- newState,
1536
- timestamp: this.lastStateChange,
1537
- failureCount: this.failureCount,
1538
- reason
1539
- };
1540
- for (const listener of this.listeners) {
1541
- try {
1542
- listener(event);
1543
- } catch {
1544
- }
1545
- }
1546
- }
1547
- shouldCountFailure(category) {
1548
- if (category === "timeout") return this.config.countTimeoutsAsFailures;
1549
- if (category === "authentication") return this.config.countAuthFailuresAsFailures;
1550
- if (category === "rate_limit") return this.config.countRateLimitsAsFailures;
1551
- return true;
1552
- }
1553
- createExecutionError(error, category) {
1554
- const message = getErrorMessage(error);
1555
- const cause = error instanceof Error ? error : new Error(String(error));
1556
- return new CircuitError(`CLI execution failed: ${message}`, {
1557
- circuitErrorCode: CircuitErrorCode.EXECUTION_FAILED,
1558
- cliName: this.cliName,
1559
- circuitState: this.state,
1560
- failureCategory: category,
1561
- cause
1562
- });
1563
- }
1564
- };
1565
- var CircuitBreakerRegistry = class {
1566
- constructor(defaultConfig = {}) {
1567
- this.defaultConfig = defaultConfig;
1568
- }
1569
- breakers = /* @__PURE__ */ new Map();
1570
- globalListeners = /* @__PURE__ */ new Set();
1571
- getBreaker(cliName, config) {
1572
- let breaker = this.breakers.get(cliName);
1573
- if (!breaker) {
1574
- const mergedConfig = {
1575
- ...DEFAULT_CIRCUIT_BREAKER_CONFIG,
1576
- ...this.defaultConfig,
1577
- ...config
1578
- };
1579
- breaker = new CliCircuitBreaker(cliName, mergedConfig);
1580
- for (const listener of this.globalListeners) {
1581
- breaker.addStateChangeListener(listener);
1582
- }
1583
- this.breakers.set(cliName, breaker);
1584
- }
1585
- return breaker;
1586
- }
1587
- isOpen(cliName) {
1588
- return this.breakers.get(cliName)?.getState() === "open";
1589
- }
1590
- getAllSnapshots() {
1591
- const snapshots = /* @__PURE__ */ new Map();
1592
- for (const [name, breaker] of this.breakers) {
1593
- snapshots.set(name, breaker.getSnapshot());
1594
- }
1595
- return snapshots;
1596
- }
1597
- resetAll() {
1598
- for (const breaker of this.breakers.values()) {
1599
- breaker.reset();
1600
- }
1601
- }
1602
- reset(cliName) {
1603
- this.breakers.get(cliName)?.reset();
1604
- }
1605
- addGlobalStateChangeListener(listener) {
1606
- this.globalListeners.add(listener);
1607
- for (const breaker of this.breakers.values()) {
1608
- breaker.addStateChangeListener(listener);
1609
- }
1610
- }
1611
- removeGlobalStateChangeListener(listener) {
1612
- this.globalListeners.delete(listener);
1613
- for (const breaker of this.breakers.values()) {
1614
- breaker.removeStateChangeListener(listener);
1615
- }
1616
- }
1617
- getHealthyClis() {
1618
- const healthy = [];
1619
- for (const [name, breaker] of this.breakers) {
1620
- if (breaker.getState() === "closed") {
1621
- healthy.push(name);
1622
- }
1623
- }
1624
- return healthy;
1625
- }
1626
- getUnhealthyClis() {
1627
- const unhealthy = [];
1628
- for (const [name, breaker] of this.breakers) {
1629
- const state = breaker.getState();
1630
- if (state === "open" || state === "half-open") {
1631
- unhealthy.push(name);
1632
- }
1633
- }
1634
- return unhealthy;
1635
- }
1636
- };
1637
- function mapCliErrorToCategory(errorCode) {
1638
- const mapping = {
1639
- TIMEOUT: "timeout",
1640
- NOT_AUTHENTICATED: "authentication",
1641
- RATE_LIMITED: "rate_limit",
1642
- CONNECTION_ERROR: "connection"
1643
- };
1644
- return mapping[errorCode] ?? "unknown";
1645
- }
1646
-
1647
1392
  // src/cli-adapters/adapters/gemini-adapter-helpers.ts
1648
1393
  var GEMINI_LEGACY_DEFAULTS = {
1649
1394
  displayNames: {},
@@ -1654,34 +1399,6 @@ var GEMINI_LEGACY_DEFAULTS = {
1654
1399
  inputCost: 0.15,
1655
1400
  outputCost: 0.6
1656
1401
  };
1657
- var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
1658
- "TIMEOUT",
1659
- "RATE_LIMITED",
1660
- "CONNECTION_ERROR"
1661
- ]);
1662
- function calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs) {
1663
- const exponentialDelay = baseDelayMs * Math.pow(2, attempt - 1);
1664
- const jitter = getRandomProvider().random() * 0.3 * exponentialDelay;
1665
- const delay2 = exponentialDelay + jitter;
1666
- return Math.min(delay2, maxDelayMs);
1667
- }
1668
- function isRetryableError(code) {
1669
- return RETRYABLE_ERROR_CODES.has(code);
1670
- }
1671
- function categorizeError2(error) {
1672
- switch (error.code) {
1673
- case "TIMEOUT":
1674
- return "timeout";
1675
- case "RATE_LIMITED":
1676
- return "rate_limit";
1677
- case "NOT_AUTHENTICATED":
1678
- return "authentication";
1679
- case "CONNECTION_ERROR":
1680
- return "connection";
1681
- default:
1682
- return "unknown";
1683
- }
1684
- }
1685
1402
  function createCircuitOpenError(cli) {
1686
1403
  return {
1687
1404
  code: "EXECUTION_ERROR",
@@ -1833,69 +1550,14 @@ var GeminiCliAdapter = class extends SubprocessCliAdapter {
1833
1550
  return err(result.error);
1834
1551
  }
1835
1552
  async executeWithRetryTracking(task, options) {
1836
- const maxAttempts = options.allowRetry ? options.maxRetries + 1 : 1;
1837
- let retryContext = {
1838
- attempt: 0,
1839
- maxAttempts,
1840
- totalDelayMs: 0
1841
- };
1842
- while (retryContext.attempt < maxAttempts) {
1843
- retryContext = { ...retryContext, attempt: retryContext.attempt + 1 };
1844
- this.logRetryAttempt(retryContext, task);
1845
- const result = await this.executeTask(task, options);
1846
- if (result.ok) {
1847
- return ok({ response: result.value, retryCount: retryContext.attempt - 1 });
1848
- }
1849
- const error = result.error;
1850
- retryContext = { ...retryContext, lastError: error };
1851
- if (this.circuitBreaker !== null) {
1852
- this.circuitBreaker.recordFailure(categorizeError2(error));
1853
- }
1854
- if (!this.shouldRetry(error, retryContext)) {
1855
- return err(error);
1856
- }
1857
- const delayMs = calculateBackoffDelay(
1858
- retryContext.attempt,
1859
- this.baseDelayMs,
1860
- this.maxDelayMs
1861
- );
1862
- retryContext = { ...retryContext, totalDelayMs: retryContext.totalDelayMs + delayMs };
1863
- this.logRetryDelay(retryContext, delayMs);
1864
- await delay(delayMs);
1865
- }
1866
- return err(retryContext.lastError ?? this.createError("UNKNOWN", "Max retries exceeded"));
1867
- }
1868
- shouldRetry(error, context) {
1869
- if (context.attempt >= context.maxAttempts) {
1870
- return false;
1871
- }
1872
- if (!error.retryable) {
1873
- return false;
1874
- }
1875
- if (this.circuitBreaker?.getState() === "open") {
1876
- return false;
1877
- }
1878
- return isRetryableError(error.code);
1879
- }
1880
- logRetryAttempt(context, task) {
1881
- if (context.attempt === 1) {
1882
- this.adapterLogger.debug("Executing Gemini task", {
1883
- contentLength: task.content.length,
1884
- model: task.model ?? this.model
1885
- });
1886
- } else {
1887
- this.adapterLogger.info("Retrying Gemini task", {
1888
- attempt: context.attempt,
1889
- maxAttempts: context.maxAttempts,
1890
- lastError: context.lastError?.code
1891
- });
1892
- }
1893
- }
1894
- logRetryDelay(context, delayMs) {
1895
- this.adapterLogger.debug("Backoff delay before retry", {
1896
- attempt: context.attempt,
1897
- delayMs: Math.round(delayMs),
1898
- totalDelayMs: Math.round(context.totalDelayMs)
1553
+ return executeCliRetryLoop(() => this.executeTask(task, options), {
1554
+ maxRetries: options.maxRetries,
1555
+ allowRetry: options.allowRetry,
1556
+ baseDelayMs: this.baseDelayMs,
1557
+ maxDelayMs: this.maxDelayMs,
1558
+ circuitBreaker: this.circuitBreaker,
1559
+ cli: this.name,
1560
+ logger: this.adapterLogger
1899
1561
  });
1900
1562
  }
1901
1563
  };
@@ -2933,9 +2595,9 @@ function createCodexAdapter(transport, options) {
2933
2595
  }
2934
2596
  return new CodexMcpAdapter(options);
2935
2597
  }
2936
- function createAllAdapters(logger6, codexTransport = "mcp") {
2598
+ function createAllAdapters(logger5, codexTransport = "mcp") {
2937
2599
  const adapters = /* @__PURE__ */ new Map();
2938
- const options = logger6 !== void 0 ? { logger: logger6 } : void 0;
2600
+ const options = logger5 !== void 0 ? { logger: logger5 } : void 0;
2939
2601
  adapters.set("claude", new ClaudeCliAdapter(options));
2940
2602
  adapters.set("gemini", new GeminiCliAdapter(options));
2941
2603
  adapters.set("codex", createCodexAdapter(codexTransport, options ?? {}));
@@ -2979,920 +2641,7 @@ async function getAvailableClis(cache) {
2979
2641
  ).map((r) => r.value.cli);
2980
2642
  }
2981
2643
 
2982
- // src/cli/setup-data-dir.ts
2983
- import { mkdirSync, existsSync as existsSync2 } from "fs";
2984
- import { homedir as homedir2 } from "os";
2985
- import { join as join2 } from "path";
2986
-
2987
- // src/cli/doctor.ts
2988
- import { existsSync, readFileSync, accessSync, constants as fsConstants } from "fs";
2989
- import { homedir } from "os";
2990
- import { join } from "path";
2991
-
2992
- // src/mcp/server.ts
2993
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2994
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2995
-
2996
- // src/mcp/task-store.ts
2997
- import { InMemoryTaskStore } from "@modelcontextprotocol/sdk/experimental/tasks";
2998
- var MAX_TASK_TTL_MS = 6e5;
2999
- var DEFAULT_TASK_TTL_MS = 3e5;
3000
- var MAX_TASK_CAPACITY = 50;
3001
- var CLEANUP_INTERVAL_MS = 6e4;
3002
- var logger5 = createLogger({ component: "task-store" });
3003
- var singletonStore;
3004
- var cleanupTimer;
3005
- function getTaskStore() {
3006
- if (singletonStore === void 0) {
3007
- singletonStore = new InMemoryTaskStore();
3008
- logger5.info("Task store created", { maxCapacity: MAX_TASK_CAPACITY });
3009
- cleanupTimer = setInterval(() => {
3010
- evictExcessTasks();
3011
- }, CLEANUP_INTERVAL_MS);
3012
- if (typeof cleanupTimer === "object" && "unref" in cleanupTimer) {
3013
- cleanupTimer.unref();
3014
- }
3015
- }
3016
- return singletonStore;
3017
- }
3018
- function clampTaskTtl(requestedTtl) {
3019
- if (requestedTtl === void 0 || requestedTtl === null) {
3020
- return DEFAULT_TASK_TTL_MS;
3021
- }
3022
- if (requestedTtl > MAX_TASK_TTL_MS) {
3023
- logger5.warn("Task TTL clamped to maximum", {
3024
- requested: requestedTtl,
3025
- max: MAX_TASK_TTL_MS
3026
- });
3027
- return MAX_TASK_TTL_MS;
3028
- }
3029
- return requestedTtl;
3030
- }
3031
- function evictExcessTasks() {
3032
- if (singletonStore === void 0) return;
3033
- const allTasks = singletonStore.getAllTasks();
3034
- if (allTasks.length <= MAX_TASK_CAPACITY) return;
3035
- const sorted = [...allTasks].sort(
3036
- (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
3037
- );
3038
- const evictCount = sorted.length - MAX_TASK_CAPACITY;
3039
- for (let i = 0; i < evictCount; i++) {
3040
- const task = sorted[i];
3041
- if (task === void 0) continue;
3042
- singletonStore.updateTaskStatus(task.taskId, "cancelled", "Evicted: capacity exceeded").catch((err2) => {
3043
- logger5.debug("Failed to evict task", {
3044
- taskId: task.taskId,
3045
- error: String(err2)
3046
- });
3047
- });
3048
- }
3049
- logger5.info("Evicted excess tasks", { evicted: evictCount, total: sorted.length });
3050
- }
3051
-
3052
- // src/mcp/server.ts
3053
- var DEFAULT_SERVER_NAME = "nexus-agents";
3054
- function createServerError(code, message, error) {
3055
- const serverError = { code, message };
3056
- if (error instanceof Error) {
3057
- serverError.cause = error;
3058
- }
3059
- return serverError;
3060
- }
3061
- function createServer(config) {
3062
- const serverName = config?.name ?? DEFAULT_SERVER_NAME;
3063
- const serverVersion = config?.version ?? VERSION;
3064
- const logger6 = config?.logger ?? createLogger({ component: "mcp-server" });
3065
- try {
3066
- initDataDirectories();
3067
- logger6.info("Creating MCP server", {
3068
- name: serverName,
3069
- version: serverVersion
3070
- });
3071
- const server = new McpServer(
3072
- {
3073
- name: serverName,
3074
- version: serverVersion
3075
- },
3076
- {
3077
- capabilities: {
3078
- logging: {},
3079
- prompts: {},
3080
- resources: {},
3081
- tasks: {}
3082
- },
3083
- taskStore: getTaskStore()
3084
- }
3085
- );
3086
- logger6.debug("MCP server created successfully");
3087
- return ok({ server, logger: logger6 });
3088
- } catch (error) {
3089
- const errorMessage = getErrorMessage(error);
3090
- logger6.error("Failed to create MCP server", error instanceof Error ? error : void 0);
3091
- return err(
3092
- createServerError(
3093
- "SERVER_CREATION_FAILED",
3094
- `Failed to create MCP server: ${errorMessage}`,
3095
- error
3096
- )
3097
- );
3098
- }
3099
- }
3100
- async function connectTransport(server, transport, logger6) {
3101
- const log = logger6 ?? createLogger({ component: "mcp-server" });
3102
- try {
3103
- log.info("Connecting server to transport");
3104
- await server.connect(transport);
3105
- log.debug("Server connected to transport successfully");
3106
- return ok(void 0);
3107
- } catch (error) {
3108
- const errorMessage = getErrorMessage(error);
3109
- log.error("Failed to connect to transport", error instanceof Error ? error : void 0);
3110
- return err(
3111
- createServerError(
3112
- "SERVER_START_FAILED",
3113
- `Failed to connect to transport: ${errorMessage}`,
3114
- error
3115
- )
3116
- );
3117
- }
3118
- }
3119
- async function startStdioServer(config) {
3120
- const serverResult = createServer(config);
3121
- if (!serverResult.ok) {
3122
- return serverResult;
3123
- }
3124
- const { server, logger: logger6 } = serverResult.value;
3125
- try {
3126
- logger6.info("Starting stdio transport");
3127
- const transport = new StdioServerTransport();
3128
- const connectResult = await connectTransport(server, transport, logger6);
3129
- if (!connectResult.ok) {
3130
- return connectResult;
3131
- }
3132
- logger6.info("MCP server running with stdio transport");
3133
- return ok({ server, logger: logger6 });
3134
- } catch (error) {
3135
- const errorMessage = getErrorMessage(error);
3136
- logger6.error("Failed to start stdio server", error instanceof Error ? error : void 0);
3137
- return err(
3138
- createServerError(
3139
- "SERVER_START_FAILED",
3140
- `Failed to start stdio server: ${errorMessage}`,
3141
- error
3142
- )
3143
- );
3144
- }
3145
- }
3146
- async function closeServer(server, logger6) {
3147
- const log = logger6 ?? createLogger({ component: "mcp-server" });
3148
- try {
3149
- log.info("Closing MCP server");
3150
- await server.close();
3151
- log.info("MCP server closed successfully");
3152
- return ok(void 0);
3153
- } catch (error) {
3154
- const errorMessage = getErrorMessage(error);
3155
- log.error("Failed to close server", error instanceof Error ? error : void 0);
3156
- return err(
3157
- createServerError("SERVER_STOP_FAILED", `Failed to close server: ${errorMessage}`, error)
3158
- );
3159
- }
3160
- }
3161
-
3162
- // src/utils/text-utils.ts
3163
- var STOPWORDS = /* @__PURE__ */ new Set([
3164
- "a",
3165
- "an",
3166
- "and",
3167
- "are",
3168
- "as",
3169
- "at",
3170
- "be",
3171
- "by",
3172
- "for",
3173
- "from",
3174
- "has",
3175
- "he",
3176
- "in",
3177
- "is",
3178
- "it",
3179
- "its",
3180
- "of",
3181
- "on",
3182
- "or",
3183
- "that",
3184
- "the",
3185
- "to",
3186
- "was",
3187
- "were",
3188
- "will",
3189
- "with",
3190
- "this",
3191
- "but",
3192
- "they",
3193
- "have",
3194
- "had",
3195
- "what",
3196
- "when",
3197
- "where",
3198
- "who",
3199
- "which",
3200
- "why",
3201
- "how",
3202
- "all",
3203
- "each",
3204
- "every",
3205
- "both",
3206
- "few",
3207
- "more",
3208
- "most",
3209
- "other",
3210
- "some",
3211
- "such",
3212
- "no",
3213
- "nor",
3214
- "not",
3215
- "only",
3216
- "own",
3217
- "same",
3218
- "so",
3219
- "than",
3220
- "too",
3221
- "very",
3222
- "can",
3223
- "just",
3224
- "should",
3225
- "now",
3226
- "i",
3227
- "you",
3228
- "we",
3229
- "me",
3230
- "my",
3231
- "your",
3232
- "our",
3233
- "their",
3234
- "him",
3235
- "her",
3236
- "them",
3237
- "his",
3238
- "hers",
3239
- "able"
3240
- ]);
3241
- function tokenize(text, minLength = 2) {
3242
- return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length >= minLength);
3243
- }
3244
- function tokenizeFiltered(text, minLength = 2) {
3245
- return tokenize(text, minLength).filter((t) => !STOPWORDS.has(t));
3246
- }
3247
- function stringifyValue(value) {
3248
- if (typeof value === "string") return value;
3249
- if (value === null || value === void 0) return "";
3250
- return JSON.stringify(value);
3251
- }
3252
- function capitalize(str) {
3253
- if (str.length === 0) return str;
3254
- return str.charAt(0).toUpperCase() + str.slice(1);
3255
- }
3256
- function capitalizeWords(str) {
3257
- return str.split(" ").map((word) => capitalize(word)).join(" ");
3258
- }
3259
- function capitalizeKebab(str) {
3260
- return str.split("-").map((word) => capitalize(word)).join(" ");
3261
- }
3262
- function truncateWithInfo(text, maxLength) {
3263
- if (text.length <= maxLength) return text;
3264
- const truncatedBytes = text.length - maxLength;
3265
- return `${text.slice(0, maxLength)}
3266
- ... [truncated ${String(truncatedBytes)} bytes]`;
3267
- }
3268
- function truncateSentence(text, maxLength = 150) {
3269
- const sentenceEnd = text.search(/[.!?](?:\s|$)/);
3270
- if (sentenceEnd !== -1 && sentenceEnd < maxLength) {
3271
- return text.slice(0, sentenceEnd + 1).trim();
3272
- }
3273
- if (text.length <= maxLength) return text;
3274
- return text.slice(0, maxLength).trim() + "...";
3275
- }
3276
-
3277
- // src/cli/doctor-formatting.ts
3278
- var REQUIRED_NODE_MAJOR = 22;
3279
- function formatStatus(healthy, warn = false) {
3280
- if (healthy) return `${colors.green}${symbols.check}${colors.reset}`;
3281
- if (warn) return `${colors.yellow}${symbols.warn}${colors.reset}`;
3282
- return `${colors.red}${symbols.cross}${colors.reset}`;
3283
- }
3284
- function formatVersionStatus(status) {
3285
- switch (status) {
3286
- case "supported":
3287
- return `${colors.green}supported${colors.reset}`;
3288
- case "outdated":
3289
- return `${colors.yellow}outdated${colors.reset}`;
3290
- case "unsupported":
3291
- case "breaking":
3292
- return `${colors.red}${status}${colors.reset}`;
3293
- default:
3294
- return status;
3295
- }
3296
- }
3297
- function formatCapacity(capacity) {
3298
- if (capacity === void 0) return "Unknown";
3299
- const remaining = 100 - capacity.utilizationPercent;
3300
- const remainingStr = String(remaining);
3301
- if (remaining > 80) return `${colors.green}${remainingStr}% remaining${colors.reset}`;
3302
- if (remaining > 20) return `${colors.yellow}${remainingStr}% remaining${colors.reset}`;
3303
- return `${colors.red}${remainingStr}% remaining${colors.reset}`;
3304
- }
3305
- function printInstalledCliDetails(cli) {
3306
- writeLine(` Version: ${cli.version} (${formatVersionStatus(cli.versionStatus)})`);
3307
- const authText = cli.authenticated ? `${colors.green}${cli.authMethod ?? "Authenticated"}${colors.reset}` : `${colors.red}Not authenticated${colors.reset}`;
3308
- writeLine(` Auth: ${authText}`);
3309
- if (cli.capacity !== void 0) {
3310
- writeLine(` Capacity: ${formatCapacity(cli.capacity)}`);
3311
- }
3312
- }
3313
- function printCliResult(cli) {
3314
- const status = cli.installed && cli.authenticated;
3315
- const warn = cli.installed && (!cli.authenticated || cli.versionStatus === "outdated");
3316
- writeLine(
3317
- `${formatStatus(status, warn)} ${colors.bold}${capitalize(cli.name)} CLI${colors.reset}`
3318
- );
3319
- if (cli.installed) {
3320
- printInstalledCliDetails(cli);
3321
- } else {
3322
- const errorText = cli.error ?? "Not installed";
3323
- writeLine(` ${colors.red}Error: ${errorText}${colors.reset}`);
3324
- }
3325
- if (cli.fix !== void 0 && cli.fix !== "") {
3326
- writeLine(` ${colors.dim}Fix: ${cli.fix}${colors.reset}`);
3327
- }
3328
- writeLine("");
3329
- }
3330
- function printCapabilities(clis) {
3331
- const installedClis = clis.filter((c) => c.installed);
3332
- if (installedClis.length === 0) {
3333
- writeLine(`${formatStatus(false)} No CLIs installed`);
3334
- return;
3335
- }
3336
- const caps = DEFAULT_CAPABILITIES;
3337
- const bestReasoning = installedClis.reduce(
3338
- (best, c) => caps[c.name].reasoning > caps[best.name].reasoning ? c : best
3339
- );
3340
- const bestContext = installedClis.reduce(
3341
- (best, c) => caps[c.name].contextWindow > caps[best.name].contextWindow ? c : best
3342
- );
3343
- const bestSpeed = installedClis.reduce(
3344
- (best, c) => caps[c.name].speed > caps[best.name].speed ? c : best
3345
- );
3346
- const contextTokensK = (caps[bestContext.name].contextWindow / 1e3).toFixed(0);
3347
- writeLine(
3348
- `${formatStatus(true)} Complex reasoning: ${colors.bold}${capitalize(bestReasoning.name)}${colors.reset}`
3349
- );
3350
- writeLine(
3351
- `${formatStatus(true)} Large context: ${colors.bold}${capitalize(bestContext.name)}${colors.reset} (${contextTokensK}K tokens)`
3352
- );
3353
- writeLine(
3354
- `${formatStatus(true)} Fast execution: ${colors.bold}${capitalize(bestSpeed.name)}${colors.reset}`
3355
- );
3356
- }
3357
- function printNodeVersionCheck(check) {
3358
- const versionText = check.supported ? `${colors.green}${check.version}${colors.reset}` : `${colors.yellow}${check.version}${colors.reset}`;
3359
- writeLine(`${formatStatus(check.supported, !check.supported)} Node.js version: ${versionText}`);
3360
- if (!check.supported) {
3361
- writeLine(
3362
- ` ${colors.dim}Warning: Node.js ${String(REQUIRED_NODE_MAJOR)}.x LTS required${colors.reset}`
3363
- );
3364
- }
3365
- }
3366
- function printApiKeysCheck(keys) {
3367
- const configuredCount = keys.filter((k) => k.configured).length;
3368
- const configuredNames = keys.filter((k) => k.configured).map((k) => k.name);
3369
- const hasAny = configuredCount > 0;
3370
- writeLine(
3371
- `${formatStatus(hasAny, !hasAny)} API keys configured: ${String(configuredCount)} of ${String(keys.length)}`
3372
- );
3373
- if (hasAny) {
3374
- writeLine(` ${colors.dim}Keys: ${configuredNames.join(", ")}${colors.reset}`);
3375
- } else {
3376
- writeLine(
3377
- ` ${colors.dim}Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_AI_API_KEY${colors.reset}`
3378
- );
3379
- }
3380
- }
3381
- function printConfigFileCheck(check) {
3382
- if (check.found && check.path !== null) {
3383
- writeLine(`${formatStatus(true)} Configuration loaded: ${check.path}`);
3384
- } else {
3385
- writeLine(`${formatStatus(false, true)} Configuration file: Not found`);
3386
- writeLine(` ${colors.dim}Run: nexus-agents config init${colors.reset}`);
3387
- }
3388
- }
3389
- function printRegistryAdvisory(advisory) {
3390
- const allAvailable = advisory.unavailableModels === 0;
3391
- const countText = `${String(advisory.availableModels)} of ${String(advisory.totalModels)}`;
3392
- writeLine(`${formatStatus(allAvailable, !allAvailable)} Models available: ${countText}`);
3393
- if (advisory.unavailableModels > 0) {
3394
- const missing = advisory.models.filter((m) => !m.available);
3395
- for (const m of missing) {
3396
- writeLine(` ${colors.dim}${m.displayName} \u2014 ${m.reason}${colors.reset}`);
3397
- }
3398
- }
3399
- const ageText = `${String(advisory.registryAgeDays)} days old`;
3400
- if (advisory.registryStale) {
3401
- writeLine(
3402
- `${colors.yellow}${symbols.warn}${colors.reset} Model registry is ${ageText} \u2014 may have stale model data`
3403
- );
3404
- writeLine(` ${colors.dim}Run: npx tsx scripts/probe-models.ts${colors.reset}`);
3405
- } else {
3406
- writeLine(`${formatStatus(true)} Model registry: ${ageText}`);
3407
- }
3408
- }
3409
- function printLearningPersistence(check) {
3410
- if (!check.enabled) {
3411
- writeLine(`${formatStatus(true)} Learning persistence: ${colors.dim}Disabled${colors.reset}`);
3412
- writeLine(` ${colors.dim}Set NEXUS_PERSIST_LEARNING=true to enable${colors.reset}`);
3413
- return;
3414
- }
3415
- const healthy = check.dirExists && check.dirWritable && check.error === null;
3416
- writeLine(`${formatStatus(healthy, !healthy)} Learning persistence: Enabled`);
3417
- if (check.error !== null) {
3418
- writeLine(` ${colors.red}Error: ${check.error}${colors.reset}`);
3419
- return;
3420
- }
3421
- const dirStatus = check.dirExists ? check.dirWritable ? `${colors.green}writable${colors.reset}` : `${colors.red}not writable${colors.reset}` : `${colors.yellow}not created yet${colors.reset}`;
3422
- writeLine(` Data directory: ${dirStatus}`);
3423
- writeLine(` Outcomes: ${String(check.outcomeCount)} recorded`);
3424
- writeLine(` Distilled rules: ${String(check.ruleCount)} active`);
3425
- if (check.rulesLastSaved !== null) {
3426
- writeLine(` Rules last saved: ${check.rulesLastSaved}`);
3427
- }
3428
- }
3429
- function printSqliteCheck(check) {
3430
- if (check.available) {
3431
- writeLine(
3432
- `${formatStatus(true)} SQLite (better-sqlite3): ${colors.green}Available${colors.reset}`
3433
- );
3434
- } else {
3435
- writeLine(
3436
- `${formatStatus(false, true)} SQLite (better-sqlite3): ${colors.yellow}Not available${colors.reset}`
3437
- );
3438
- writeLine(
3439
- ` ${colors.dim}Memory backends (agentic, adaptive, typed) require it${colors.reset}`
3440
- );
3441
- writeLine(` ${colors.dim}Fix: npm install -g better-sqlite3${colors.reset}`);
3442
- }
3443
- }
3444
- function printDataDirectory(check) {
3445
- if (check.rootExists) {
3446
- const existCount = check.subdirectories.filter((d) => d.exists).length;
3447
- const totalCount = check.subdirectories.length;
3448
- const allExist = existCount === totalCount;
3449
- const allWritable = check.subdirectories.every((d) => !d.exists || d.writable);
3450
- const healthy = allExist && allWritable;
3451
- writeLine(
3452
- `${formatStatus(healthy, !healthy)} Data directory: ${check.rootPath} (${String(existCount)}/${String(totalCount)} subdirs)`
3453
- );
3454
- if (!allExist) {
3455
- const missing = check.subdirectories.filter((d) => !d.exists);
3456
- for (const dir of missing) {
3457
- writeLine(` ${colors.dim}Missing: ${dir.name}/${colors.reset}`);
3458
- }
3459
- writeLine(` ${colors.dim}Fix: nexus-agents setup${colors.reset}`);
3460
- }
3461
- if (!allWritable) {
3462
- const readonly_ = check.subdirectories.filter((d) => d.exists && !d.writable);
3463
- for (const dir of readonly_) {
3464
- writeLine(` ${colors.yellow}Not writable: ${dir.name}/${colors.reset}`);
3465
- }
3466
- }
3467
- } else {
3468
- writeLine(
3469
- `${formatStatus(false, true)} Data directory: ${colors.yellow}Not created${colors.reset}`
3470
- );
3471
- writeLine(` ${colors.dim}Run: nexus-agents setup${colors.reset}`);
3472
- }
3473
- }
3474
- function printDoctorSummary(result) {
3475
- const unhealthyCount = result.clis.filter((c) => !c.installed || !c.authenticated).length;
3476
- const nodeIssue = result.nodeVersion.supported ? 0 : 1;
3477
- const totalIssues = unhealthyCount + nodeIssue + (result.mcpServerReady ? 0 : 1);
3478
- const summary = result.allHealthy ? `${colors.green}${colors.bold}Status: Ready${colors.reset}` : `${colors.yellow}${colors.bold}Summary: ${String(totalIssues)} issue(s) found${colors.reset}`;
3479
- writeLine(summary);
3480
- writeLine("");
3481
- }
3482
- function printDoctorResults(result) {
3483
- writeLine("");
3484
- writeLine(`${colors.bold}Nexus Agents Doctor${colors.reset}`);
3485
- writeLine("===================");
3486
- writeLine("");
3487
- writeLine(`${colors.cyan}Checking environment...${colors.reset}`);
3488
- writeLine("");
3489
- printNodeVersionCheck(result.nodeVersion);
3490
- printApiKeysCheck(result.apiKeys);
3491
- printConfigFileCheck(result.configFile);
3492
- writeLine("");
3493
- writeLine(`${colors.cyan}Checking CLI installations...${colors.reset}`);
3494
- writeLine("");
3495
- for (const cli of result.clis) {
3496
- printCliResult(cli);
3497
- }
3498
- writeLine(`${colors.cyan}Checking MCP configuration...${colors.reset}`);
3499
- writeLine("");
3500
- writeLine(
3501
- `${formatStatus(result.mcpServerReady)} MCP Server mode: ${result.mcpServerReady ? "Ready" : "Not ready"}`
3502
- );
3503
- writeLine(
3504
- `${formatStatus(result.mcpClientReady)} MCP Client mode: ${result.mcpClientReady ? "Ready (Codex mcp-server)" : "Not ready (Codex not installed)"}`
3505
- );
3506
- writeLine("");
3507
- writeLine(`${colors.cyan}Checking capabilities...${colors.reset}`);
3508
- writeLine("");
3509
- printCapabilities(result.clis);
3510
- writeLine("");
3511
- writeLine(`${colors.cyan}Checking model registry...${colors.reset}`);
3512
- writeLine("");
3513
- printRegistryAdvisory(result.registryAdvisory);
3514
- writeLine("");
3515
- writeLine(`${colors.cyan}Checking learning subsystem...${colors.reset}`);
3516
- writeLine("");
3517
- printLearningPersistence(result.learningPersistence);
3518
- writeLine("");
3519
- writeLine(`${colors.cyan}Checking data storage...${colors.reset}`);
3520
- writeLine("");
3521
- printSqliteCheck(result.sqliteCheck);
3522
- printDataDirectory(result.dataDirectory);
3523
- writeLine("");
3524
- printDoctorSummary(result);
3525
- }
3526
-
3527
- // src/cli/doctor.ts
3528
- var REQUIRED_NODE_MAJOR2 = 22;
3529
- var API_KEY_VARS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_AI_API_KEY"];
3530
- var CONFIG_FILE_PATHS = ["./nexus-agents.yaml", "./nexus-agents.yml"];
3531
- var DATA_SUBDIRECTORIES = [
3532
- "memory",
3533
- "memory/beliefs",
3534
- "learning",
3535
- "sessions",
3536
- "audit",
3537
- "voting",
3538
- "auth",
3539
- "research",
3540
- "checkpoints"
3541
- ];
3542
- function getFixCommand(name, issue) {
3543
- const commands = {
3544
- claude: {
3545
- install: "npm install -g @anthropic-ai/claude-code",
3546
- upgrade: "npm update -g @anthropic-ai/claude-code",
3547
- auth: "claude auth login"
3548
- },
3549
- gemini: {
3550
- install: "npm install -g @google/gemini-cli",
3551
- upgrade: "npm update -g @google/gemini-cli",
3552
- auth: "gemini auth login"
3553
- },
3554
- codex: {
3555
- install: "npm install -g @openai/codex",
3556
- upgrade: "npm update -g @openai/codex",
3557
- auth: "codex auth login"
3558
- },
3559
- opencode: {
3560
- install: "npm install -g opencode-ai",
3561
- upgrade: "npm update -g opencode-ai",
3562
- auth: "opencode auth login"
3563
- }
3564
- };
3565
- return commands[name][issue] ?? "";
3566
- }
3567
- function createNotFoundResult(name, errorMsg) {
3568
- return {
3569
- name,
3570
- installed: false,
3571
- version: "N/A",
3572
- versionStatus: "unsupported",
3573
- authenticated: false,
3574
- error: errorMsg,
3575
- fix: getFixCommand(name, "install")
3576
- };
3577
- }
3578
- function detectAuthMethod(name) {
3579
- const authMethods = {
3580
- claude: "CLI auth",
3581
- gemini: "ADC/CLI auth",
3582
- codex: "CLI auth",
3583
- opencode: "CLI auth"
3584
- };
3585
- return authMethods[name];
3586
- }
3587
- function createHealthyResult(name, health, capacity) {
3588
- const authenticated = health.healthy;
3589
- const result = {
3590
- name,
3591
- installed: true,
3592
- version: health.version,
3593
- versionStatus: health.versionStatus,
3594
- authenticated,
3595
- ...authenticated && { authMethod: detectAuthMethod(name) },
3596
- ...capacity !== void 0 && { capacity }
3597
- };
3598
- if (health.message !== void 0 && health.message !== "") {
3599
- return { ...result, error: health.message };
3600
- }
3601
- if (!authenticated) {
3602
- return { ...result, fix: getFixCommand(name, "auth") };
3603
- }
3604
- if (health.versionStatus === "outdated") {
3605
- return { ...result, fix: getFixCommand(name, "upgrade") };
3606
- }
3607
- return result;
3608
- }
3609
- async function checkCli(name) {
3610
- const adapters = createAllAdapters();
3611
- const adapter = adapters.get(name);
3612
- if (!adapter) {
3613
- return createNotFoundResult(name, "Adapter not available");
3614
- }
3615
- try {
3616
- const health = await adapter.healthCheck();
3617
- let capacity;
3618
- try {
3619
- capacity = await adapter.getCapacity();
3620
- } catch (capErr) {
3621
- void capErr;
3622
- }
3623
- return createHealthyResult(name, health, capacity);
3624
- } catch (error) {
3625
- const message = getErrorMessage(error);
3626
- const isNotFound = message.includes("ENOENT") || message.includes("not found");
3627
- return createNotFoundResult(name, isNotFound ? "Not found in PATH" : message);
3628
- }
3629
- }
3630
- function checkNodeVersion() {
3631
- const version = process.version;
3632
- const major = Number(version.slice(1).split(".")[0]);
3633
- return {
3634
- version,
3635
- major,
3636
- supported: major >= REQUIRED_NODE_MAJOR2
3637
- };
3638
- }
3639
- function checkApiKeys() {
3640
- return API_KEY_VARS.map((name) => ({
3641
- name,
3642
- configured: typeof process.env[name] === "string" && process.env[name] !== ""
3643
- }));
3644
- }
3645
- function checkConfigFile() {
3646
- for (const configPath of CONFIG_FILE_PATHS) {
3647
- if (existsSync(configPath)) {
3648
- return { found: true, path: configPath };
3649
- }
3650
- }
3651
- return { found: false, path: null };
3652
- }
3653
- function checkMcpServerReady() {
3654
- try {
3655
- const result = createServer({ name: "nexus-agents-doctor-check" });
3656
- return result.ok;
3657
- } catch {
3658
- return false;
3659
- }
3660
- }
3661
- function buildRegistryAdvisory(cliResults) {
3662
- const installedClis = new Set(cliResults.filter((c) => c.installed).map((c) => c.name));
3663
- const models = DEFAULT_MODEL_CAPABILITIES.models.filter((m) => m.cliName !== void 0).map((m) => {
3664
- const cliName = m.cliName ?? "";
3665
- const available = cliName.length > 0 && installedClis.has(cliName);
3666
- const reason = available ? `${cliName} CLI is installed` : `${cliName} CLI is not installed`;
3667
- return { modelId: m.id, displayName: m.displayName, cliName, available, reason };
3668
- });
3669
- const STALE_THRESHOLD_DAYS = 30;
3670
- const updatedAt = new Date(DEFAULT_MODEL_CAPABILITIES.updatedAt);
3671
- const nowMs = getTimeProvider().now();
3672
- const ageDays = Math.floor((nowMs - updatedAt.getTime()) / (1e3 * 60 * 60 * 24));
3673
- return {
3674
- totalModels: models.length,
3675
- availableModels: models.filter((m) => m.available).length,
3676
- unavailableModels: models.filter((m) => !m.available).length,
3677
- models,
3678
- registryAgeDays: ageDays,
3679
- registryStale: ageDays > STALE_THRESHOLD_DAYS
3680
- };
3681
- }
3682
- function countJsonlLines(filePath) {
3683
- if (!existsSync(filePath)) return 0;
3684
- return readFileSync(filePath, "utf-8").split("\n").filter((l) => l.trim().length > 0).length;
3685
- }
3686
- function readRulesMetadata(filePath) {
3687
- if (!existsSync(filePath)) return { count: 0, savedAt: null };
3688
- try {
3689
- const raw = JSON.parse(readFileSync(filePath, "utf-8"));
3690
- const rules = raw["rules"];
3691
- const saved = raw["savedAt"];
3692
- return {
3693
- count: Array.isArray(rules) ? rules.length : 0,
3694
- savedAt: typeof saved === "string" ? saved : null
3695
- };
3696
- } catch {
3697
- return { count: 0, savedAt: null };
3698
- }
3699
- }
3700
- function checkDirAccess(dir) {
3701
- const exists = existsSync(dir);
3702
- if (!exists) return { exists: false, writable: false };
3703
- try {
3704
- accessSync(dir, fsConstants.W_OK);
3705
- return { exists: true, writable: true };
3706
- } catch {
3707
- return { exists: true, writable: false };
3708
- }
3709
- }
3710
- var DISABLED_CHECK = {
3711
- enabled: false,
3712
- dirExists: false,
3713
- dirWritable: false,
3714
- outcomeCount: 0,
3715
- ruleCount: 0,
3716
- rulesLastSaved: null,
3717
- error: null
3718
- };
3719
- function checkLearningPersistence() {
3720
- if (!isPersistenceEnabled()) return DISABLED_CHECK;
3721
- try {
3722
- const { exists: dirExists, writable: dirWritable } = checkDirAccess(LEARNING_DIR);
3723
- const outcomeCount = countJsonlLines(OUTCOMES_FILE);
3724
- const { count: ruleCount, savedAt: rulesLastSaved } = readRulesMetadata(RULES_FILE);
3725
- return {
3726
- enabled: true,
3727
- dirExists,
3728
- dirWritable,
3729
- outcomeCount,
3730
- ruleCount,
3731
- rulesLastSaved,
3732
- error: null
3733
- };
3734
- } catch (error) {
3735
- return {
3736
- enabled: true,
3737
- dirExists: false,
3738
- dirWritable: false,
3739
- outcomeCount: 0,
3740
- ruleCount: 0,
3741
- rulesLastSaved: null,
3742
- error: getErrorMessage(error)
3743
- };
3744
- }
3745
- }
3746
- async function checkSqlite() {
3747
- try {
3748
- await import("better-sqlite3");
3749
- return { available: true, error: null };
3750
- } catch (error) {
3751
- const msg = getErrorMessage(error);
3752
- const isNotFound = msg.includes("Cannot find") || msg.includes("MODULE_NOT_FOUND");
3753
- return {
3754
- available: false,
3755
- error: isNotFound ? "better-sqlite3 not installed \u2014 5 memory backends unavailable" : `better-sqlite3 load error: ${msg}`
3756
- };
3757
- }
3758
- }
3759
- function checkDataDirectory() {
3760
- const rootPath = join(homedir(), ".nexus-agents");
3761
- const rootExists = existsSync(rootPath);
3762
- const subdirectories = DATA_SUBDIRECTORIES.map((name) => {
3763
- const fullPath = join(rootPath, name);
3764
- const exists = existsSync(fullPath);
3765
- return { name, path: fullPath, exists, writable: exists && isWritable(fullPath) };
3766
- });
3767
- return { rootExists, rootPath, subdirectories };
3768
- }
3769
- function isWritable(dirPath) {
3770
- try {
3771
- accessSync(dirPath, fsConstants.W_OK);
3772
- return true;
3773
- } catch {
3774
- return false;
3775
- }
3776
- }
3777
- async function runDoctor() {
3778
- const clis = await Promise.all([
3779
- checkCli("claude"),
3780
- checkCli("gemini"),
3781
- checkCli("codex"),
3782
- checkCli("opencode")
3783
- ]);
3784
- const nodeVersion = checkNodeVersion();
3785
- const apiKeys = checkApiKeys();
3786
- const configFile = checkConfigFile();
3787
- const mcpServerReady = checkMcpServerReady();
3788
- const codexCheck = clis.find((c) => c.name === "codex");
3789
- const mcpClientReady = codexCheck?.installed ?? false;
3790
- const registryAdvisory = buildRegistryAdvisory(clis);
3791
- const learningPersistence = checkLearningPersistence();
3792
- const sqliteCheck = await checkSqlite();
3793
- const dataDirectory = checkDataDirectory();
3794
- const hasAuthMethod = apiKeys.some((k) => k.configured) || clis.some((c) => c.installed && c.authenticated);
3795
- const allHealthy = nodeVersion.supported && hasAuthMethod && mcpServerReady && clis.every((c) => c.installed && c.authenticated && c.versionStatus !== "unsupported");
3796
- return {
3797
- clis,
3798
- nodeVersion,
3799
- apiKeys,
3800
- configFile,
3801
- mcpServerReady,
3802
- mcpClientReady,
3803
- registryAdvisory,
3804
- learningPersistence,
3805
- sqliteCheck,
3806
- dataDirectory,
3807
- allHealthy,
3808
- timestamp: new Date(getTimeProvider().now())
3809
- };
3810
- }
3811
- async function doctorCommand(options = {}) {
3812
- const result = await runDoctor();
3813
- printDoctorResults(result);
3814
- if (options.fix === true) {
3815
- await runDoctorFix(result);
3816
- }
3817
- return result.allHealthy ? 0 : 1;
3818
- }
3819
- async function runDoctorFix(result) {
3820
- const writeLine2 = (text) => {
3821
- process.stdout.write(text + "\n");
3822
- };
3823
- writeLine2("");
3824
- writeLine2("\x1B[1mAuto-fix\x1B[0m");
3825
- writeLine2("\u2500".repeat(40));
3826
- let fixCount = 0;
3827
- if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
3828
- const { runSetup } = await import("./setup-command-SS7LMN7Y.js");
3829
- const setupResult = runSetup({
3830
- skipMcp: true,
3831
- skipRules: true,
3832
- skipHooks: true,
3833
- skipConfig: true,
3834
- skipOpencode: true
3835
- });
3836
- if (setupResult.success) {
3837
- writeLine2("\u2713 Created missing data directories");
3838
- fixCount++;
3839
- }
3840
- }
3841
- if (!result.configFile.found) {
3842
- const { runConfigInitSync } = await import("./setup-config-DSMOOLVW.js");
3843
- const configResult = runConfigInitSync(process.cwd(), false, false);
3844
- if (configResult.success && configResult.created) {
3845
- writeLine2(`\u2713 Generated config: ${configResult.path}`);
3846
- fixCount++;
3847
- }
3848
- }
3849
- if (!result.sqliteCheck.available) {
3850
- writeLine2("");
3851
- writeLine2("\u26A0 better-sqlite3 not installed (manual step required):");
3852
- writeLine2(" npm install -g better-sqlite3");
3853
- }
3854
- if (fixCount > 0) {
3855
- writeLine2("");
3856
- writeLine2(
3857
- `\x1B[32m${String(fixCount)} issue(s) fixed.\x1B[0m Re-run \x1B[1mnexus-agents doctor\x1B[0m to verify.`
3858
- );
3859
- } else {
3860
- writeLine2("No auto-fixable issues found.");
3861
- }
3862
- writeLine2("");
3863
- }
3864
-
3865
- // src/cli/setup-data-dir.ts
3866
- var NEXUS_DATA_DIR = join2(homedir2(), ".nexus-agents");
3867
- var RESTRICTED_DIRS = /* @__PURE__ */ new Set(["auth"]);
3868
- function initDataDirectories(dryRun = false) {
3869
- const created = [];
3870
- const alreadyExisted = [];
3871
- try {
3872
- ensureDir(NEXUS_DATA_DIR, dryRun, created, alreadyExisted);
3873
- for (const subdir of DATA_SUBDIRECTORIES) {
3874
- const mode = RESTRICTED_DIRS.has(subdir) ? 448 : void 0;
3875
- ensureDir(join2(NEXUS_DATA_DIR, subdir), dryRun, created, alreadyExisted, mode);
3876
- }
3877
- return { success: true, rootPath: NEXUS_DATA_DIR, created, alreadyExisted, error: null };
3878
- } catch (error) {
3879
- const msg = error instanceof Error ? error.message : String(error);
3880
- return { success: false, rootPath: NEXUS_DATA_DIR, created, alreadyExisted, error: msg };
3881
- }
3882
- }
3883
- function ensureDir(dirPath, dryRun, created, alreadyExisted, mode) {
3884
- if (existsSync2(dirPath)) {
3885
- alreadyExisted.push(dirPath);
3886
- return;
3887
- }
3888
- if (!dryRun) {
3889
- mkdirSync(dirPath, { recursive: true, ...mode !== void 0 ? { mode } : {} });
3890
- }
3891
- created.push(dirPath);
3892
- }
3893
-
3894
2644
  export {
3895
- VERSION,
3896
2645
  sleep,
3897
2646
  delay,
3898
2647
  withTimeout,
@@ -3901,6 +2650,10 @@ export {
3901
2650
  getTimeoutForTask,
3902
2651
  estimateTaskComplexity2 as estimateTaskComplexity,
3903
2652
  getTimeoutForTaskAuto,
2653
+ calculateBackoffDelay,
2654
+ isRetryableError,
2655
+ categorizeError,
2656
+ executeCliRetryLoop,
3904
2657
  BaseCliAdapter,
3905
2658
  SubprocessCliAdapter,
3906
2659
  asRecord,
@@ -3908,9 +2661,6 @@ export {
3908
2661
  extractNumberField,
3909
2662
  ClaudeResponseParser,
3910
2663
  ClaudeCliAdapter,
3911
- CircuitError,
3912
- CircuitBreakerRegistry,
3913
- mapCliErrorToCategory,
3914
2664
  GeminiCliAdapter,
3915
2665
  CodexResponseParser,
3916
2666
  CodexCliAdapter,
@@ -3923,23 +2673,6 @@ export {
3923
2673
  createCliAdapter,
3924
2674
  createAllAdapters,
3925
2675
  isCliAvailable,
3926
- getAvailableClis,
3927
- STOPWORDS,
3928
- tokenize,
3929
- tokenizeFiltered,
3930
- stringifyValue,
3931
- capitalize,
3932
- capitalizeWords,
3933
- capitalizeKebab,
3934
- truncateWithInfo,
3935
- truncateSentence,
3936
- DEFAULT_TASK_TTL_MS,
3937
- clampTaskTtl,
3938
- doctorCommand,
3939
- initDataDirectories,
3940
- createServer,
3941
- connectTransport,
3942
- startStdioServer,
3943
- closeServer
2676
+ getAvailableClis
3944
2677
  };
3945
- //# sourceMappingURL=chunk-LKSTILEE.js.map
2678
+ //# sourceMappingURL=chunk-4AGPR6XZ.js.map