nexus-agents 2.27.0 → 2.29.0

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 (73) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +11 -10
  3. package/dist/chunk-5VZLXMO7.js +838 -0
  4. package/dist/chunk-5VZLXMO7.js.map +1 -0
  5. package/dist/chunk-633WH2ML.js +127 -0
  6. package/dist/chunk-633WH2ML.js.map +1 -0
  7. package/dist/chunk-7F6HYUIY.js +327 -0
  8. package/dist/chunk-7F6HYUIY.js.map +1 -0
  9. package/dist/chunk-CLYZ7FWP.js +30 -0
  10. package/dist/chunk-CLYZ7FWP.js.map +1 -0
  11. package/dist/{chunk-WZACXZGT.js → chunk-DAMRMAM2.js} +19543 -35952
  12. package/dist/chunk-DAMRMAM2.js.map +1 -0
  13. package/dist/{chunk-VL7L56MU.js → chunk-HH5LVGEE.js} +6 -6
  14. package/dist/{chunk-6QLUXEA6.js → chunk-HWDBNDUX.js} +2 -2
  15. package/dist/chunk-I6YDS23R.js +354 -0
  16. package/dist/chunk-I6YDS23R.js.map +1 -0
  17. package/dist/{chunk-WF5CWL2N.js → chunk-IMWYKX4H.js} +4377 -4190
  18. package/dist/chunk-IMWYKX4H.js.map +1 -0
  19. package/dist/chunk-KGDG6PWZ.js +61 -0
  20. package/dist/chunk-KGDG6PWZ.js.map +1 -0
  21. package/dist/chunk-POBO4G2P.js +3700 -0
  22. package/dist/chunk-POBO4G2P.js.map +1 -0
  23. package/dist/chunk-S3BKWNST.js +13475 -0
  24. package/dist/chunk-S3BKWNST.js.map +1 -0
  25. package/dist/chunk-T7PU3NPQ.js +42 -0
  26. package/dist/chunk-T7PU3NPQ.js.map +1 -0
  27. package/dist/{chunk-4LM3YGJF.js → chunk-WSK4VSXP.js} +2 -2
  28. package/dist/{chunk-RNQRE45M.js → chunk-ZBZJHXRT.js} +181 -1119
  29. package/dist/chunk-ZBZJHXRT.js.map +1 -0
  30. package/dist/cli.d.ts +2 -2
  31. package/dist/cli.js +1410 -659
  32. package/dist/cli.js.map +1 -1
  33. package/dist/composite-router-YPRWVTRB.js +17 -0
  34. package/dist/consensus-vote-DBE6RNZG.js +23 -0
  35. package/dist/{dist-Y5F6UM2N.js → dist-7PQR2BQB.js} +1584 -1443
  36. package/dist/dist-7PQR2BQB.js.map +1 -0
  37. package/dist/{doctor-deep-EN3V4PXO.js → doctor-deep-AWE7SRU6.js} +4 -3
  38. package/dist/expert-config-FHNBQRX2.js +22 -0
  39. package/dist/expert-config-FHNBQRX2.js.map +1 -0
  40. package/dist/factory-O5C7ZBZO.js +13 -0
  41. package/dist/factory-O5C7ZBZO.js.map +1 -0
  42. package/dist/factory-PCHGQ3ZG.js +16 -0
  43. package/dist/factory-PCHGQ3ZG.js.map +1 -0
  44. package/dist/index.d.ts +3851 -9022
  45. package/dist/index.js +505 -318
  46. package/dist/index.js.map +1 -1
  47. package/dist/learning-persistence-WMWZJZ35.js +16 -0
  48. package/dist/learning-persistence-WMWZJZ35.js.map +1 -0
  49. package/dist/mcp-config-AUZQPUBY.js +12 -0
  50. package/dist/mcp-config-AUZQPUBY.js.map +1 -0
  51. package/dist/{model-capabilities-types-CSWO27YN.d.ts → model-capabilities-types-B57GZryc.d.ts} +1 -1
  52. package/dist/routing-memory-QY3XMU2R.js +13 -0
  53. package/dist/routing-memory-QY3XMU2R.js.map +1 -0
  54. package/dist/session-memory-3MBCE5KS.js +22 -0
  55. package/dist/session-memory-3MBCE5KS.js.map +1 -0
  56. package/dist/{setup-command-ZN7DSP53.js → setup-command-IQ4MD3FT.js} +8 -5
  57. package/dist/setup-command-IQ4MD3FT.js.map +1 -0
  58. package/dist/setup-config-5YUPLDXF.js +10 -0
  59. package/dist/setup-config-5YUPLDXF.js.map +1 -0
  60. package/dist/weather-report-CC2C4KAX.js +15 -0
  61. package/dist/weather-report-CC2C4KAX.js.map +1 -0
  62. package/package.json +33 -36
  63. package/dist/chunk-RNQRE45M.js.map +0 -1
  64. package/dist/chunk-WF5CWL2N.js.map +0 -1
  65. package/dist/chunk-WZACXZGT.js.map +0 -1
  66. package/dist/dist-Y5F6UM2N.js.map +0 -1
  67. package/dist/setup-config-ZOJUDXNP.js +0 -9
  68. /package/dist/{chunk-VL7L56MU.js.map → chunk-HH5LVGEE.js.map} +0 -0
  69. /package/dist/{chunk-6QLUXEA6.js.map → chunk-HWDBNDUX.js.map} +0 -0
  70. /package/dist/{chunk-4LM3YGJF.js.map → chunk-WSK4VSXP.js.map} +0 -0
  71. /package/dist/{doctor-deep-EN3V4PXO.js.map → composite-router-YPRWVTRB.js.map} +0 -0
  72. /package/dist/{setup-command-ZN7DSP53.js.map → consensus-vote-DBE6RNZG.js.map} +0 -0
  73. /package/dist/{setup-config-ZOJUDXNP.js.map → doctor-deep-AWE7SRU6.js.map} +0 -0
@@ -1,5 +1,4 @@
1
1
  import {
2
- BACKOFF_CONFIG,
3
2
  CLI_SUBPROCESS_TIMEOUTS,
4
3
  CLI_TIMEOUTS,
5
4
  CLI_VERSION_REQUIREMENTS,
@@ -8,13 +7,9 @@ import {
8
7
  DEFAULT_MODEL_CAPABILITIES,
9
8
  DEFAULT_MODEL_PER_CLI,
10
9
  ErrorCode,
11
- LEARNING_DIR,
12
10
  NexusError,
13
- OUTCOMES_FILE,
14
- RULES_FILE,
15
11
  buildModelInfo,
16
12
  clampPercent,
17
- colors,
18
13
  createLogger,
19
14
  detectTaskCategory,
20
15
  err,
@@ -25,36 +20,17 @@ import {
25
20
  getOutcomeStore,
26
21
  getRandomProvider,
27
22
  getTimeProvider,
28
- isPersistenceEnabled,
29
- ok,
30
- symbols,
31
- writeLine
32
- } from "./chunk-WF5CWL2N.js";
23
+ isRateLimitText,
24
+ ok
25
+ } from "./chunk-IMWYKX4H.js";
33
26
 
34
- // src/version.ts
35
- var VERSION = true ? "2.27.0" : "dev";
27
+ // src/cli-adapters/subprocess-adapter.ts
28
+ import { spawn } from "child_process";
36
29
 
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
- }
30
+ // src/cli-adapters/base-adapter.ts
31
+ import { exec } from "child_process";
32
+ import { promisify } from "util";
33
+ import semver from "semver";
58
34
 
59
35
  // src/cli-adapters/cli-timeout-helpers.ts
60
36
  function estimateTaskComplexity(taskDescription) {
@@ -123,11 +99,6 @@ function getTimeoutForTaskAuto(cli, taskDescription) {
123
99
  return getAdaptiveTimeout(cli, taskDescription);
124
100
  }
125
101
 
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
102
  // src/cli-adapters/capacity-tracker.ts
132
103
  var DEFAULT_TOKEN_LIMITS = {
133
104
  claude: 1e5,
@@ -258,6 +229,98 @@ function createCapacityTracker(cli) {
258
229
  return new CapacityTracker(getDefaultConfig(cli));
259
230
  }
260
231
 
232
+ // src/utils/async-utils.ts
233
+ function sleep(ms) {
234
+ return new Promise((resolve) => setTimeout(resolve, ms));
235
+ }
236
+ var delay = sleep;
237
+ async function withTimeout(promise, timeoutMs, errorMessage) {
238
+ let timeoutId;
239
+ const timeoutPromise = new Promise((_, reject) => {
240
+ timeoutId = setTimeout(() => {
241
+ reject(new Error(errorMessage));
242
+ }, timeoutMs);
243
+ });
244
+ try {
245
+ const result = await Promise.race([promise, timeoutPromise]);
246
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
247
+ return { ok: true, value: result };
248
+ } catch (error) {
249
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
250
+ return { ok: false, error: getErrorMessage(error) };
251
+ }
252
+ }
253
+
254
+ // src/cli-adapters/cli-retry-loop.ts
255
+ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
256
+ "TIMEOUT",
257
+ "RATE_LIMITED",
258
+ "CONNECTION_ERROR"
259
+ ]);
260
+ function calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs) {
261
+ const exponentialDelay = baseDelayMs * Math.pow(2, attempt - 1);
262
+ const jitter = getRandomProvider().random() * 0.3 * exponentialDelay;
263
+ const delayMs = exponentialDelay + jitter;
264
+ return Math.min(delayMs, maxDelayMs);
265
+ }
266
+ function isRetryableError(code) {
267
+ return RETRYABLE_ERROR_CODES.has(code);
268
+ }
269
+ function categorizeError(error) {
270
+ switch (error.code) {
271
+ case "TIMEOUT":
272
+ return "timeout";
273
+ case "RATE_LIMITED":
274
+ return "rate_limit";
275
+ case "NOT_AUTHENTICATED":
276
+ return "authentication";
277
+ case "CONNECTION_ERROR":
278
+ return "connection";
279
+ default:
280
+ return "unknown";
281
+ }
282
+ }
283
+ async function executeCliRetryLoop(executeFn, config) {
284
+ const maxAttempts = config.allowRetry ? config.maxRetries + 1 : 1;
285
+ let lastError;
286
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
287
+ const result = await executeFn();
288
+ if (result.ok) {
289
+ return ok({ response: result.value, retryCount: attempt - 1 });
290
+ }
291
+ lastError = result.error;
292
+ if (config.circuitBreaker !== void 0 && config.circuitBreaker !== null) {
293
+ config.circuitBreaker.recordFailure(categorizeError(lastError));
294
+ }
295
+ if (!shouldRetry(lastError, attempt, maxAttempts, config.circuitBreaker)) {
296
+ return err(lastError);
297
+ }
298
+ const delayMs = calculateBackoffDelay(attempt, config.baseDelayMs, config.maxDelayMs);
299
+ config.logger.debug("Retrying CLI execution", {
300
+ cli: config.cli,
301
+ attempt,
302
+ nextAttempt: attempt + 1,
303
+ delayMs: Math.round(delayMs)
304
+ });
305
+ await delay(delayMs);
306
+ }
307
+ return err(
308
+ lastError ?? {
309
+ code: "UNKNOWN",
310
+ message: "Max retries exceeded",
311
+ cli: config.cli,
312
+ retryable: false
313
+ }
314
+ );
315
+ }
316
+ function shouldRetry(error, attempt, maxAttempts, circuitBreaker) {
317
+ if (attempt >= maxAttempts) return false;
318
+ if (!error.retryable) return false;
319
+ if (!isRetryableError(error.code)) return false;
320
+ if (circuitBreaker?.getState() === "open") return false;
321
+ return true;
322
+ }
323
+
261
324
  // src/cli-adapters/base-adapter.ts
262
325
  var execAsync = promisify(exec);
263
326
  var DEFAULT_CAPACITY_FALLBACK = 1e5;
@@ -276,8 +339,8 @@ var BaseCliAdapter = class {
276
339
  initialized = false;
277
340
  lastHealthCheck;
278
341
  cachedVersion;
279
- constructor(logger6) {
280
- this.logger = logger6 ?? createLogger({ component: "cli-adapter" });
342
+ constructor(logger5) {
343
+ this.logger = logger5 ?? createLogger({ component: "cli-adapter" });
281
344
  }
282
345
  /**
283
346
  * Initializes the capacity tracker.
@@ -323,47 +386,33 @@ var BaseCliAdapter = class {
323
386
  return getTimeoutForTaskAuto(this.name, task.content);
324
387
  }
325
388
  /**
326
- * Executes task with retry logic.
389
+ * Executes task with retry logic via shared retry loop.
327
390
  */
328
391
  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", {
392
+ const result = await executeCliRetryLoop(() => this.executeTask(task, opts), {
393
+ maxRetries: opts.maxRetries,
394
+ allowRetry: opts.allowRetry,
395
+ baseDelayMs: 1e3,
396
+ maxDelayMs: 16e3,
397
+ cli: this.name,
398
+ logger: this.logger
399
+ });
400
+ if (result.ok) {
401
+ this.recordUsage(result.value.response);
402
+ this.logger.info("Task executed successfully", {
354
403
  cli: this.name,
355
- attempt,
356
- nextAttempt: attempt + 1
404
+ attempt: result.value.retryCount + 1,
405
+ durationMs: result.value.response.durationMs,
406
+ tokensUsed: result.value.response.usage?.totalTokens
357
407
  });
358
- await this.delay(Math.pow(BACKOFF_CONFIG.exponentBase, attempt) * BACKOFF_CONFIG.baseDelayMs);
408
+ return { ok: true, value: result.value.response };
359
409
  }
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;
410
+ this.logger.warn("Task execution failed", {
411
+ cli: this.name,
412
+ error: result.error.message,
413
+ retryable: result.error.retryable
414
+ });
415
+ return result;
367
416
  }
368
417
  /**
369
418
  * Performs a health check.
@@ -518,19 +567,39 @@ var BaseCliAdapter = class {
518
567
  }
519
568
  };
520
569
 
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"
570
+ // src/security/output-sanitizer.ts
571
+ var REDACTED_KEY_PLACEHOLDER = "[REDACTED_KEY]";
572
+ var KEY_PATTERNS = [
573
+ // Anthropic: sk-ant-api03-... (at least 20 chars after prefix)
574
+ /sk-ant-[A-Za-z0-9_-]{20,}/g,
575
+ // OpenAI project: sk-proj-... (at least 20 chars after prefix)
576
+ /sk-proj-[A-Za-z0-9_-]{20,}/g,
577
+ // Generic OpenAI: sk-... (at least 20 chars after prefix)
578
+ /sk-[A-Za-z0-9_-]{20,}/g,
579
+ // Google AI / Gemini: AIzaSy... (at least 30 chars total)
580
+ /AIzaSy[A-Za-z0-9_-]{24,}/g,
581
+ // GitHub PAT: ghp_...
582
+ /ghp_[A-Za-z0-9]{20,}/g,
583
+ // GitHub OAuth: gho_...
584
+ /gho_[A-Za-z0-9]{20,}/g,
585
+ // GitLab PAT: glpat-...
586
+ /glpat-[A-Za-z0-9_-]{10,}/g,
587
+ // npm token: npm_...
588
+ /npm_[A-Za-z0-9]{20,}/g,
589
+ // PyPI token: pypi-...
590
+ /pypi-[A-Za-z0-9_-]{20,}/g
529
591
  ];
530
- function isRateLimitOutput(stdout) {
531
- const lower = stdout.toLowerCase();
532
- return RATE_LIMIT_PATTERNS.some((pattern) => lower.includes(pattern));
592
+ function sanitizeOutput(text) {
593
+ if (text === "") return text;
594
+ let result = text;
595
+ for (const pattern of KEY_PATTERNS) {
596
+ pattern.lastIndex = 0;
597
+ result = result.replace(pattern, REDACTED_KEY_PLACEHOLDER);
598
+ }
599
+ return result;
533
600
  }
601
+
602
+ // src/cli-adapters/subprocess-adapter.ts
534
603
  var PLAINTEXT_FALLBACK_MIN_LENGTH = 30;
535
604
  function tryPlaintextFallback(stdout) {
536
605
  const trimmed = stdout.trim();
@@ -572,12 +641,11 @@ var STDERR_CONNECTION_PATTERNS = [
572
641
  "eaddrinuse",
573
642
  "address already in use"
574
643
  ];
575
- var STDERR_RATE_LIMIT_PATTERNS = ["rate limit", "quota exceeded", "429", "too many requests"];
576
644
  var STDERR_TIMEOUT_PATTERNS = ["timeout", "timed out", "etimedout"];
577
645
  function classifyStderrError(stderr) {
578
646
  const lower = stderr.toLowerCase();
579
647
  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";
648
+ if (isRateLimitText(stderr)) return "RATE_LIMITED";
581
649
  if (STDERR_TIMEOUT_PATTERNS.some((p) => lower.includes(p))) return "TIMEOUT";
582
650
  return "EXECUTION_ERROR";
583
651
  }
@@ -731,6 +799,8 @@ var SubprocessCliAdapter = class extends BaseCliAdapter {
731
799
  }
732
800
  /** Classify a subprocess close event into a Result. */
733
801
  classifyCloseResult(code, state, startTime) {
802
+ state.stdout = sanitizeOutput(state.stdout);
803
+ state.stderr = sanitizeOutput(state.stderr);
734
804
  if (code !== 0 && state.stdout === "") {
735
805
  if (state.stderr !== "") {
736
806
  const msg2 = state.stderr;
@@ -757,7 +827,7 @@ var SubprocessCliAdapter = class extends BaseCliAdapter {
757
827
  }
758
828
  const text = this.parser.extractResponse(stdout);
759
829
  if (text === null) {
760
- if (isRateLimitOutput(stdout)) {
830
+ if (isRateLimitText(stdout)) {
761
831
  const snippet2 = stdout.slice(0, 500).trim();
762
832
  return err(this.createError("RATE_LIMITED", snippet2));
763
833
  }
@@ -800,7 +870,7 @@ var SubprocessCliAdapter = class extends BaseCliAdapter {
800
870
  if (error.message.includes("ENOENT")) {
801
871
  return err(this.createError("NOT_FOUND", `${this.name} CLI not found`, error));
802
872
  }
803
- return err(this.createError("EXECUTION_ERROR", error.message, error));
873
+ return err(this.createError("EXECUTION_ERROR", sanitizeOutput(error.message), error));
804
874
  }
805
875
  /**
806
876
  * Initializes the adapter and capacity tracker.
@@ -1352,7 +1422,7 @@ var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
1352
1422
  };
1353
1423
  var TIMEOUT_PATTERNS = ["timeout", "timed out"];
1354
1424
  var AUTH_PATTERNS = ["auth", "unauthorized", "forbidden", "oauth"];
1355
- var RATE_LIMIT_PATTERNS2 = ["rate limit", "too many requests", "429"];
1425
+ var RATE_LIMIT_PATTERNS = ["rate limit", "too many requests", "429"];
1356
1426
  var CONNECTION_PATTERNS = [
1357
1427
  "connection",
1358
1428
  "econnrefused",
@@ -1365,7 +1435,7 @@ var CRASH_PATTERNS = ["crash", "exited", "killed", "sigterm", "sigkill"];
1365
1435
  function matchesPatterns(text, patterns) {
1366
1436
  return patterns.some((pattern) => text.includes(pattern));
1367
1437
  }
1368
- function categorizeError(error) {
1438
+ function categorizeError2(error) {
1369
1439
  if (!(error instanceof Error)) {
1370
1440
  return "unknown";
1371
1441
  }
@@ -1374,7 +1444,7 @@ function categorizeError(error) {
1374
1444
  const combined = `${message} ${name}`;
1375
1445
  if (matchesPatterns(combined, TIMEOUT_PATTERNS)) return "timeout";
1376
1446
  if (matchesPatterns(combined, AUTH_PATTERNS)) return "authentication";
1377
- if (matchesPatterns(combined, RATE_LIMIT_PATTERNS2)) return "rate_limit";
1447
+ if (matchesPatterns(combined, RATE_LIMIT_PATTERNS)) return "rate_limit";
1378
1448
  if (matchesPatterns(combined, CONNECTION_PATTERNS)) return "connection";
1379
1449
  if (matchesPatterns(combined, CRASH_PATTERNS)) return "crash";
1380
1450
  return "unknown";
@@ -1407,7 +1477,7 @@ var CliCircuitBreaker = class {
1407
1477
  this.onSuccess();
1408
1478
  return ok(result);
1409
1479
  } catch (error) {
1410
- const category = categorizeError(error);
1480
+ const category = categorizeError2(error);
1411
1481
  if (this.shouldCountFailure(category)) {
1412
1482
  this.onFailure(category);
1413
1483
  }
@@ -1654,34 +1724,6 @@ var GEMINI_LEGACY_DEFAULTS = {
1654
1724
  inputCost: 0.15,
1655
1725
  outputCost: 0.6
1656
1726
  };
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
1727
  function createCircuitOpenError(cli) {
1686
1728
  return {
1687
1729
  code: "EXECUTION_ERROR",
@@ -1833,69 +1875,14 @@ var GeminiCliAdapter = class extends SubprocessCliAdapter {
1833
1875
  return err(result.error);
1834
1876
  }
1835
1877
  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)
1878
+ return executeCliRetryLoop(() => this.executeTask(task, options), {
1879
+ maxRetries: options.maxRetries,
1880
+ allowRetry: options.allowRetry,
1881
+ baseDelayMs: this.baseDelayMs,
1882
+ maxDelayMs: this.maxDelayMs,
1883
+ circuitBreaker: this.circuitBreaker,
1884
+ cli: this.name,
1885
+ logger: this.adapterLogger
1899
1886
  });
1900
1887
  }
1901
1888
  };
@@ -2933,9 +2920,9 @@ function createCodexAdapter(transport, options) {
2933
2920
  }
2934
2921
  return new CodexMcpAdapter(options);
2935
2922
  }
2936
- function createAllAdapters(logger6, codexTransport = "mcp") {
2923
+ function createAllAdapters(logger5, codexTransport = "mcp") {
2937
2924
  const adapters = /* @__PURE__ */ new Map();
2938
- const options = logger6 !== void 0 ? { logger: logger6 } : void 0;
2925
+ const options = logger5 !== void 0 ? { logger: logger5 } : void 0;
2939
2926
  adapters.set("claude", new ClaudeCliAdapter(options));
2940
2927
  adapters.set("gemini", new GeminiCliAdapter(options));
2941
2928
  adapters.set("codex", createCodexAdapter(codexTransport, options ?? {}));
@@ -2979,919 +2966,7 @@ async function getAvailableClis(cache) {
2979
2966
  ).map((r) => r.value.cli);
2980
2967
  }
2981
2968
 
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
- } else {
3405
- writeLine(`${formatStatus(true)} Model registry: ${ageText}`);
3406
- }
3407
- }
3408
- function printLearningPersistence(check) {
3409
- if (!check.enabled) {
3410
- writeLine(`${formatStatus(true)} Learning persistence: ${colors.dim}Disabled${colors.reset}`);
3411
- writeLine(` ${colors.dim}Set NEXUS_PERSIST_LEARNING=true to enable${colors.reset}`);
3412
- return;
3413
- }
3414
- const healthy = check.dirExists && check.dirWritable && check.error === null;
3415
- writeLine(`${formatStatus(healthy, !healthy)} Learning persistence: Enabled`);
3416
- if (check.error !== null) {
3417
- writeLine(` ${colors.red}Error: ${check.error}${colors.reset}`);
3418
- return;
3419
- }
3420
- const dirStatus = check.dirExists ? check.dirWritable ? `${colors.green}writable${colors.reset}` : `${colors.red}not writable${colors.reset}` : `${colors.yellow}not created yet${colors.reset}`;
3421
- writeLine(` Data directory: ${dirStatus}`);
3422
- writeLine(` Outcomes: ${String(check.outcomeCount)} recorded`);
3423
- writeLine(` Distilled rules: ${String(check.ruleCount)} active`);
3424
- if (check.rulesLastSaved !== null) {
3425
- writeLine(` Rules last saved: ${check.rulesLastSaved}`);
3426
- }
3427
- }
3428
- function printSqliteCheck(check) {
3429
- if (check.available) {
3430
- writeLine(
3431
- `${formatStatus(true)} SQLite (better-sqlite3): ${colors.green}Available${colors.reset}`
3432
- );
3433
- } else {
3434
- writeLine(
3435
- `${formatStatus(false, true)} SQLite (better-sqlite3): ${colors.yellow}Not available${colors.reset}`
3436
- );
3437
- writeLine(
3438
- ` ${colors.dim}Memory backends (agentic, adaptive, typed) require it${colors.reset}`
3439
- );
3440
- writeLine(` ${colors.dim}Fix: npm install -g better-sqlite3${colors.reset}`);
3441
- }
3442
- }
3443
- function printDataDirectory(check) {
3444
- if (check.rootExists) {
3445
- const existCount = check.subdirectories.filter((d) => d.exists).length;
3446
- const totalCount = check.subdirectories.length;
3447
- const allExist = existCount === totalCount;
3448
- const allWritable = check.subdirectories.every((d) => !d.exists || d.writable);
3449
- const healthy = allExist && allWritable;
3450
- writeLine(
3451
- `${formatStatus(healthy, !healthy)} Data directory: ${check.rootPath} (${String(existCount)}/${String(totalCount)} subdirs)`
3452
- );
3453
- if (!allExist) {
3454
- const missing = check.subdirectories.filter((d) => !d.exists);
3455
- for (const dir of missing) {
3456
- writeLine(` ${colors.dim}Missing: ${dir.name}/${colors.reset}`);
3457
- }
3458
- writeLine(` ${colors.dim}Fix: nexus-agents setup${colors.reset}`);
3459
- }
3460
- if (!allWritable) {
3461
- const readonly_ = check.subdirectories.filter((d) => d.exists && !d.writable);
3462
- for (const dir of readonly_) {
3463
- writeLine(` ${colors.yellow}Not writable: ${dir.name}/${colors.reset}`);
3464
- }
3465
- }
3466
- } else {
3467
- writeLine(
3468
- `${formatStatus(false, true)} Data directory: ${colors.yellow}Not created${colors.reset}`
3469
- );
3470
- writeLine(` ${colors.dim}Run: nexus-agents setup${colors.reset}`);
3471
- }
3472
- }
3473
- function printDoctorSummary(result) {
3474
- const unhealthyCount = result.clis.filter((c) => !c.installed || !c.authenticated).length;
3475
- const nodeIssue = result.nodeVersion.supported ? 0 : 1;
3476
- const totalIssues = unhealthyCount + nodeIssue + (result.mcpServerReady ? 0 : 1);
3477
- const summary = result.allHealthy ? `${colors.green}${colors.bold}Status: Ready${colors.reset}` : `${colors.yellow}${colors.bold}Summary: ${String(totalIssues)} issue(s) found${colors.reset}`;
3478
- writeLine(summary);
3479
- writeLine("");
3480
- }
3481
- function printDoctorResults(result) {
3482
- writeLine("");
3483
- writeLine(`${colors.bold}Nexus Agents Doctor${colors.reset}`);
3484
- writeLine("===================");
3485
- writeLine("");
3486
- writeLine(`${colors.cyan}Checking environment...${colors.reset}`);
3487
- writeLine("");
3488
- printNodeVersionCheck(result.nodeVersion);
3489
- printApiKeysCheck(result.apiKeys);
3490
- printConfigFileCheck(result.configFile);
3491
- writeLine("");
3492
- writeLine(`${colors.cyan}Checking CLI installations...${colors.reset}`);
3493
- writeLine("");
3494
- for (const cli of result.clis) {
3495
- printCliResult(cli);
3496
- }
3497
- writeLine(`${colors.cyan}Checking MCP configuration...${colors.reset}`);
3498
- writeLine("");
3499
- writeLine(
3500
- `${formatStatus(result.mcpServerReady)} MCP Server mode: ${result.mcpServerReady ? "Ready" : "Not ready"}`
3501
- );
3502
- writeLine(
3503
- `${formatStatus(result.mcpClientReady)} MCP Client mode: ${result.mcpClientReady ? "Ready (Codex mcp-server)" : "Not ready (Codex not installed)"}`
3504
- );
3505
- writeLine("");
3506
- writeLine(`${colors.cyan}Checking capabilities...${colors.reset}`);
3507
- writeLine("");
3508
- printCapabilities(result.clis);
3509
- writeLine("");
3510
- writeLine(`${colors.cyan}Checking model registry...${colors.reset}`);
3511
- writeLine("");
3512
- printRegistryAdvisory(result.registryAdvisory);
3513
- writeLine("");
3514
- writeLine(`${colors.cyan}Checking learning subsystem...${colors.reset}`);
3515
- writeLine("");
3516
- printLearningPersistence(result.learningPersistence);
3517
- writeLine("");
3518
- writeLine(`${colors.cyan}Checking data storage...${colors.reset}`);
3519
- writeLine("");
3520
- printSqliteCheck(result.sqliteCheck);
3521
- printDataDirectory(result.dataDirectory);
3522
- writeLine("");
3523
- printDoctorSummary(result);
3524
- }
3525
-
3526
- // src/cli/doctor.ts
3527
- var REQUIRED_NODE_MAJOR2 = 22;
3528
- var API_KEY_VARS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_AI_API_KEY"];
3529
- var CONFIG_FILE_PATHS = ["./nexus-agents.yaml", "./nexus-agents.yml"];
3530
- var DATA_SUBDIRECTORIES = [
3531
- "memory",
3532
- "memory/beliefs",
3533
- "learning",
3534
- "sessions",
3535
- "audit",
3536
- "voting",
3537
- "auth",
3538
- "research",
3539
- "checkpoints"
3540
- ];
3541
- function getFixCommand(name, issue) {
3542
- const commands = {
3543
- claude: {
3544
- install: "npm install -g @anthropic-ai/claude-code",
3545
- upgrade: "npm update -g @anthropic-ai/claude-code",
3546
- auth: "claude auth login"
3547
- },
3548
- gemini: {
3549
- install: "npm install -g @google/gemini-cli",
3550
- upgrade: "npm update -g @google/gemini-cli",
3551
- auth: "gemini auth login"
3552
- },
3553
- codex: {
3554
- install: "npm install -g @openai/codex",
3555
- upgrade: "npm update -g @openai/codex",
3556
- auth: "codex auth login"
3557
- },
3558
- opencode: {
3559
- install: "npm install -g opencode-ai",
3560
- upgrade: "npm update -g opencode-ai",
3561
- auth: "opencode auth login"
3562
- }
3563
- };
3564
- return commands[name][issue] ?? "";
3565
- }
3566
- function createNotFoundResult(name, errorMsg) {
3567
- return {
3568
- name,
3569
- installed: false,
3570
- version: "N/A",
3571
- versionStatus: "unsupported",
3572
- authenticated: false,
3573
- error: errorMsg,
3574
- fix: getFixCommand(name, "install")
3575
- };
3576
- }
3577
- function detectAuthMethod(name) {
3578
- const authMethods = {
3579
- claude: "CLI auth",
3580
- gemini: "ADC/CLI auth",
3581
- codex: "CLI auth",
3582
- opencode: "CLI auth"
3583
- };
3584
- return authMethods[name];
3585
- }
3586
- function createHealthyResult(name, health, capacity) {
3587
- const authenticated = health.healthy;
3588
- const result = {
3589
- name,
3590
- installed: true,
3591
- version: health.version,
3592
- versionStatus: health.versionStatus,
3593
- authenticated,
3594
- ...authenticated && { authMethod: detectAuthMethod(name) },
3595
- ...capacity !== void 0 && { capacity }
3596
- };
3597
- if (health.message !== void 0 && health.message !== "") {
3598
- return { ...result, error: health.message };
3599
- }
3600
- if (!authenticated) {
3601
- return { ...result, fix: getFixCommand(name, "auth") };
3602
- }
3603
- if (health.versionStatus === "outdated") {
3604
- return { ...result, fix: getFixCommand(name, "upgrade") };
3605
- }
3606
- return result;
3607
- }
3608
- async function checkCli(name) {
3609
- const adapters = createAllAdapters();
3610
- const adapter = adapters.get(name);
3611
- if (!adapter) {
3612
- return createNotFoundResult(name, "Adapter not available");
3613
- }
3614
- try {
3615
- const health = await adapter.healthCheck();
3616
- let capacity;
3617
- try {
3618
- capacity = await adapter.getCapacity();
3619
- } catch (capErr) {
3620
- void capErr;
3621
- }
3622
- return createHealthyResult(name, health, capacity);
3623
- } catch (error) {
3624
- const message = getErrorMessage(error);
3625
- const isNotFound = message.includes("ENOENT") || message.includes("not found");
3626
- return createNotFoundResult(name, isNotFound ? "Not found in PATH" : message);
3627
- }
3628
- }
3629
- function checkNodeVersion() {
3630
- const version = process.version;
3631
- const major = Number(version.slice(1).split(".")[0]);
3632
- return {
3633
- version,
3634
- major,
3635
- supported: major >= REQUIRED_NODE_MAJOR2
3636
- };
3637
- }
3638
- function checkApiKeys() {
3639
- return API_KEY_VARS.map((name) => ({
3640
- name,
3641
- configured: typeof process.env[name] === "string" && process.env[name] !== ""
3642
- }));
3643
- }
3644
- function checkConfigFile() {
3645
- for (const configPath of CONFIG_FILE_PATHS) {
3646
- if (existsSync(configPath)) {
3647
- return { found: true, path: configPath };
3648
- }
3649
- }
3650
- return { found: false, path: null };
3651
- }
3652
- function checkMcpServerReady() {
3653
- try {
3654
- const result = createServer({ name: "nexus-agents-doctor-check" });
3655
- return result.ok;
3656
- } catch {
3657
- return false;
3658
- }
3659
- }
3660
- function buildRegistryAdvisory(cliResults) {
3661
- const installedClis = new Set(cliResults.filter((c) => c.installed).map((c) => c.name));
3662
- const models = DEFAULT_MODEL_CAPABILITIES.models.filter((m) => m.cliName !== void 0).map((m) => {
3663
- const cliName = m.cliName ?? "";
3664
- const available = cliName.length > 0 && installedClis.has(cliName);
3665
- const reason = available ? `${cliName} CLI is installed` : `${cliName} CLI is not installed`;
3666
- return { modelId: m.id, displayName: m.displayName, cliName, available, reason };
3667
- });
3668
- const STALE_THRESHOLD_DAYS = 30;
3669
- const updatedAt = new Date(DEFAULT_MODEL_CAPABILITIES.updatedAt);
3670
- const nowMs = getTimeProvider().now();
3671
- const ageDays = Math.floor((nowMs - updatedAt.getTime()) / (1e3 * 60 * 60 * 24));
3672
- return {
3673
- totalModels: models.length,
3674
- availableModels: models.filter((m) => m.available).length,
3675
- unavailableModels: models.filter((m) => !m.available).length,
3676
- models,
3677
- registryAgeDays: ageDays,
3678
- registryStale: ageDays > STALE_THRESHOLD_DAYS
3679
- };
3680
- }
3681
- function countJsonlLines(filePath) {
3682
- if (!existsSync(filePath)) return 0;
3683
- return readFileSync(filePath, "utf-8").split("\n").filter((l) => l.trim().length > 0).length;
3684
- }
3685
- function readRulesMetadata(filePath) {
3686
- if (!existsSync(filePath)) return { count: 0, savedAt: null };
3687
- try {
3688
- const raw = JSON.parse(readFileSync(filePath, "utf-8"));
3689
- const rules = raw["rules"];
3690
- const saved = raw["savedAt"];
3691
- return {
3692
- count: Array.isArray(rules) ? rules.length : 0,
3693
- savedAt: typeof saved === "string" ? saved : null
3694
- };
3695
- } catch {
3696
- return { count: 0, savedAt: null };
3697
- }
3698
- }
3699
- function checkDirAccess(dir) {
3700
- const exists = existsSync(dir);
3701
- if (!exists) return { exists: false, writable: false };
3702
- try {
3703
- accessSync(dir, fsConstants.W_OK);
3704
- return { exists: true, writable: true };
3705
- } catch {
3706
- return { exists: true, writable: false };
3707
- }
3708
- }
3709
- var DISABLED_CHECK = {
3710
- enabled: false,
3711
- dirExists: false,
3712
- dirWritable: false,
3713
- outcomeCount: 0,
3714
- ruleCount: 0,
3715
- rulesLastSaved: null,
3716
- error: null
3717
- };
3718
- function checkLearningPersistence() {
3719
- if (!isPersistenceEnabled()) return DISABLED_CHECK;
3720
- try {
3721
- const { exists: dirExists, writable: dirWritable } = checkDirAccess(LEARNING_DIR);
3722
- const outcomeCount = countJsonlLines(OUTCOMES_FILE);
3723
- const { count: ruleCount, savedAt: rulesLastSaved } = readRulesMetadata(RULES_FILE);
3724
- return {
3725
- enabled: true,
3726
- dirExists,
3727
- dirWritable,
3728
- outcomeCount,
3729
- ruleCount,
3730
- rulesLastSaved,
3731
- error: null
3732
- };
3733
- } catch (error) {
3734
- return {
3735
- enabled: true,
3736
- dirExists: false,
3737
- dirWritable: false,
3738
- outcomeCount: 0,
3739
- ruleCount: 0,
3740
- rulesLastSaved: null,
3741
- error: getErrorMessage(error)
3742
- };
3743
- }
3744
- }
3745
- async function checkSqlite() {
3746
- try {
3747
- await import("better-sqlite3");
3748
- return { available: true, error: null };
3749
- } catch (error) {
3750
- const msg = getErrorMessage(error);
3751
- const isNotFound = msg.includes("Cannot find") || msg.includes("MODULE_NOT_FOUND");
3752
- return {
3753
- available: false,
3754
- error: isNotFound ? "better-sqlite3 not installed \u2014 5 memory backends unavailable" : `better-sqlite3 load error: ${msg}`
3755
- };
3756
- }
3757
- }
3758
- function checkDataDirectory() {
3759
- const rootPath = join(homedir(), ".nexus-agents");
3760
- const rootExists = existsSync(rootPath);
3761
- const subdirectories = DATA_SUBDIRECTORIES.map((name) => {
3762
- const fullPath = join(rootPath, name);
3763
- const exists = existsSync(fullPath);
3764
- return { name, path: fullPath, exists, writable: exists && isWritable(fullPath) };
3765
- });
3766
- return { rootExists, rootPath, subdirectories };
3767
- }
3768
- function isWritable(dirPath) {
3769
- try {
3770
- accessSync(dirPath, fsConstants.W_OK);
3771
- return true;
3772
- } catch {
3773
- return false;
3774
- }
3775
- }
3776
- async function runDoctor() {
3777
- const clis = await Promise.all([
3778
- checkCli("claude"),
3779
- checkCli("gemini"),
3780
- checkCli("codex"),
3781
- checkCli("opencode")
3782
- ]);
3783
- const nodeVersion = checkNodeVersion();
3784
- const apiKeys = checkApiKeys();
3785
- const configFile = checkConfigFile();
3786
- const mcpServerReady = checkMcpServerReady();
3787
- const codexCheck = clis.find((c) => c.name === "codex");
3788
- const mcpClientReady = codexCheck?.installed ?? false;
3789
- const registryAdvisory = buildRegistryAdvisory(clis);
3790
- const learningPersistence = checkLearningPersistence();
3791
- const sqliteCheck = await checkSqlite();
3792
- const dataDirectory = checkDataDirectory();
3793
- const hasAuthMethod = apiKeys.some((k) => k.configured) || clis.some((c) => c.installed && c.authenticated);
3794
- const allHealthy = nodeVersion.supported && hasAuthMethod && mcpServerReady && clis.every((c) => c.installed && c.authenticated && c.versionStatus !== "unsupported");
3795
- return {
3796
- clis,
3797
- nodeVersion,
3798
- apiKeys,
3799
- configFile,
3800
- mcpServerReady,
3801
- mcpClientReady,
3802
- registryAdvisory,
3803
- learningPersistence,
3804
- sqliteCheck,
3805
- dataDirectory,
3806
- allHealthy,
3807
- timestamp: new Date(getTimeProvider().now())
3808
- };
3809
- }
3810
- async function doctorCommand(options = {}) {
3811
- const result = await runDoctor();
3812
- printDoctorResults(result);
3813
- if (options.fix === true) {
3814
- await runDoctorFix(result);
3815
- }
3816
- return result.allHealthy ? 0 : 1;
3817
- }
3818
- async function runDoctorFix(result) {
3819
- const writeLine2 = (text) => {
3820
- process.stdout.write(text + "\n");
3821
- };
3822
- writeLine2("");
3823
- writeLine2("\x1B[1mAuto-fix\x1B[0m");
3824
- writeLine2("\u2500".repeat(40));
3825
- let fixCount = 0;
3826
- if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
3827
- const { runSetup } = await import("./setup-command-ZN7DSP53.js");
3828
- const setupResult = runSetup({
3829
- skipMcp: true,
3830
- skipRules: true,
3831
- skipHooks: true,
3832
- skipConfig: true,
3833
- skipOpencode: true
3834
- });
3835
- if (setupResult.success) {
3836
- writeLine2("\u2713 Created missing data directories");
3837
- fixCount++;
3838
- }
3839
- }
3840
- if (!result.configFile.found) {
3841
- const { runConfigInitSync } = await import("./setup-config-ZOJUDXNP.js");
3842
- const configResult = runConfigInitSync(process.cwd(), false, false);
3843
- if (configResult.success && configResult.created) {
3844
- writeLine2(`\u2713 Generated config: ${configResult.path}`);
3845
- fixCount++;
3846
- }
3847
- }
3848
- if (!result.sqliteCheck.available) {
3849
- writeLine2("");
3850
- writeLine2("\u26A0 better-sqlite3 not installed (manual step required):");
3851
- writeLine2(" npm install -g better-sqlite3");
3852
- }
3853
- if (fixCount > 0) {
3854
- writeLine2("");
3855
- writeLine2(
3856
- `\x1B[32m${String(fixCount)} issue(s) fixed.\x1B[0m Re-run \x1B[1mnexus-agents doctor\x1B[0m to verify.`
3857
- );
3858
- } else {
3859
- writeLine2("No auto-fixable issues found.");
3860
- }
3861
- writeLine2("");
3862
- }
3863
-
3864
- // src/cli/setup-data-dir.ts
3865
- var NEXUS_DATA_DIR = join2(homedir2(), ".nexus-agents");
3866
- var RESTRICTED_DIRS = /* @__PURE__ */ new Set(["auth"]);
3867
- function initDataDirectories(dryRun = false) {
3868
- const created = [];
3869
- const alreadyExisted = [];
3870
- try {
3871
- ensureDir(NEXUS_DATA_DIR, dryRun, created, alreadyExisted);
3872
- for (const subdir of DATA_SUBDIRECTORIES) {
3873
- const mode = RESTRICTED_DIRS.has(subdir) ? 448 : void 0;
3874
- ensureDir(join2(NEXUS_DATA_DIR, subdir), dryRun, created, alreadyExisted, mode);
3875
- }
3876
- return { success: true, rootPath: NEXUS_DATA_DIR, created, alreadyExisted, error: null };
3877
- } catch (error) {
3878
- const msg = error instanceof Error ? error.message : String(error);
3879
- return { success: false, rootPath: NEXUS_DATA_DIR, created, alreadyExisted, error: msg };
3880
- }
3881
- }
3882
- function ensureDir(dirPath, dryRun, created, alreadyExisted, mode) {
3883
- if (existsSync2(dirPath)) {
3884
- alreadyExisted.push(dirPath);
3885
- return;
3886
- }
3887
- if (!dryRun) {
3888
- mkdirSync(dirPath, { recursive: true, ...mode !== void 0 ? { mode } : {} });
3889
- }
3890
- created.push(dirPath);
3891
- }
3892
-
3893
2969
  export {
3894
- VERSION,
3895
2970
  sleep,
3896
2971
  delay,
3897
2972
  withTimeout,
@@ -3900,6 +2975,10 @@ export {
3900
2975
  getTimeoutForTask,
3901
2976
  estimateTaskComplexity2 as estimateTaskComplexity,
3902
2977
  getTimeoutForTaskAuto,
2978
+ calculateBackoffDelay,
2979
+ isRetryableError,
2980
+ categorizeError,
2981
+ executeCliRetryLoop,
3903
2982
  BaseCliAdapter,
3904
2983
  SubprocessCliAdapter,
3905
2984
  asRecord,
@@ -3922,23 +3001,6 @@ export {
3922
3001
  createCliAdapter,
3923
3002
  createAllAdapters,
3924
3003
  isCliAvailable,
3925
- getAvailableClis,
3926
- STOPWORDS,
3927
- tokenize,
3928
- tokenizeFiltered,
3929
- stringifyValue,
3930
- capitalize,
3931
- capitalizeWords,
3932
- capitalizeKebab,
3933
- truncateWithInfo,
3934
- truncateSentence,
3935
- DEFAULT_TASK_TTL_MS,
3936
- clampTaskTtl,
3937
- doctorCommand,
3938
- initDataDirectories,
3939
- createServer,
3940
- connectTransport,
3941
- startStdioServer,
3942
- closeServer
3004
+ getAvailableClis
3943
3005
  };
3944
- //# sourceMappingURL=chunk-RNQRE45M.js.map
3006
+ //# sourceMappingURL=chunk-ZBZJHXRT.js.map