@defai.digital/automatosx 5.6.35 → 5.7.2

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 (106) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +44 -2
  3. package/dist/index.js +1129 -345
  4. package/examples/agents/.automatosx/abilities/accessibility.md +115 -0
  5. package/examples/agents/.automatosx/abilities/api-design.md +159 -0
  6. package/examples/agents/.automatosx/abilities/best-practices.md +102 -0
  7. package/examples/agents/.automatosx/abilities/caching-strategy.md +165 -0
  8. package/examples/agents/.automatosx/abilities/ci-cd.md +61 -0
  9. package/examples/agents/.automatosx/abilities/clean-code.md +398 -0
  10. package/examples/agents/.automatosx/abilities/code-generation.md +95 -0
  11. package/examples/agents/.automatosx/abilities/code-review.md +42 -0
  12. package/examples/agents/.automatosx/abilities/component-architecture.md +112 -0
  13. package/examples/agents/.automatosx/abilities/content-creation.md +97 -0
  14. package/examples/agents/.automatosx/abilities/data-modeling.md +171 -0
  15. package/examples/agents/.automatosx/abilities/data-validation.md +50 -0
  16. package/examples/agents/.automatosx/abilities/db-modeling.md +158 -0
  17. package/examples/agents/.automatosx/abilities/debugging.md +43 -0
  18. package/examples/agents/.automatosx/abilities/dependency-audit.md +60 -0
  19. package/examples/agents/.automatosx/abilities/design-patterns.md +437 -0
  20. package/examples/agents/.automatosx/abilities/design-system-implementation.md +126 -0
  21. package/examples/agents/.automatosx/abilities/documentation.md +54 -0
  22. package/examples/agents/.automatosx/abilities/error-analysis.md +107 -0
  23. package/examples/agents/.automatosx/abilities/etl-pipelines.md +44 -0
  24. package/examples/agents/.automatosx/abilities/feasibility-study.md +20 -0
  25. package/examples/agents/.automatosx/abilities/general-assistance.md +26 -0
  26. package/examples/agents/.automatosx/abilities/idea-evaluation.md +21 -0
  27. package/examples/agents/.automatosx/abilities/infra-as-code.md +57 -0
  28. package/examples/agents/.automatosx/abilities/job-orchestration.md +44 -0
  29. package/examples/agents/.automatosx/abilities/literature-review.md +19 -0
  30. package/examples/agents/.automatosx/abilities/logical-analysis.md +21 -0
  31. package/examples/agents/.automatosx/abilities/longform-report.md +25 -0
  32. package/examples/agents/.automatosx/abilities/mathematical-reasoning.md +170 -0
  33. package/examples/agents/.automatosx/abilities/mission-analysis.md +49 -0
  34. package/examples/agents/.automatosx/abilities/observability.md +61 -0
  35. package/examples/agents/.automatosx/abilities/orbital-mechanics.md +50 -0
  36. package/examples/agents/.automatosx/abilities/our-architecture-decisions.md +180 -0
  37. package/examples/agents/.automatosx/abilities/our-code-review-checklist.md +149 -0
  38. package/examples/agents/.automatosx/abilities/our-coding-standards.md +270 -0
  39. package/examples/agents/.automatosx/abilities/our-project-structure.md +183 -0
  40. package/examples/agents/.automatosx/abilities/performance-analysis.md +56 -0
  41. package/examples/agents/.automatosx/abilities/performance.md +80 -0
  42. package/examples/agents/.automatosx/abilities/problem-solving.md +50 -0
  43. package/examples/agents/.automatosx/abilities/propulsion-systems.md +50 -0
  44. package/examples/agents/.automatosx/abilities/quantum-algorithm-design.md +54 -0
  45. package/examples/agents/.automatosx/abilities/quantum-error-correction.md +56 -0
  46. package/examples/agents/.automatosx/abilities/quantum-frameworks-transpilation.md +53 -0
  47. package/examples/agents/.automatosx/abilities/quantum-noise-modeling.md +58 -0
  48. package/examples/agents/.automatosx/abilities/refactoring.md +223 -0
  49. package/examples/agents/.automatosx/abilities/release-strategy.md +58 -0
  50. package/examples/agents/.automatosx/abilities/risk-assessment.md +19 -0
  51. package/examples/agents/.automatosx/abilities/secrets-policy.md +61 -0
  52. package/examples/agents/.automatosx/abilities/secure-coding-review.md +51 -0
  53. package/examples/agents/.automatosx/abilities/security-audit.md +65 -0
  54. package/examples/agents/.automatosx/abilities/software-architecture.md +394 -0
  55. package/examples/agents/.automatosx/abilities/solid-principles.md +341 -0
  56. package/examples/agents/.automatosx/abilities/sql-optimization.md +84 -0
  57. package/examples/agents/.automatosx/abilities/state-management.md +96 -0
  58. package/examples/agents/.automatosx/abilities/task-planning.md +65 -0
  59. package/examples/agents/.automatosx/abilities/technical-writing.md +77 -0
  60. package/examples/agents/.automatosx/abilities/telemetry-diagnostics.md +51 -0
  61. package/examples/agents/.automatosx/abilities/testing.md +47 -0
  62. package/examples/agents/.automatosx/abilities/threat-modeling.md +49 -0
  63. package/examples/agents/.automatosx/abilities/troubleshooting.md +80 -0
  64. package/examples/agents/.automatosx/agents/aerospace-scientist.yaml +75 -0
  65. package/examples/agents/.automatosx/agents/backend.yaml +152 -0
  66. package/examples/agents/.automatosx/agents/ceo.yaml +63 -0
  67. package/examples/agents/.automatosx/agents/creative-marketer.yaml +121 -0
  68. package/examples/agents/.automatosx/agents/cto.yaml +72 -0
  69. package/examples/agents/.automatosx/agents/data-scientist.yaml +124 -0
  70. package/examples/agents/.automatosx/agents/data.yaml +77 -0
  71. package/examples/agents/.automatosx/agents/design.yaml +74 -0
  72. package/examples/agents/.automatosx/agents/devops.yaml +89 -0
  73. package/examples/agents/.automatosx/agents/frontend.yaml +139 -0
  74. package/examples/agents/.automatosx/agents/fullstack.yaml +151 -0
  75. package/examples/agents/.automatosx/agents/mobile.yaml +161 -0
  76. package/examples/agents/.automatosx/agents/product.yaml +72 -0
  77. package/examples/agents/.automatosx/agents/quality.yaml +79 -0
  78. package/examples/agents/.automatosx/agents/quantum-engineer.yaml +75 -0
  79. package/examples/agents/.automatosx/agents/researcher.yaml +71 -0
  80. package/examples/agents/.automatosx/agents/security.yaml +86 -0
  81. package/examples/agents/.automatosx/agents/stan.yaml +189 -0
  82. package/examples/agents/.automatosx/agents/writer.yaml +78 -0
  83. package/examples/agents/.automatosx/teams/business.yaml +56 -0
  84. package/examples/agents/.automatosx/teams/core.yaml +60 -0
  85. package/examples/agents/.automatosx/teams/design.yaml +58 -0
  86. package/examples/agents/.automatosx/teams/engineering.yaml +69 -0
  87. package/examples/agents/.automatosx/teams/research.yaml +56 -0
  88. package/examples/agents/.automatosx/templates/analyst.yaml +60 -0
  89. package/examples/agents/.automatosx/templates/assistant.yaml +48 -0
  90. package/examples/agents/.automatosx/templates/basic-agent.yaml +28 -0
  91. package/examples/agents/.automatosx/templates/code-reviewer.yaml +52 -0
  92. package/examples/agents/.automatosx/templates/debugger.yaml +63 -0
  93. package/examples/agents/.automatosx/templates/designer.yaml +69 -0
  94. package/examples/agents/.automatosx/templates/developer.yaml +60 -0
  95. package/examples/agents/.automatosx/templates/fullstack-developer.yaml +395 -0
  96. package/examples/agents/.automatosx/templates/qa-specialist.yaml +71 -0
  97. package/examples/agents/.claude/commands/ax-agent.md +37 -0
  98. package/examples/agents/.claude/commands/ax-clear.md +22 -0
  99. package/examples/agents/.claude/commands/ax-init.md +25 -0
  100. package/examples/agents/.claude/commands/ax-list.md +19 -0
  101. package/examples/agents/.claude/commands/ax-memory.md +25 -0
  102. package/examples/agents/.claude/commands/ax-status.md +24 -0
  103. package/examples/agents/.claude/commands/ax-update.md +28 -0
  104. package/examples/agents/.claude/mcp/automatosx.json +244 -0
  105. package/examples/agents/CLAUDE.md +262 -0
  106. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import * as actualFS from 'fs';
7
7
  import { realpathSync as realpathSync$1, access, realpath, existsSync, readFileSync, constants, writeFileSync, readlinkSync, readdirSync, readdir as readdir$1, lstatSync, promises, mkdirSync } from 'fs';
8
8
  import os2, { homedir, cpus } from 'os';
9
9
  import { findUp } from 'find-up';
10
+ import { EventEmitter } from 'events';
10
11
  import { exec, spawnSync } from 'child_process';
11
12
  import yargs from 'yargs';
12
13
  import { hideBin } from 'yargs/helpers';
@@ -17,7 +18,6 @@ import chalk27 from 'chalk';
17
18
  import Table from 'cli-table3';
18
19
  import * as sqliteVec from 'sqlite-vec';
19
20
  import { Mutex } from 'async-mutex';
20
- import { EventEmitter } from 'events';
21
21
  import ora from 'ora';
22
22
  import * as readline from 'readline';
23
23
  import { promisify } from 'util';
@@ -490,6 +490,233 @@ var init_path_utils = __esm({
490
490
  }
491
491
  });
492
492
 
493
+ // src/utils/errors.ts
494
+ function toBaseError(error) {
495
+ if (error instanceof BaseError) {
496
+ return error;
497
+ }
498
+ if (error instanceof Error) {
499
+ return new BaseError(
500
+ error.message,
501
+ "E9999" /* UNKNOWN_ERROR */,
502
+ ["Check error details for more information"],
503
+ { originalError: error.name, stack: error.stack }
504
+ );
505
+ }
506
+ return new BaseError(
507
+ String(error),
508
+ "E9999" /* UNKNOWN_ERROR */,
509
+ ["An unexpected error occurred"]
510
+ );
511
+ }
512
+ var BaseError, ConfigError, ProviderError;
513
+ var init_errors = __esm({
514
+ "src/utils/errors.ts"() {
515
+ init_esm_shims();
516
+ BaseError = class extends Error {
517
+ code;
518
+ suggestions;
519
+ context;
520
+ isOperational;
521
+ constructor(message, code, suggestions = [], context, isOperational = true) {
522
+ super(message);
523
+ this.name = this.constructor.name;
524
+ this.code = code;
525
+ this.suggestions = suggestions;
526
+ this.context = context;
527
+ this.isOperational = isOperational;
528
+ Error.captureStackTrace(this, this.constructor);
529
+ }
530
+ /**
531
+ * Get formatted error message for display
532
+ */
533
+ getFormattedMessage() {
534
+ let formatted = `[${this.code}] ${this.message}`;
535
+ if (this.suggestions.length > 0) {
536
+ formatted += "\n\nSuggestions:";
537
+ this.suggestions.forEach((suggestion, i) => {
538
+ formatted += `
539
+ ${i + 1}. ${suggestion}`;
540
+ });
541
+ }
542
+ return formatted;
543
+ }
544
+ /**
545
+ * Get error details as object
546
+ */
547
+ toJSON() {
548
+ return {
549
+ name: this.name,
550
+ code: this.code,
551
+ message: this.message,
552
+ suggestions: this.suggestions,
553
+ context: this.context,
554
+ isOperational: this.isOperational,
555
+ stack: this.stack
556
+ };
557
+ }
558
+ };
559
+ ConfigError = class _ConfigError extends BaseError {
560
+ constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
561
+ super(message, code, suggestions, context);
562
+ }
563
+ static notFound(path6) {
564
+ return new _ConfigError(
565
+ `Configuration file not found: ${path6}`,
566
+ "E1000" /* CONFIG_NOT_FOUND */,
567
+ [
568
+ 'Run "automatosx init" to create a new configuration',
569
+ "Specify a custom config path with --config option",
570
+ "Check that you are in a valid AutomatosX project directory"
571
+ ],
572
+ { path: path6 }
573
+ );
574
+ }
575
+ static invalid(reason, context) {
576
+ return new _ConfigError(
577
+ `Invalid configuration: ${reason}`,
578
+ "E1001" /* CONFIG_INVALID */,
579
+ [
580
+ "Check your automatosx.config.json for syntax errors",
581
+ "Validate against the schema in documentation",
582
+ 'Reset to default with "automatosx init --force"'
583
+ ],
584
+ context
585
+ );
586
+ }
587
+ static parseError(error, path6) {
588
+ return new _ConfigError(
589
+ `Failed to parse configuration: ${error.message}`,
590
+ "E1002" /* CONFIG_PARSE_ERROR */,
591
+ [
592
+ "Check JSON syntax in your config file",
593
+ "Use a JSON validator to find syntax errors",
594
+ 'Reset to default with "automatosx init --force"'
595
+ ],
596
+ { path: path6, originalError: error.message }
597
+ );
598
+ }
599
+ };
600
+ ProviderError = class _ProviderError extends BaseError {
601
+ constructor(message, code = "E1301" /* PROVIDER_UNAVAILABLE */, suggestions = [], context) {
602
+ super(message, code, suggestions, context);
603
+ }
604
+ static notFound(providerName) {
605
+ return new _ProviderError(
606
+ `Provider "${providerName}" not found`,
607
+ "E1300" /* PROVIDER_NOT_FOUND */,
608
+ [
609
+ 'Check available providers with "automatosx list providers"',
610
+ "Verify provider is enabled in automatosx.config.json",
611
+ "Install the provider CLI if not already installed"
612
+ ],
613
+ { providerName }
614
+ );
615
+ }
616
+ static unavailable(providerName, reason) {
617
+ const msg = reason ? `Provider "${providerName}" unavailable: ${reason}` : `Provider "${providerName}" is unavailable`;
618
+ return new _ProviderError(
619
+ msg,
620
+ "E1301" /* PROVIDER_UNAVAILABLE */,
621
+ [
622
+ "Check that the provider CLI is installed and in your PATH",
623
+ "Verify provider configuration in automatosx.config.json",
624
+ "Try running the provider CLI directly to diagnose issues",
625
+ 'Check system status with "automatosx status --verbose"'
626
+ ],
627
+ { providerName, reason }
628
+ );
629
+ }
630
+ static timeout(providerName, timeoutMs) {
631
+ return new _ProviderError(
632
+ `Provider "${providerName}" timed out after ${timeoutMs}ms`,
633
+ "E1302" /* PROVIDER_TIMEOUT */,
634
+ [
635
+ "Increase timeout in automatosx.config.json",
636
+ "Try a simpler prompt or reduce context size",
637
+ "Check your network connection",
638
+ "Verify the provider service is responsive"
639
+ ],
640
+ { providerName, timeoutMs }
641
+ );
642
+ }
643
+ static noAvailableProviders() {
644
+ return new _ProviderError(
645
+ "No AI providers are available",
646
+ "E1306" /* PROVIDER_NO_AVAILABLE */,
647
+ [
648
+ "Install at least one provider CLI (Claude or Gemini)",
649
+ "Enable providers in automatosx.config.json",
650
+ 'Check provider status with "automatosx status"',
651
+ "Verify provider CLIs are in your PATH"
652
+ ]
653
+ );
654
+ }
655
+ static executionError(providerName, error) {
656
+ return new _ProviderError(
657
+ `Provider "${providerName}" execution failed: ${error.message}`,
658
+ "E1305" /* PROVIDER_EXEC_ERROR */,
659
+ [
660
+ "Check that the provider CLI is installed correctly",
661
+ "Verify you have necessary API keys configured",
662
+ "Try running the provider CLI directly to diagnose",
663
+ "Check error logs with --debug flag"
664
+ ],
665
+ { providerName, originalError: error.message }
666
+ );
667
+ }
668
+ /**
669
+ * Create rate limit error with detailed context
670
+ * v5.7.0: Provider limit detection and rotation
671
+ */
672
+ static rateLimit(providerName, limitWindow, resetAtMs, rawMessage, retryAfterSeconds) {
673
+ const resetDate = new Date(resetAtMs);
674
+ const hoursUntilReset = Math.ceil((resetAtMs - Date.now()) / (1e3 * 60 * 60));
675
+ const timeDesc = hoursUntilReset < 1 ? "less than 1 hour" : hoursUntilReset === 1 ? "1 hour" : `${hoursUntilReset} hours`;
676
+ return new _ProviderError(
677
+ `Provider "${providerName}" has hit its ${limitWindow} usage limit. Resets in ${timeDesc} at ${resetDate.toISOString()}`,
678
+ "E1303" /* PROVIDER_RATE_LIMIT */,
679
+ [
680
+ "AutomatosX will automatically switch to the next available provider",
681
+ `Wait until ${resetDate.toLocaleString()} for "${providerName}" to become available again`,
682
+ 'Check current provider status with "ax provider limits"',
683
+ 'Use "ax provider use <name>" to manually select a different provider'
684
+ ],
685
+ {
686
+ providerName,
687
+ limitWindow,
688
+ resetAtMs,
689
+ rawMessage,
690
+ retryAfterSeconds
691
+ }
692
+ );
693
+ }
694
+ /**
695
+ * Create error for all providers exhausted
696
+ * v5.7.0: Provider limit detection and rotation
697
+ */
698
+ static allProvidersLimited(limitedProviders, soonestResetMs) {
699
+ const soonestResetDate = new Date(soonestResetMs);
700
+ const hoursUntilReset = Math.ceil((soonestResetMs - Date.now()) / (1e3 * 60 * 60));
701
+ return new _ProviderError(
702
+ `All providers have hit usage limits. Next provider resets in ${hoursUntilReset} hour(s) at ${soonestResetDate.toISOString()}`,
703
+ "E1306" /* PROVIDER_NO_AVAILABLE */,
704
+ [
705
+ `Wait until ${soonestResetDate.toLocaleString()} for providers to reset`,
706
+ 'Check provider limits with "ax provider limits"',
707
+ 'Use "ax provider use <name> --force" to override and try anyway',
708
+ "Consider spreading workload across different time periods"
709
+ ],
710
+ {
711
+ limitedProviders,
712
+ soonestResetMs
713
+ }
714
+ );
715
+ }
716
+ };
717
+ }
718
+ });
719
+
493
720
  // src/core/cache.ts
494
721
  var TTLCache, ProviderResponseCache;
495
722
  var init_cache = __esm({
@@ -868,8 +1095,8 @@ __export(path_resolver_exports, {
868
1095
  PathResolver: () => PathResolver,
869
1096
  detectProjectRoot: () => detectProjectRoot
870
1097
  });
871
- function isWindowsPath(path5) {
872
- return /^[a-zA-Z]:[/\\]/.test(path5);
1098
+ function isWindowsPath(path6) {
1099
+ return /^[a-zA-Z]:[/\\]/.test(path6);
873
1100
  }
874
1101
  async function detectProjectRoot(startDir = process.cwd()) {
875
1102
  const gitDir = await findUp(".git", {
@@ -972,8 +1199,8 @@ var init_path_resolver = __esm({
972
1199
  /**
973
1200
  * Validate path is within allowed base directory
974
1201
  */
975
- validatePath(path5, baseDir) {
976
- const normalized = normalizePath(resolvePath(path5));
1202
+ validatePath(path6, baseDir) {
1203
+ const normalized = normalizePath(resolvePath(path6));
977
1204
  const base = normalizePath(resolvePath(baseDir));
978
1205
  const separator = "/";
979
1206
  const pathWithSep = normalized + separator;
@@ -983,15 +1210,15 @@ var init_path_resolver = __esm({
983
1210
  /**
984
1211
  * Check if path is within allowed boundaries
985
1212
  */
986
- isPathAllowed(path5) {
987
- const boundary = this.checkBoundaries(path5);
1213
+ isPathAllowed(path6) {
1214
+ const boundary = this.checkBoundaries(path6);
988
1215
  return boundary === "agent_workspace" || boundary === "user_project";
989
1216
  }
990
1217
  /**
991
1218
  * Check which boundary a path belongs to
992
1219
  */
993
- checkBoundaries(path5) {
994
- const normalized = resolvePath(path5);
1220
+ checkBoundaries(path6) {
1221
+ const normalized = resolvePath(path6);
995
1222
  if (this.validatePath(normalized, this.config.agentWorkspace)) {
996
1223
  return "agent_workspace";
997
1224
  }
@@ -1009,15 +1236,15 @@ var init_path_resolver = __esm({
1009
1236
  /**
1010
1237
  * Get relative path from project root
1011
1238
  */
1012
- getRelativeToProject(path5) {
1013
- const normalized = resolvePath(path5);
1239
+ getRelativeToProject(path6) {
1240
+ const normalized = resolvePath(path6);
1014
1241
  return normalizePath(getRelativePath(this.config.projectDir, normalized));
1015
1242
  }
1016
1243
  /**
1017
1244
  * Get relative path from working directory
1018
1245
  */
1019
- getRelativeToWorking(path5) {
1020
- const normalized = resolvePath(path5);
1246
+ getRelativeToWorking(path6) {
1247
+ const normalized = resolvePath(path6);
1021
1248
  return normalizePath(getRelativePath(this.config.workingDir, normalized));
1022
1249
  }
1023
1250
  /**
@@ -1036,11 +1263,11 @@ var init_path_resolver = __esm({
1036
1263
  * Validate path is within project boundaries
1037
1264
  * @throws PathError if outside project
1038
1265
  */
1039
- validateInProject(path5) {
1040
- const boundary = this.checkBoundaries(path5);
1266
+ validateInProject(path6) {
1267
+ const boundary = this.checkBoundaries(path6);
1041
1268
  if (boundary === "outside_boundaries" || boundary === "system_restricted") {
1042
1269
  throw new PathError("Path outside project directory", {
1043
- path: path5,
1270
+ path: path6,
1044
1271
  projectDir: this.config.projectDir,
1045
1272
  boundary
1046
1273
  });
@@ -1064,6 +1291,347 @@ var init_path_resolver = __esm({
1064
1291
  };
1065
1292
  }
1066
1293
  });
1294
+ function getProviderLimitManager(stateDirectory) {
1295
+ return ProviderLimitManager.getInstance(stateDirectory);
1296
+ }
1297
+ var ProviderLimitManager;
1298
+ var init_provider_limit_manager = __esm({
1299
+ "src/core/provider-limit-manager.ts"() {
1300
+ init_esm_shims();
1301
+ init_logger();
1302
+ ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
1303
+ static instance = null;
1304
+ stateFilePath;
1305
+ providerStates;
1306
+ manualOverride;
1307
+ providerConfigs;
1308
+ initialized = false;
1309
+ // Performance tracking
1310
+ metrics = {
1311
+ checksPerformed: 0,
1312
+ limitsRecorded: 0,
1313
+ limitsCleared: 0,
1314
+ cacheHits: 0
1315
+ };
1316
+ constructor(stateDirectory) {
1317
+ super();
1318
+ this.stateFilePath = path2.join(stateDirectory, "provider-limits.json");
1319
+ this.providerStates = /* @__PURE__ */ new Map();
1320
+ this.providerConfigs = /* @__PURE__ */ new Map();
1321
+ }
1322
+ /**
1323
+ * Get singleton instance
1324
+ */
1325
+ static getInstance(stateDirectory = ".automatosx/state") {
1326
+ if (!_ProviderLimitManager.instance) {
1327
+ _ProviderLimitManager.instance = new _ProviderLimitManager(stateDirectory);
1328
+ }
1329
+ return _ProviderLimitManager.instance;
1330
+ }
1331
+ /**
1332
+ * Reset singleton instance (for testing)
1333
+ */
1334
+ static resetInstance() {
1335
+ _ProviderLimitManager.instance = null;
1336
+ }
1337
+ /**
1338
+ * Initialize manager: load state from disk
1339
+ */
1340
+ async initialize() {
1341
+ if (this.initialized) {
1342
+ return;
1343
+ }
1344
+ try {
1345
+ await this.loadState();
1346
+ this.initialized = true;
1347
+ logger.debug("ProviderLimitManager initialized", {
1348
+ stateFile: this.stateFilePath,
1349
+ trackedProviders: this.providerStates.size
1350
+ });
1351
+ } catch (error) {
1352
+ logger.warn("Failed to initialize ProviderLimitManager", {
1353
+ error: error.message
1354
+ });
1355
+ this.initialized = true;
1356
+ }
1357
+ }
1358
+ /**
1359
+ * Register provider limit tracking configuration
1360
+ */
1361
+ registerProvider(providerName, config) {
1362
+ this.providerConfigs.set(providerName, config);
1363
+ logger.debug("Provider limit tracking registered", {
1364
+ provider: providerName,
1365
+ window: config.window,
1366
+ enabled: config.enabled
1367
+ });
1368
+ }
1369
+ /**
1370
+ * Record a limit hit for a provider
1371
+ */
1372
+ async recordLimitHit(providerName, limitWindow, resetAtMs, metadata) {
1373
+ this.metrics.limitsRecorded++;
1374
+ const state = {
1375
+ status: "limited",
1376
+ window: limitWindow,
1377
+ detectedAtMs: Date.now(),
1378
+ resetAtMs,
1379
+ reason: metadata?.reason,
1380
+ rawMessage: metadata?.rawMessage,
1381
+ retryAfterSeconds: metadata?.retryAfterSeconds,
1382
+ manualHold: false
1383
+ };
1384
+ this.providerStates.set(providerName, state);
1385
+ await this.saveState();
1386
+ this.emit("limit-hit", {
1387
+ providerName,
1388
+ limitWindow,
1389
+ resetAtMs,
1390
+ reason: metadata?.reason
1391
+ });
1392
+ logger.warn("Provider limit hit recorded", {
1393
+ provider: providerName,
1394
+ window: limitWindow,
1395
+ resetAt: new Date(resetAtMs).toISOString(),
1396
+ reason: metadata?.reason
1397
+ });
1398
+ }
1399
+ /**
1400
+ * Check if a provider is currently limited (O(1) operation)
1401
+ */
1402
+ isProviderLimited(providerName, now = Date.now()) {
1403
+ this.metrics.checksPerformed++;
1404
+ if (this.manualOverride && this.manualOverride.provider === providerName) {
1405
+ return { isLimited: false };
1406
+ }
1407
+ const state = this.providerStates.get(providerName);
1408
+ if (!state || state.status !== "limited") {
1409
+ this.metrics.cacheHits++;
1410
+ return { isLimited: false };
1411
+ }
1412
+ if (now >= state.resetAtMs) {
1413
+ void this.clearLimit(providerName, "expired");
1414
+ return { isLimited: false };
1415
+ }
1416
+ const remainingMs = state.resetAtMs - now;
1417
+ return {
1418
+ isLimited: true,
1419
+ remainingMs,
1420
+ resetAtMs: state.resetAtMs,
1421
+ reason: state.reason
1422
+ };
1423
+ }
1424
+ /**
1425
+ * Clear limit for a provider
1426
+ */
1427
+ async clearLimit(providerName, reason = "manual") {
1428
+ const state = this.providerStates.get(providerName);
1429
+ if (!state || state.status !== "limited") {
1430
+ return;
1431
+ }
1432
+ this.metrics.limitsCleared++;
1433
+ this.providerStates.delete(providerName);
1434
+ await this.saveState();
1435
+ this.emit("limit-cleared", {
1436
+ providerName,
1437
+ reason
1438
+ });
1439
+ logger.info("Provider limit cleared", {
1440
+ provider: providerName,
1441
+ reason
1442
+ });
1443
+ }
1444
+ /**
1445
+ * Refresh expired limits (background task)
1446
+ */
1447
+ async refreshExpired() {
1448
+ const now = Date.now();
1449
+ const expiredProviders = [];
1450
+ for (const [providerName, state] of this.providerStates.entries()) {
1451
+ if (state.status === "limited" && now >= state.resetAtMs) {
1452
+ expiredProviders.push(providerName);
1453
+ await this.clearLimit(providerName, "expired");
1454
+ }
1455
+ }
1456
+ if (expiredProviders.length > 0) {
1457
+ logger.debug("Expired provider limits refreshed", {
1458
+ providers: expiredProviders,
1459
+ count: expiredProviders.length
1460
+ });
1461
+ }
1462
+ return expiredProviders;
1463
+ }
1464
+ /**
1465
+ * Set manual override for a specific provider
1466
+ */
1467
+ async setManualOverride(provider, expiresAtMs) {
1468
+ this.manualOverride = {
1469
+ provider,
1470
+ setAtMs: Date.now(),
1471
+ expiresAtMs
1472
+ };
1473
+ await this.saveState();
1474
+ this.emit("override-set", {
1475
+ provider,
1476
+ expiresAtMs
1477
+ });
1478
+ logger.info("Manual provider override set", {
1479
+ provider,
1480
+ expiresAt: expiresAtMs ? new Date(expiresAtMs).toISOString() : "never"
1481
+ });
1482
+ }
1483
+ /**
1484
+ * Clear manual override
1485
+ */
1486
+ async clearManualOverride() {
1487
+ if (!this.manualOverride) {
1488
+ return;
1489
+ }
1490
+ const provider = this.manualOverride.provider;
1491
+ this.manualOverride = void 0;
1492
+ await this.saveState();
1493
+ this.emit("override-cleared", { provider });
1494
+ logger.info("Manual provider override cleared", { provider });
1495
+ }
1496
+ /**
1497
+ * Get current manual override
1498
+ */
1499
+ getManualOverride() {
1500
+ if (this.manualOverride && this.manualOverride.expiresAtMs) {
1501
+ if (Date.now() >= this.manualOverride.expiresAtMs) {
1502
+ void this.clearManualOverride();
1503
+ return void 0;
1504
+ }
1505
+ }
1506
+ return this.manualOverride;
1507
+ }
1508
+ /**
1509
+ * Get all provider states (for status commands)
1510
+ */
1511
+ getAllStates() {
1512
+ return new Map(this.providerStates);
1513
+ }
1514
+ /**
1515
+ * Get next reset time across all providers
1516
+ */
1517
+ getNextReset() {
1518
+ let soonest = null;
1519
+ for (const [providerName, state] of this.providerStates.entries()) {
1520
+ if (state.status === "limited") {
1521
+ if (!soonest || state.resetAtMs < soonest.resetAtMs) {
1522
+ soonest = { providerName, resetAtMs: state.resetAtMs };
1523
+ }
1524
+ }
1525
+ }
1526
+ return soonest;
1527
+ }
1528
+ /**
1529
+ * Calculate reset time for a provider based on its configuration
1530
+ */
1531
+ calculateResetTime(providerName, limitWindow, retryAfterSeconds) {
1532
+ const config = this.providerConfigs.get(providerName);
1533
+ const now = Date.now();
1534
+ if (retryAfterSeconds) {
1535
+ return now + retryAfterSeconds * 1e3;
1536
+ }
1537
+ const resetHourUtc = config?.resetHourUtc ?? 0;
1538
+ if (limitWindow === "daily") {
1539
+ const tomorrow = /* @__PURE__ */ new Date();
1540
+ tomorrow.setUTCDate(tomorrow.getUTCDate() + 1);
1541
+ tomorrow.setUTCHours(resetHourUtc, 0, 0, 0);
1542
+ return tomorrow.getTime();
1543
+ }
1544
+ if (limitWindow === "weekly") {
1545
+ const nextWeek = /* @__PURE__ */ new Date();
1546
+ nextWeek.setUTCDate(nextWeek.getUTCDate() + 7);
1547
+ nextWeek.setUTCHours(resetHourUtc, 0, 0, 0);
1548
+ return nextWeek.getTime();
1549
+ }
1550
+ if (limitWindow === "custom" && config?.customResetMs) {
1551
+ return now + config.customResetMs;
1552
+ }
1553
+ return now + 24 * 60 * 60 * 1e3;
1554
+ }
1555
+ /**
1556
+ * Get performance metrics
1557
+ */
1558
+ getMetrics() {
1559
+ return { ...this.metrics };
1560
+ }
1561
+ /**
1562
+ * Serialize state for JSON export
1563
+ */
1564
+ serialize() {
1565
+ const providers = {};
1566
+ for (const [name, state] of this.providerStates.entries()) {
1567
+ providers[name] = state;
1568
+ }
1569
+ return {
1570
+ schemaVersion: 1,
1571
+ providers,
1572
+ manualOverride: this.manualOverride
1573
+ };
1574
+ }
1575
+ // ========================================
1576
+ // Private Methods: Persistence
1577
+ // ========================================
1578
+ /**
1579
+ * Load state from disk
1580
+ */
1581
+ async loadState() {
1582
+ try {
1583
+ const data = await promises.readFile(this.stateFilePath, "utf-8");
1584
+ const parsed = JSON.parse(data);
1585
+ if (parsed.schemaVersion !== 1) {
1586
+ logger.warn("Unsupported state schema version", {
1587
+ version: parsed.schemaVersion
1588
+ });
1589
+ return;
1590
+ }
1591
+ for (const [name, state] of Object.entries(parsed.providers)) {
1592
+ this.providerStates.set(name, state);
1593
+ }
1594
+ if (parsed.manualOverride) {
1595
+ this.manualOverride = parsed.manualOverride;
1596
+ }
1597
+ logger.debug("Provider limit state loaded", {
1598
+ providers: this.providerStates.size,
1599
+ hasOverride: !!this.manualOverride
1600
+ });
1601
+ } catch (error) {
1602
+ if (error.code === "ENOENT") {
1603
+ logger.debug("No existing provider limit state found (new installation)");
1604
+ } else {
1605
+ throw error;
1606
+ }
1607
+ }
1608
+ }
1609
+ /**
1610
+ * Save state to disk (atomic write)
1611
+ */
1612
+ async saveState() {
1613
+ try {
1614
+ const dir = path2.dirname(this.stateFilePath);
1615
+ await promises.mkdir(dir, { recursive: true });
1616
+ const state = this.serialize();
1617
+ const json = JSON.stringify(state, null, 2);
1618
+ const tempPath = `${this.stateFilePath}.tmp`;
1619
+ await promises.writeFile(tempPath, json, "utf-8");
1620
+ await promises.rename(tempPath, this.stateFilePath);
1621
+ logger.debug("Provider limit state saved", {
1622
+ file: this.stateFilePath,
1623
+ providers: this.providerStates.size
1624
+ });
1625
+ } catch (error) {
1626
+ logger.error("Failed to save provider limit state", {
1627
+ error: error.message,
1628
+ file: this.stateFilePath
1629
+ });
1630
+ }
1631
+ }
1632
+ };
1633
+ }
1634
+ });
1067
1635
 
1068
1636
  // src/providers/retry-errors.ts
1069
1637
  function containsErrorPattern(message, patterns) {
@@ -1189,10 +1757,10 @@ function findOnPathUnix(cmdBase) {
1189
1757
  try {
1190
1758
  const which = spawnSync("which", [cmdBase], { timeout: 3e3 });
1191
1759
  if (which.status === 0) {
1192
- const path5 = which.stdout.toString().trim();
1193
- if (path5) {
1194
- logger.debug("Found via which", { cmdBase, path: path5 });
1195
- return { found: true, path: path5 };
1760
+ const path6 = which.stdout.toString().trim();
1761
+ if (path6) {
1762
+ logger.debug("Found via which", { cmdBase, path: path6 });
1763
+ return { found: true, path: path6 };
1196
1764
  }
1197
1765
  }
1198
1766
  } catch (error) {
@@ -1314,6 +1882,25 @@ var init_provider_cache = __esm({
1314
1882
  });
1315
1883
  return entry.available;
1316
1884
  }
1885
+ /**
1886
+ * Get cache entry with metadata (age, timestamp).
1887
+ * v5.7.2: Added for enhanced metrics tracking.
1888
+ */
1889
+ getWithMetadata(providerName, ttl = 3e4) {
1890
+ const entry = this.cache.get(providerName);
1891
+ if (!entry) {
1892
+ return void 0;
1893
+ }
1894
+ const age = Date.now() - entry.timestamp;
1895
+ if (age >= ttl) {
1896
+ this.cache.delete(providerName);
1897
+ return void 0;
1898
+ }
1899
+ return {
1900
+ available: entry.available,
1901
+ age
1902
+ };
1903
+ }
1317
1904
  /**
1318
1905
  * Set availability status for a provider.
1319
1906
  *
@@ -1531,19 +2118,21 @@ var init_base_provider = __esm({
1531
2118
  }
1532
2119
  const ttl = this.calculateAdaptiveTTL();
1533
2120
  try {
1534
- const sharedCached = providerCache.get(this.config.name, ttl);
2121
+ const sharedCached = providerCache.getWithMetadata(this.config.name, ttl);
1535
2122
  if (sharedCached !== void 0) {
1536
2123
  this.cacheMetrics.availabilityHits++;
1537
2124
  this.availabilityCacheMetrics.lastHit = Date.now();
2125
+ this.availabilityCacheMetrics.totalAge += sharedCached.age;
1538
2126
  this.availabilityCacheMetrics.hitCount++;
1539
2127
  logger.debug(`Using shared cache for ${this.config.name}`, {
1540
2128
  provider: this.config.name,
1541
- available: sharedCached,
2129
+ available: sharedCached.available,
1542
2130
  source: "shared-cache",
2131
+ cacheAge: sharedCached.age,
1543
2132
  cacheTTL: ttl,
1544
2133
  cacheHits: this.cacheMetrics.availabilityHits
1545
2134
  });
1546
- return sharedCached;
2135
+ return sharedCached.available;
1547
2136
  }
1548
2137
  } catch (error) {
1549
2138
  logger.warn(`Failed to read shared cache for ${this.config.name}, falling back to instance cache`, {
@@ -1712,25 +2301,25 @@ var init_base_provider = __esm({
1712
2301
  * @param path File path to check
1713
2302
  * @returns true if path exists and is valid
1714
2303
  */
1715
- checkPathExists(path5) {
2304
+ checkPathExists(path6) {
1716
2305
  try {
1717
- if (path5.includes("..")) {
1718
- logger.warn("Path traversal pattern detected (..)", { path: path5 });
2306
+ if (path6.includes("..")) {
2307
+ logger.warn("Path traversal pattern detected (..)", { path: path6 });
1719
2308
  return false;
1720
2309
  }
1721
- if (path5.startsWith("~") || path5.includes("~")) {
1722
- logger.warn("Home directory shortcut detected (~)", { path: path5 });
2310
+ if (path6.startsWith("~") || path6.includes("~")) {
2311
+ logger.warn("Home directory shortcut detected (~)", { path: path6 });
1723
2312
  return false;
1724
2313
  }
1725
2314
  const DANGEROUS_CHARS = /[;|&$`<>{}[\]'"\\]/;
1726
- if (DANGEROUS_CHARS.test(path5)) {
1727
- logger.warn("Dangerous shell characters detected in path", { path: path5 });
2315
+ if (DANGEROUS_CHARS.test(path6)) {
2316
+ logger.warn("Dangerous shell characters detected in path", { path: path6 });
1728
2317
  return false;
1729
2318
  }
1730
- return existsSync(path5);
2319
+ return existsSync(path6);
1731
2320
  } catch (error) {
1732
2321
  logger.debug(`Error checking path existence`, {
1733
- path: path5,
2322
+ path: path6,
1734
2323
  error: error.message
1735
2324
  });
1736
2325
  return false;
@@ -2444,6 +3033,8 @@ var init_claude_provider = __esm({
2444
3033
  init_retry_errors();
2445
3034
  init_environment();
2446
3035
  init_logger();
3036
+ init_errors();
3037
+ init_provider_limit_manager();
2447
3038
  ClaudeProvider = class extends BaseProvider {
2448
3039
  constructor(config) {
2449
3040
  super(config);
@@ -2659,11 +3250,8 @@ Details: ${errorMsg}`
2659
3250
  `Authentication failed: Please check your Claude API credentials.
2660
3251
  Details: ${errorMsg}`
2661
3252
  ));
2662
- } else if (errorMsg.toLowerCase().includes("rate limit") || errorMsg.toLowerCase().includes("quota")) {
2663
- reject(new Error(
2664
- `Rate limit exceeded: Please wait a moment and try again.
2665
- Details: ${errorMsg}`
2666
- ));
3253
+ } else if (this.isRateLimitError(errorMsg)) {
3254
+ reject(this.createRateLimitError(errorMsg));
2667
3255
  } else {
2668
3256
  reject(new Error(`Claude CLI exited with code ${code}: ${errorMsg}`));
2669
3257
  }
@@ -2754,6 +3342,41 @@ Try again or use --timeout option to increase the limit.`
2754
3342
  supportsStreaming() {
2755
3343
  return false;
2756
3344
  }
3345
+ /**
3346
+ * Check if error message indicates a rate limit / usage limit error
3347
+ * v5.7.0: Provider limit detection patterns for Claude
3348
+ */
3349
+ isRateLimitError(errorMsg) {
3350
+ const lowerMsg = errorMsg.toLowerCase();
3351
+ return lowerMsg.includes("rate limit") || lowerMsg.includes("quota") || lowerMsg.includes("limit for today") || lowerMsg.includes("limit for this week") || lowerMsg.includes("anthropicusagelimit") || lowerMsg.includes("usage limit") || lowerMsg.includes("exceeded") && (lowerMsg.includes("limit") || lowerMsg.includes("quota"));
3352
+ }
3353
+ /**
3354
+ * Create detailed rate limit error with automatic recording
3355
+ * v5.7.0: Provider limit detection and auto-rotation
3356
+ */
3357
+ createRateLimitError(errorMsg) {
3358
+ const limitWindow = errorMsg.toLowerCase().includes("week") ? "weekly" : "daily";
3359
+ const limitManager = getProviderLimitManager();
3360
+ const resetAtMs = limitManager.calculateResetTime(
3361
+ this.config.name,
3362
+ limitWindow
3363
+ );
3364
+ void limitManager.recordLimitHit(
3365
+ this.config.name,
3366
+ limitWindow,
3367
+ resetAtMs,
3368
+ {
3369
+ reason: "usage_limit_exceeded",
3370
+ rawMessage: errorMsg
3371
+ }
3372
+ );
3373
+ return ProviderError.rateLimit(
3374
+ this.config.name,
3375
+ limitWindow,
3376
+ resetAtMs,
3377
+ errorMsg
3378
+ );
3379
+ }
2757
3380
  };
2758
3381
  }
2759
3382
  });
@@ -2770,6 +3393,8 @@ var init_gemini_provider = __esm({
2770
3393
  init_base_provider();
2771
3394
  init_retry_errors();
2772
3395
  init_logger();
3396
+ init_errors();
3397
+ init_provider_limit_manager();
2773
3398
  GeminiProvider = class extends BaseProvider {
2774
3399
  constructor(config) {
2775
3400
  super(config);
@@ -2964,7 +3589,12 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
2964
3589
  return;
2965
3590
  }
2966
3591
  if (code !== 0) {
2967
- reject(new Error(`Gemini CLI exited with code ${code}: ${stderr}`));
3592
+ const errorMsg = stderr || "No error message";
3593
+ if (this.isRateLimitError(errorMsg)) {
3594
+ reject(this.createRateLimitError(errorMsg));
3595
+ } else {
3596
+ reject(new Error(`Gemini CLI exited with code ${code}: ${errorMsg}`));
3597
+ }
2968
3598
  } else {
2969
3599
  resolve10({ content: stdout.trim() });
2970
3600
  }
@@ -3028,6 +3658,44 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
3028
3658
  supportsStreaming() {
3029
3659
  return false;
3030
3660
  }
3661
+ /**
3662
+ * Check if error message indicates a rate limit / usage limit error
3663
+ * v5.7.0: Provider limit detection patterns for Gemini
3664
+ */
3665
+ isRateLimitError(errorMsg) {
3666
+ const lowerMsg = errorMsg.toLowerCase();
3667
+ return lowerMsg.includes("resource_exhausted") || // gRPC error code
3668
+ lowerMsg.includes("429") || // HTTP status
3669
+ lowerMsg.includes("ratelimitexceeded") || // Gemini API error
3670
+ lowerMsg.includes("quota exceeded") || lowerMsg.includes("quota") || lowerMsg.includes("rate limit") || lowerMsg.includes("too many requests");
3671
+ }
3672
+ /**
3673
+ * Create detailed rate limit error with automatic recording
3674
+ * v5.7.0: Provider limit detection and auto-rotation
3675
+ */
3676
+ createRateLimitError(errorMsg) {
3677
+ const limitWindow = "daily";
3678
+ const limitManager = getProviderLimitManager();
3679
+ const resetAtMs = limitManager.calculateResetTime(
3680
+ this.config.name,
3681
+ limitWindow
3682
+ );
3683
+ void limitManager.recordLimitHit(
3684
+ this.config.name,
3685
+ limitWindow,
3686
+ resetAtMs,
3687
+ {
3688
+ reason: "quota_exceeded",
3689
+ rawMessage: errorMsg
3690
+ }
3691
+ );
3692
+ return ProviderError.rateLimit(
3693
+ this.config.name,
3694
+ limitWindow,
3695
+ resetAtMs,
3696
+ errorMsg
3697
+ );
3698
+ }
3031
3699
  };
3032
3700
  }
3033
3701
  });
@@ -3044,6 +3712,8 @@ var init_openai_provider = __esm({
3044
3712
  init_base_provider();
3045
3713
  init_retry_errors();
3046
3714
  init_logger();
3715
+ init_errors();
3716
+ init_provider_limit_manager();
3047
3717
  OpenAIProvider = class extends BaseProvider {
3048
3718
  constructor(config) {
3049
3719
  super(config);
@@ -3243,7 +3913,12 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
3243
3913
  return;
3244
3914
  }
3245
3915
  if (code !== 0) {
3246
- reject(new Error(`OpenAI CLI exited with code ${code}: ${stderr}`));
3916
+ const errorMsg = stderr || "No error message";
3917
+ if (this.isRateLimitError(errorMsg)) {
3918
+ reject(this.createRateLimitError(errorMsg));
3919
+ } else {
3920
+ reject(new Error(`OpenAI CLI exited with code ${code}: ${errorMsg}`));
3921
+ }
3247
3922
  } else {
3248
3923
  resolve10({ content: stdout.trim() });
3249
3924
  }
@@ -3524,6 +4199,43 @@ This is a placeholder streaming response.`;
3524
4199
  });
3525
4200
  });
3526
4201
  }
4202
+ /**
4203
+ * Check if error message indicates a rate limit / usage limit error
4204
+ * v5.7.0: Provider limit detection patterns for OpenAI
4205
+ */
4206
+ isRateLimitError(errorMsg) {
4207
+ const lowerMsg = errorMsg.toLowerCase();
4208
+ return lowerMsg.includes("rate_limit_exceeded") || // OpenAI API error type
4209
+ lowerMsg.includes("429") || // HTTP status
4210
+ lowerMsg.includes("quota exceeded") || lowerMsg.includes("insufficient_quota") || lowerMsg.includes("rate limit") || lowerMsg.includes("too many requests");
4211
+ }
4212
+ /**
4213
+ * Create detailed rate limit error with automatic recording
4214
+ * v5.7.0: Provider limit detection and auto-rotation
4215
+ */
4216
+ createRateLimitError(errorMsg) {
4217
+ const limitWindow = "daily";
4218
+ const limitManager = getProviderLimitManager();
4219
+ const resetAtMs = limitManager.calculateResetTime(
4220
+ this.config.name,
4221
+ limitWindow
4222
+ );
4223
+ void limitManager.recordLimitHit(
4224
+ this.config.name,
4225
+ limitWindow,
4226
+ resetAtMs,
4227
+ {
4228
+ reason: "rate_limit_exceeded",
4229
+ rawMessage: errorMsg
4230
+ }
4231
+ );
4232
+ return ProviderError.rateLimit(
4233
+ this.config.name,
4234
+ limitWindow,
4235
+ resetAtMs,
4236
+ errorMsg
4237
+ );
4238
+ }
3527
4239
  };
3528
4240
  }
3529
4241
  });
@@ -4062,6 +4774,14 @@ var GLOBAL_PROVIDER_DEFAULTS = {
4062
4774
  forceKillDelay: 1e3,
4063
4775
  // 1 second
4064
4776
  cacheEnabled: true
4777
+ },
4778
+ limitTracking: {
4779
+ enabled: true,
4780
+ // Enabled by default
4781
+ window: "daily",
4782
+ // Daily reset by default
4783
+ resetHourUtc: 0
4784
+ // Midnight UTC
4065
4785
  }
4066
4786
  };
4067
4787
  var DEFAULT_CONFIG = {
@@ -4082,7 +4802,13 @@ var DEFAULT_CONFIG = {
4082
4802
  // v5.6.18: Circuit breaker, process management, and version detection
4083
4803
  circuitBreaker: GLOBAL_PROVIDER_DEFAULTS.circuitBreaker,
4084
4804
  processManagement: GLOBAL_PROVIDER_DEFAULTS.processManagement,
4085
- versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection
4805
+ versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection,
4806
+ // v5.7.0: Usage limit tracking
4807
+ limitTracking: {
4808
+ ...GLOBAL_PROVIDER_DEFAULTS.limitTracking,
4809
+ window: "weekly"
4810
+ // Claude Code has weekly limits
4811
+ }
4086
4812
  // v5.0.5: Removed defaults - let provider CLI use optimal defaults
4087
4813
  // Users can still set provider.defaults in config for specific needs
4088
4814
  },
@@ -4101,7 +4827,10 @@ var DEFAULT_CONFIG = {
4101
4827
  // v5.6.18: Circuit breaker, process management, and version detection
4102
4828
  circuitBreaker: GLOBAL_PROVIDER_DEFAULTS.circuitBreaker,
4103
4829
  processManagement: GLOBAL_PROVIDER_DEFAULTS.processManagement,
4104
- versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection
4830
+ versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection,
4831
+ // v5.7.0: Usage limit tracking
4832
+ limitTracking: GLOBAL_PROVIDER_DEFAULTS.limitTracking
4833
+ // Gemini: daily limits
4105
4834
  // v5.0.5: Removed defaults - let provider CLI use optimal defaults
4106
4835
  },
4107
4836
  "openai": {
@@ -4119,7 +4848,10 @@ var DEFAULT_CONFIG = {
4119
4848
  // v5.6.18: Circuit breaker, process management, and version detection
4120
4849
  circuitBreaker: GLOBAL_PROVIDER_DEFAULTS.circuitBreaker,
4121
4850
  processManagement: GLOBAL_PROVIDER_DEFAULTS.processManagement,
4122
- versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection
4851
+ versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection,
4852
+ // v5.7.0: Usage limit tracking
4853
+ limitTracking: GLOBAL_PROVIDER_DEFAULTS.limitTracking
4854
+ // OpenAI: daily limits
4123
4855
  // v5.0.5: Removed defaults - let provider CLI use optimal defaults
4124
4856
  }
4125
4857
  },
@@ -4385,181 +5117,8 @@ var DEFAULT_CONFIG = {
4385
5117
  }
4386
5118
  };
4387
5119
 
4388
- // src/utils/errors.ts
4389
- init_esm_shims();
4390
- var BaseError = class extends Error {
4391
- code;
4392
- suggestions;
4393
- context;
4394
- isOperational;
4395
- constructor(message, code, suggestions = [], context, isOperational = true) {
4396
- super(message);
4397
- this.name = this.constructor.name;
4398
- this.code = code;
4399
- this.suggestions = suggestions;
4400
- this.context = context;
4401
- this.isOperational = isOperational;
4402
- Error.captureStackTrace(this, this.constructor);
4403
- }
4404
- /**
4405
- * Get formatted error message for display
4406
- */
4407
- getFormattedMessage() {
4408
- let formatted = `[${this.code}] ${this.message}`;
4409
- if (this.suggestions.length > 0) {
4410
- formatted += "\n\nSuggestions:";
4411
- this.suggestions.forEach((suggestion, i) => {
4412
- formatted += `
4413
- ${i + 1}. ${suggestion}`;
4414
- });
4415
- }
4416
- return formatted;
4417
- }
4418
- /**
4419
- * Get error details as object
4420
- */
4421
- toJSON() {
4422
- return {
4423
- name: this.name,
4424
- code: this.code,
4425
- message: this.message,
4426
- suggestions: this.suggestions,
4427
- context: this.context,
4428
- isOperational: this.isOperational,
4429
- stack: this.stack
4430
- };
4431
- }
4432
- };
4433
- var ConfigError = class _ConfigError extends BaseError {
4434
- constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
4435
- super(message, code, suggestions, context);
4436
- }
4437
- static notFound(path5) {
4438
- return new _ConfigError(
4439
- `Configuration file not found: ${path5}`,
4440
- "E1000" /* CONFIG_NOT_FOUND */,
4441
- [
4442
- 'Run "automatosx init" to create a new configuration',
4443
- "Specify a custom config path with --config option",
4444
- "Check that you are in a valid AutomatosX project directory"
4445
- ],
4446
- { path: path5 }
4447
- );
4448
- }
4449
- static invalid(reason, context) {
4450
- return new _ConfigError(
4451
- `Invalid configuration: ${reason}`,
4452
- "E1001" /* CONFIG_INVALID */,
4453
- [
4454
- "Check your automatosx.config.json for syntax errors",
4455
- "Validate against the schema in documentation",
4456
- 'Reset to default with "automatosx init --force"'
4457
- ],
4458
- context
4459
- );
4460
- }
4461
- static parseError(error, path5) {
4462
- return new _ConfigError(
4463
- `Failed to parse configuration: ${error.message}`,
4464
- "E1002" /* CONFIG_PARSE_ERROR */,
4465
- [
4466
- "Check JSON syntax in your config file",
4467
- "Use a JSON validator to find syntax errors",
4468
- 'Reset to default with "automatosx init --force"'
4469
- ],
4470
- { path: path5, originalError: error.message }
4471
- );
4472
- }
4473
- };
4474
- var ProviderError = class _ProviderError extends BaseError {
4475
- constructor(message, code = "E1301" /* PROVIDER_UNAVAILABLE */, suggestions = [], context) {
4476
- super(message, code, suggestions, context);
4477
- }
4478
- static notFound(providerName) {
4479
- return new _ProviderError(
4480
- `Provider "${providerName}" not found`,
4481
- "E1300" /* PROVIDER_NOT_FOUND */,
4482
- [
4483
- 'Check available providers with "automatosx list providers"',
4484
- "Verify provider is enabled in automatosx.config.json",
4485
- "Install the provider CLI if not already installed"
4486
- ],
4487
- { providerName }
4488
- );
4489
- }
4490
- static unavailable(providerName, reason) {
4491
- const msg = reason ? `Provider "${providerName}" unavailable: ${reason}` : `Provider "${providerName}" is unavailable`;
4492
- return new _ProviderError(
4493
- msg,
4494
- "E1301" /* PROVIDER_UNAVAILABLE */,
4495
- [
4496
- "Check that the provider CLI is installed and in your PATH",
4497
- "Verify provider configuration in automatosx.config.json",
4498
- "Try running the provider CLI directly to diagnose issues",
4499
- 'Check system status with "automatosx status --verbose"'
4500
- ],
4501
- { providerName, reason }
4502
- );
4503
- }
4504
- static timeout(providerName, timeoutMs) {
4505
- return new _ProviderError(
4506
- `Provider "${providerName}" timed out after ${timeoutMs}ms`,
4507
- "E1302" /* PROVIDER_TIMEOUT */,
4508
- [
4509
- "Increase timeout in automatosx.config.json",
4510
- "Try a simpler prompt or reduce context size",
4511
- "Check your network connection",
4512
- "Verify the provider service is responsive"
4513
- ],
4514
- { providerName, timeoutMs }
4515
- );
4516
- }
4517
- static noAvailableProviders() {
4518
- return new _ProviderError(
4519
- "No AI providers are available",
4520
- "E1306" /* PROVIDER_NO_AVAILABLE */,
4521
- [
4522
- "Install at least one provider CLI (Claude or Gemini)",
4523
- "Enable providers in automatosx.config.json",
4524
- 'Check provider status with "automatosx status"',
4525
- "Verify provider CLIs are in your PATH"
4526
- ]
4527
- );
4528
- }
4529
- static executionError(providerName, error) {
4530
- return new _ProviderError(
4531
- `Provider "${providerName}" execution failed: ${error.message}`,
4532
- "E1305" /* PROVIDER_EXEC_ERROR */,
4533
- [
4534
- "Check that the provider CLI is installed correctly",
4535
- "Verify you have necessary API keys configured",
4536
- "Try running the provider CLI directly to diagnose",
4537
- "Check error logs with --debug flag"
4538
- ],
4539
- { providerName, originalError: error.message }
4540
- );
4541
- }
4542
- };
4543
- function toBaseError(error) {
4544
- if (error instanceof BaseError) {
4545
- return error;
4546
- }
4547
- if (error instanceof Error) {
4548
- return new BaseError(
4549
- error.message,
4550
- "E9999" /* UNKNOWN_ERROR */,
4551
- ["Check error details for more information"],
4552
- { originalError: error.name, stack: error.stack }
4553
- );
4554
- }
4555
- return new BaseError(
4556
- String(error),
4557
- "E9999" /* UNKNOWN_ERROR */,
4558
- ["An unexpected error occurred"]
4559
- );
4560
- }
4561
-
4562
5120
  // src/core/config.ts
5121
+ init_errors();
4563
5122
  init_logger();
4564
5123
 
4565
5124
  // src/utils/deep-merge.ts
@@ -4644,20 +5203,20 @@ var VALIDATION_LIMITS = {
4644
5203
  MAX_CONCURRENT_AGENTS: 32
4645
5204
  // Cap parallel agent execution to protect resources
4646
5205
  };
4647
- function isValidRelativePath(path5) {
4648
- if (!path5 || typeof path5 !== "string") {
5206
+ function isValidRelativePath(path6) {
5207
+ if (!path6 || typeof path6 !== "string") {
4649
5208
  return false;
4650
5209
  }
4651
- if (path5.startsWith("/")) {
5210
+ if (path6.startsWith("/")) {
4652
5211
  return false;
4653
5212
  }
4654
- if (path5.includes("..")) {
5213
+ if (path6.includes("..")) {
4655
5214
  return false;
4656
5215
  }
4657
- if (/^[a-zA-Z]:/.test(path5)) {
5216
+ if (/^[a-zA-Z]:/.test(path6)) {
4658
5217
  return false;
4659
5218
  }
4660
- if (path5.startsWith("\\\\")) {
5219
+ if (path6.startsWith("\\\\")) {
4661
5220
  return false;
4662
5221
  }
4663
5222
  return true;
@@ -5095,16 +5654,16 @@ async function loadConfigUncached(projectDir) {
5095
5654
  });
5096
5655
  return config;
5097
5656
  }
5098
- async function loadConfigFile(path5) {
5657
+ async function loadConfigFile(path6) {
5099
5658
  try {
5100
- const content = await readFile(path5, "utf-8");
5659
+ const content = await readFile(path6, "utf-8");
5101
5660
  if (content.length > VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE) {
5102
5661
  throw ConfigError.parseError(
5103
5662
  new Error(`Config file too large (max ${VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE / 1024}KB, got ${Math.ceil(content.length / 1024)}KB)`),
5104
- path5
5663
+ path6
5105
5664
  );
5106
5665
  }
5107
- const ext2 = extname(path5).toLowerCase();
5666
+ const ext2 = extname(path6).toLowerCase();
5108
5667
  let userConfig;
5109
5668
  try {
5110
5669
  if (ext2 === ".yaml" || ext2 === ".yml") {
@@ -5113,7 +5672,7 @@ async function loadConfigFile(path5) {
5113
5672
  userConfig = JSON.parse(content);
5114
5673
  }
5115
5674
  } catch (parseError) {
5116
- throw ConfigError.parseError(parseError, path5);
5675
+ throw ConfigError.parseError(parseError, path6);
5117
5676
  }
5118
5677
  const config = mergeConfig(DEFAULT_CONFIG, userConfig);
5119
5678
  if (config.execution && userConfig.execution?.maxConcurrentAgents === void 0) {
@@ -5129,35 +5688,35 @@ async function loadConfigFile(path5) {
5129
5688
  if (validationErrors.length > 0) {
5130
5689
  throw ConfigError.invalid(
5131
5690
  validationErrors.join("; "),
5132
- { path: path5, errors: validationErrors }
5691
+ { path: path6, errors: validationErrors }
5133
5692
  );
5134
5693
  }
5135
- logger.info("Config loaded successfully", { path: normalizePath(path5), format: ext2 });
5694
+ logger.info("Config loaded successfully", { path: normalizePath(path6), format: ext2 });
5136
5695
  return config;
5137
5696
  } catch (error) {
5138
5697
  if (error instanceof ConfigError) {
5139
5698
  throw error;
5140
5699
  }
5141
5700
  if (error.code === "ENOENT") {
5142
- throw ConfigError.notFound(path5);
5701
+ throw ConfigError.notFound(path6);
5143
5702
  }
5144
5703
  if (error.code === "EACCES") {
5145
5704
  throw new ConfigError(
5146
- `Permission denied reading config: ${path5}`,
5705
+ `Permission denied reading config: ${path6}`,
5147
5706
  "E1002" /* CONFIG_PARSE_ERROR */,
5148
5707
  [
5149
5708
  "Check file permissions",
5150
5709
  "Run with appropriate user privileges",
5151
5710
  "Verify the file is accessible"
5152
5711
  ],
5153
- { path: path5, error: error.message }
5712
+ { path: path6, error: error.message }
5154
5713
  );
5155
5714
  }
5156
5715
  throw new ConfigError(
5157
5716
  `Failed to load config: ${error.message}`,
5158
5717
  "E1002" /* CONFIG_PARSE_ERROR */,
5159
5718
  ["Check file format and permissions"],
5160
- { path: path5, originalError: error.message }
5719
+ { path: path6, originalError: error.message }
5161
5720
  );
5162
5721
  }
5163
5722
  }
@@ -5535,16 +6094,16 @@ function validateConfig(config) {
5535
6094
  }
5536
6095
  return errors;
5537
6096
  }
5538
- async function saveConfigFile(path5, config) {
6097
+ async function saveConfigFile(path6, config) {
5539
6098
  try {
5540
6099
  const validationErrors = validateConfig(config);
5541
6100
  if (validationErrors.length > 0) {
5542
6101
  throw ConfigError.invalid(
5543
6102
  validationErrors.join("; "),
5544
- { path: path5, errors: validationErrors }
6103
+ { path: path6, errors: validationErrors }
5545
6104
  );
5546
6105
  }
5547
- const ext2 = extname(path5).toLowerCase();
6106
+ const ext2 = extname(path6).toLowerCase();
5548
6107
  let content;
5549
6108
  if (ext2 === ".yaml" || ext2 === ".yml") {
5550
6109
  content = dump(config, {
@@ -5556,29 +6115,29 @@ async function saveConfigFile(path5, config) {
5556
6115
  } else {
5557
6116
  content = JSON.stringify(config, null, 2);
5558
6117
  }
5559
- await writeFile(path5, content, "utf-8");
5560
- logger.info("Config saved successfully", { path: normalizePath(path5), format: ext2 });
6118
+ await writeFile(path6, content, "utf-8");
6119
+ logger.info("Config saved successfully", { path: normalizePath(path6), format: ext2 });
5561
6120
  } catch (error) {
5562
6121
  if (error instanceof ConfigError) {
5563
6122
  throw error;
5564
6123
  }
5565
6124
  if (error.code === "EACCES") {
5566
6125
  throw new ConfigError(
5567
- `Permission denied writing config: ${path5}`,
6126
+ `Permission denied writing config: ${path6}`,
5568
6127
  "E1002" /* CONFIG_PARSE_ERROR */,
5569
6128
  [
5570
6129
  "Check file permissions",
5571
6130
  "Run with appropriate user privileges",
5572
6131
  "Verify the directory is writable"
5573
6132
  ],
5574
- { path: path5, error: error.message }
6133
+ { path: path6, error: error.message }
5575
6134
  );
5576
6135
  }
5577
6136
  throw new ConfigError(
5578
6137
  `Failed to save config: ${error.message}`,
5579
6138
  "E1002" /* CONFIG_PARSE_ERROR */,
5580
6139
  ["Check file path and permissions"],
5581
- { path: path5, originalError: error.message }
6140
+ { path: path6, originalError: error.message }
5582
6141
  );
5583
6142
  }
5584
6143
  }
@@ -5857,6 +6416,7 @@ init_logger();
5857
6416
 
5858
6417
  // src/utils/error-formatter.ts
5859
6418
  init_esm_shims();
6419
+ init_errors();
5860
6420
  function formatError(error, options = {}) {
5861
6421
  const {
5862
6422
  verbose = false,
@@ -6314,10 +6874,10 @@ var configCommand = {
6314
6874
  } else {
6315
6875
  const projectConfig = resolve(process.cwd(), "automatosx.config.json");
6316
6876
  const hiddenConfig = resolve(process.cwd(), ".automatosx", "config.json");
6317
- const fs4 = await import('fs');
6318
- if (fs4.existsSync(projectConfig)) {
6877
+ const fs5 = await import('fs');
6878
+ if (fs5.existsSync(projectConfig)) {
6319
6879
  configPath = projectConfig;
6320
- } else if (fs4.existsSync(hiddenConfig)) {
6880
+ } else if (fs5.existsSync(hiddenConfig)) {
6321
6881
  configPath = hiddenConfig;
6322
6882
  } else {
6323
6883
  configPath = projectConfig;
@@ -6364,9 +6924,9 @@ var configCommand = {
6364
6924
  }
6365
6925
  }
6366
6926
  };
6367
- async function checkExists(path5) {
6927
+ async function checkExists(path6) {
6368
6928
  try {
6369
- await access$1(path5, constants.F_OK);
6929
+ await access$1(path6, constants.F_OK);
6370
6930
  return true;
6371
6931
  } catch {
6372
6932
  return false;
@@ -6397,7 +6957,7 @@ async function validateConfigFile(config, verbose) {
6397
6957
  }
6398
6958
  console.log();
6399
6959
  }
6400
- async function resetConfig(path5, verbose) {
6960
+ async function resetConfig(path6, verbose) {
6401
6961
  const { createRequire } = await import('module');
6402
6962
  const require2 = createRequire(import.meta.url);
6403
6963
  let version = "5.2.2";
@@ -6412,14 +6972,14 @@ async function resetConfig(path5, verbose) {
6412
6972
  // Users should rely on IDE JSON Schema plugins that fetch from npm package
6413
6973
  version
6414
6974
  };
6415
- await saveConfigFile(path5, config);
6975
+ await saveConfigFile(path6, config);
6416
6976
  printSuccess("Configuration reset to defaults");
6417
6977
  if (verbose) {
6418
6978
  console.log(chalk27.gray(`
6419
- Config file: ${path5}
6979
+ Config file: ${path6}
6420
6980
  `));
6421
6981
  }
6422
- logger.info("Configuration reset", { path: path5 });
6982
+ logger.info("Configuration reset", { path: path6 });
6423
6983
  }
6424
6984
  async function listConfig(config, verbose) {
6425
6985
  console.log(chalk27.bold.cyan("\n\u{1F4CB} AutomatosX Configuration\n"));
@@ -6498,7 +7058,7 @@ async function getConfig(config, key, verbose) {
6498
7058
  }
6499
7059
  }
6500
7060
  }
6501
- async function setConfig(path5, config, key, value, verbose) {
7061
+ async function setConfig(path6, config, key, value, verbose) {
6502
7062
  let parsedValue = value;
6503
7063
  try {
6504
7064
  parsedValue = JSON.parse(value);
@@ -6517,20 +7077,20 @@ async function setConfig(path5, config, key, value, verbose) {
6517
7077
  console.log();
6518
7078
  process.exit(1);
6519
7079
  }
6520
- await saveConfigFile(path5, config);
7080
+ await saveConfigFile(path6, config);
6521
7081
  printSuccess(`Configuration updated: ${key} = ${value}`);
6522
7082
  if (verbose) {
6523
7083
  console.log(chalk27.gray(`
6524
- Config file: ${path5}`));
7084
+ Config file: ${path6}`));
6525
7085
  console.log(chalk27.gray("Configuration validated successfully\n"));
6526
7086
  }
6527
7087
  logger.info("Configuration updated", { key, value });
6528
7088
  }
6529
- function getNestedValue(obj, path5) {
6530
- return path5.split(".").reduce((current, key) => current?.[key], obj);
7089
+ function getNestedValue(obj, path6) {
7090
+ return path6.split(".").reduce((current, key) => current?.[key], obj);
6531
7091
  }
6532
- function setNestedValue(obj, path5, value) {
6533
- const keys = path5.split(".");
7092
+ function setNestedValue(obj, path6, value) {
7093
+ const keys = path6.split(".");
6534
7094
  const lastKey = keys.pop();
6535
7095
  if (!lastKey) return false;
6536
7096
  const target = keys.reduce((current, key) => {
@@ -6581,7 +7141,7 @@ var initCommand = {
6581
7141
  let version = "5.1.2";
6582
7142
  try {
6583
7143
  const packageJson = JSON.parse(
6584
- await import('fs/promises').then((fs4) => fs4.readFile(join(packageRoot, "package.json"), "utf-8"))
7144
+ await import('fs/promises').then((fs5) => fs5.readFile(join(packageRoot, "package.json"), "utf-8"))
6585
7145
  );
6586
7146
  version = packageJson.version;
6587
7147
  } catch {
@@ -6724,9 +7284,9 @@ var initCommand = {
6724
7284
  }
6725
7285
  }
6726
7286
  };
6727
- async function checkExists2(path5) {
7287
+ async function checkExists2(path6) {
6728
7288
  try {
6729
- await access$1(path5, constants.F_OK);
7289
+ await access$1(path6, constants.F_OK);
6730
7290
  return true;
6731
7291
  } catch {
6732
7292
  return false;
@@ -7296,6 +7856,8 @@ init_logger();
7296
7856
  // src/core/router.ts
7297
7857
  init_esm_shims();
7298
7858
  init_logger();
7859
+ init_errors();
7860
+ init_provider_limit_manager();
7299
7861
 
7300
7862
  // src/utils/performance-markers.ts
7301
7863
  init_esm_shims();
@@ -7476,6 +8038,10 @@ var Router = class {
7476
8038
  this.penalizedProviders = /* @__PURE__ */ new Map();
7477
8039
  this.providerCooldownMs = config.providerCooldownMs ?? 3e4;
7478
8040
  this.cache = config.cache;
8041
+ const limitManager = getProviderLimitManager();
8042
+ void limitManager.initialize().catch((err) => {
8043
+ logger.warn("Failed to initialize ProviderLimitManager", { error: err.message });
8044
+ });
7479
8045
  this.healthCheckIntervalMs = config.healthCheckInterval;
7480
8046
  if (config.healthCheckInterval) {
7481
8047
  this.startHealthChecks(config.healthCheckInterval);
@@ -7533,6 +8099,22 @@ var Router = class {
7533
8099
  cacheEnabled: this.cache?.isEnabled ?? false
7534
8100
  });
7535
8101
  if (availableProviders.length === 0) {
8102
+ const limitManager2 = getProviderLimitManager();
8103
+ const allProviders = this.providers;
8104
+ const limitedProviders2 = [];
8105
+ for (const provider of allProviders) {
8106
+ const limitCheck = limitManager2.isProviderLimited(provider.name);
8107
+ if (limitCheck.isLimited && limitCheck.resetAtMs) {
8108
+ limitedProviders2.push({
8109
+ name: provider.name,
8110
+ resetAtMs: limitCheck.resetAtMs
8111
+ });
8112
+ }
8113
+ }
8114
+ if (limitedProviders2.length === allProviders.length && limitedProviders2.length > 0) {
8115
+ const soonestReset = Math.min(...limitedProviders2.map((p) => p.resetAtMs));
8116
+ throw ProviderError.allProvidersLimited(limitedProviders2, soonestReset);
8117
+ }
7536
8118
  throw ProviderError.noAvailableProviders();
7537
8119
  }
7538
8120
  let lastError;
@@ -7600,20 +8182,47 @@ var Router = class {
7600
8182
  return response;
7601
8183
  } catch (error) {
7602
8184
  lastError = error;
7603
- logger.warn("\u274C Router: provider failed", {
7604
- provider: provider.name,
7605
- attempt: attemptNumber,
7606
- error: lastError.message,
7607
- willFallback: this.fallbackEnabled && attemptNumber < availableProviders.length
7608
- });
7609
- const penaltyExpiry = Date.now() + this.providerCooldownMs;
7610
- this.penalizedProviders.set(provider.name, penaltyExpiry);
7611
- logger.debug(`Provider ${provider.name} penalized until ${new Date(penaltyExpiry).toISOString()}`);
8185
+ const isRateLimitError = error instanceof ProviderError && error.code === "E1303" /* PROVIDER_RATE_LIMIT */;
8186
+ if (isRateLimitError) {
8187
+ const providerError = error;
8188
+ const limitManager2 = getProviderLimitManager();
8189
+ const resetAtMs = providerError.context?.resetAtMs;
8190
+ const limitWindow = providerError.context?.limitWindow;
8191
+ if (typeof resetAtMs === "number" && typeof limitWindow === "string") {
8192
+ await limitManager2.recordLimitHit(
8193
+ provider.name,
8194
+ limitWindow,
8195
+ resetAtMs,
8196
+ {
8197
+ reason: providerError.context?.reason || "usage_limit_exceeded",
8198
+ rawMessage: providerError.message
8199
+ }
8200
+ );
8201
+ }
8202
+ logger.warn("\u26A0\uFE0F Router: provider hit usage limit, auto-rotating", {
8203
+ provider: provider.name,
8204
+ attempt: attemptNumber,
8205
+ resetAtMs: providerError.context?.resetAtMs,
8206
+ willFallback: this.fallbackEnabled && attemptNumber < availableProviders.length
8207
+ });
8208
+ } else {
8209
+ logger.warn("\u274C Router: provider failed", {
8210
+ provider: provider.name,
8211
+ attempt: attemptNumber,
8212
+ error: lastError.message,
8213
+ willFallback: this.fallbackEnabled && attemptNumber < availableProviders.length
8214
+ });
8215
+ const penaltyExpiry = Date.now() + this.providerCooldownMs;
8216
+ this.penalizedProviders.set(provider.name, penaltyExpiry);
8217
+ logger.debug(`Provider ${provider.name} penalized until ${new Date(penaltyExpiry).toISOString()}`);
8218
+ }
7612
8219
  if (!this.fallbackEnabled) {
7613
8220
  throw lastError;
7614
8221
  }
7615
8222
  if (attemptNumber < availableProviders.length) {
8223
+ const reason = isRateLimitError ? "usage limit hit" : "error";
7616
8224
  logger.info("\u{1F504} Router: fallback triggered", {
8225
+ reason,
7617
8226
  failedProvider: provider.name,
7618
8227
  nextProvider: availableProviders[attemptNumber]?.name,
7619
8228
  remainingProviders: availableProviders.length - attemptNumber
@@ -7626,6 +8235,21 @@ var Router = class {
7626
8235
  totalAttempts: attemptNumber,
7627
8236
  lastError: lastError?.message
7628
8237
  });
8238
+ const limitManager = getProviderLimitManager();
8239
+ const limitedProviders = [];
8240
+ for (const provider of availableProviders) {
8241
+ const limitCheck = limitManager.isProviderLimited(provider.name);
8242
+ if (limitCheck.isLimited && limitCheck.resetAtMs) {
8243
+ limitedProviders.push({
8244
+ name: provider.name,
8245
+ resetAtMs: limitCheck.resetAtMs
8246
+ });
8247
+ }
8248
+ }
8249
+ if (limitedProviders.length === availableProviders.length && limitedProviders.length > 0) {
8250
+ const soonestReset = Math.min(...limitedProviders.map((p) => p.resetAtMs));
8251
+ throw ProviderError.allProvidersLimited(limitedProviders, soonestReset);
8252
+ }
7629
8253
  throw new ProviderError(
7630
8254
  `All providers failed. Last error: ${lastError?.message || "Unknown error"}`,
7631
8255
  "E1306" /* PROVIDER_NO_AVAILABLE */,
@@ -7640,10 +8264,12 @@ var Router = class {
7640
8264
  }
7641
8265
  /**
7642
8266
  * Get available providers sorted by priority
7643
- * Filters out penalized providers (those in cooldown period)
8267
+ * v5.7.0: Enhanced with provider limit detection
8268
+ * Filters out penalized providers (those in cooldown period) and limited providers (quota exceeded)
7644
8269
  */
7645
8270
  async getAvailableProviders() {
7646
8271
  const now = Date.now();
8272
+ const limitManager = getProviderLimitManager();
7647
8273
  for (const [providerName, expiryTime] of this.penalizedProviders.entries()) {
7648
8274
  if (now >= expiryTime) {
7649
8275
  this.penalizedProviders.delete(providerName);
@@ -7652,6 +8278,13 @@ var Router = class {
7652
8278
  }
7653
8279
  const checks = this.providers.map(async (provider) => {
7654
8280
  try {
8281
+ const limitCheck = limitManager.isProviderLimited(provider.name, now);
8282
+ if (limitCheck.isLimited) {
8283
+ const remainingSec = Math.ceil((limitCheck.remainingMs || 0) / 1e3);
8284
+ const remainingHours = Math.ceil(remainingSec / 3600);
8285
+ logger.debug(`Skipping limited provider ${provider.name} (resets in ${remainingHours}h)`);
8286
+ return null;
8287
+ }
7655
8288
  if (this.penalizedProviders.has(provider.name)) {
7656
8289
  const expiryTime = this.penalizedProviders.get(provider.name);
7657
8290
  const remainingMs = expiryTime - now;
@@ -7707,6 +8340,14 @@ var Router = class {
7707
8340
  const checkStartTime = Date.now();
7708
8341
  this.healthCheckMetrics.checksPerformed++;
7709
8342
  try {
8343
+ const limitManager = getProviderLimitManager();
8344
+ const restoredProviders = await limitManager.refreshExpired();
8345
+ if (restoredProviders.length > 0) {
8346
+ logger.info("\u2705 Router: provider limits auto-restored", {
8347
+ providers: restoredProviders,
8348
+ count: restoredProviders.length
8349
+ });
8350
+ }
7710
8351
  const availabilityResults = await Promise.allSettled(
7711
8352
  this.providers.map(async (provider) => {
7712
8353
  const startTime = Date.now();
@@ -10218,6 +10859,7 @@ var WorkspaceManager = class _WorkspaceManager {
10218
10859
  // src/agents/context-manager.ts
10219
10860
  init_esm_shims();
10220
10861
  init_logger();
10862
+ init_errors();
10221
10863
  var PROVIDER_ALIASES = {
10222
10864
  "claude": "claude-code",
10223
10865
  "gemini": "gemini-cli",
@@ -12159,6 +12801,7 @@ var TimeoutManager = class {
12159
12801
 
12160
12802
  // src/utils/timeout-validator.ts
12161
12803
  init_esm_shims();
12804
+ init_errors();
12162
12805
  var MIN_WARNING_THRESHOLD2 = 0.5;
12163
12806
  var MAX_WARNING_THRESHOLD2 = 0.95;
12164
12807
  function validateTimeoutConfig(config) {
@@ -12345,9 +12988,9 @@ var DependencyGraphBuilder = class {
12345
12988
  detectCycles(graph) {
12346
12989
  const visiting = /* @__PURE__ */ new Set();
12347
12990
  const visited = /* @__PURE__ */ new Set();
12348
- const visit = (nodeName, path5) => {
12991
+ const visit = (nodeName, path6) => {
12349
12992
  if (visiting.has(nodeName)) {
12350
- throw new Error(`Circular dependency detected: ${[...path5, nodeName].join(" \u2192 ")}`);
12993
+ throw new Error(`Circular dependency detected: ${[...path6, nodeName].join(" \u2192 ")}`);
12351
12994
  }
12352
12995
  if (visited.has(nodeName)) {
12353
12996
  return;
@@ -12362,7 +13005,7 @@ var DependencyGraphBuilder = class {
12362
13005
  }
12363
13006
  visiting.add(nodeName);
12364
13007
  for (const dependency of node.dependencies) {
12365
- visit(dependency, [...path5, nodeName]);
13008
+ visit(dependency, [...path6, nodeName]);
12366
13009
  }
12367
13010
  visiting.delete(nodeName);
12368
13011
  visited.add(nodeName);
@@ -13950,12 +14593,12 @@ var ValidationError = class extends Error {
13950
14593
  this.name = "ValidationError";
13951
14594
  }
13952
14595
  };
13953
- function validatePathParameter(path5, paramName, projectRoot = process.cwd()) {
13954
- if (!path5 || path5.trim() === "") {
14596
+ function validatePathParameter(path6, paramName, projectRoot = process.cwd()) {
14597
+ if (!path6 || path6.trim() === "") {
13955
14598
  throw new ValidationError(
13956
14599
  `Invalid ${paramName}: path cannot be empty`,
13957
14600
  -32602 /* InvalidParams */,
13958
- { path: path5, paramName }
14601
+ { path: path6, paramName }
13959
14602
  );
13960
14603
  }
13961
14604
  const dangerousPatterns = [
@@ -13985,51 +14628,51 @@ function validatePathParameter(path5, paramName, projectRoot = process.cwd()) {
13985
14628
  // Common data drive (Windows, alt format)
13986
14629
  ];
13987
14630
  for (const pattern of dangerousPatterns) {
13988
- if (path5.includes(pattern)) {
14631
+ if (path6.includes(pattern)) {
13989
14632
  throw new ValidationError(
13990
14633
  `Invalid ${paramName}: path contains dangerous pattern "${pattern}"`,
13991
14634
  -32602 /* InvalidParams */,
13992
- { path: path5, paramName, pattern }
14635
+ { path: path6, paramName, pattern }
13993
14636
  );
13994
14637
  }
13995
14638
  }
13996
- if (isAbsolute(path5)) {
14639
+ if (isAbsolute(path6)) {
13997
14640
  throw new ValidationError(
13998
14641
  `Invalid ${paramName}: absolute paths are not allowed`,
13999
14642
  -32602 /* InvalidParams */,
14000
- { path: path5, paramName }
14643
+ { path: path6, paramName }
14001
14644
  );
14002
14645
  }
14003
14646
  try {
14004
- const resolvedPath = resolve(projectRoot, path5);
14647
+ const resolvedPath = resolve(projectRoot, path6);
14005
14648
  const normalizedRoot = resolve(projectRoot);
14006
14649
  if (!resolvedPath.startsWith(normalizedRoot + sep) && resolvedPath !== normalizedRoot) {
14007
14650
  throw new ValidationError(
14008
14651
  `Invalid ${paramName}: path escapes project boundary`,
14009
14652
  -32602 /* InvalidParams */,
14010
- { path: path5, paramName, projectRoot, resolvedPath }
14653
+ { path: path6, paramName, projectRoot, resolvedPath }
14011
14654
  );
14012
14655
  }
14013
14656
  } catch (error) {
14014
14657
  throw new ValidationError(
14015
14658
  `Invalid ${paramName}: path resolution failed`,
14016
14659
  -32602 /* InvalidParams */,
14017
- { path: path5, paramName, error: String(error) }
14660
+ { path: path6, paramName, error: String(error) }
14018
14661
  );
14019
14662
  }
14020
- if (path5.includes("\0")) {
14663
+ if (path6.includes("\0")) {
14021
14664
  throw new ValidationError(
14022
14665
  `Invalid ${paramName}: path contains null byte`,
14023
14666
  -32602 /* InvalidParams */,
14024
- { path: path5, paramName }
14667
+ { path: path6, paramName }
14025
14668
  );
14026
14669
  }
14027
14670
  const suspiciousChars = /[<>:|"]/;
14028
- if (suspiciousChars.test(path5)) {
14671
+ if (suspiciousChars.test(path6)) {
14029
14672
  throw new ValidationError(
14030
14673
  `Invalid ${paramName}: path contains invalid characters`,
14031
14674
  -32602 /* InvalidParams */,
14032
- { path: path5, paramName }
14675
+ { path: path6, paramName }
14033
14676
  );
14034
14677
  }
14035
14678
  }
@@ -14540,8 +15183,8 @@ function createMemoryExportHandler(deps) {
14540
15183
  return async (input) => {
14541
15184
  logger.info("[MCP] memory_export called", { input });
14542
15185
  try {
14543
- const { path: path5 } = input;
14544
- const absolutePath = resolveExportPath(deps.pathResolver, path5);
15186
+ const { path: path6 } = input;
15187
+ const absolutePath = resolveExportPath(deps.pathResolver, path6);
14545
15188
  const exported = await deps.memoryManager.exportToJSON(absolutePath);
14546
15189
  const result = {
14547
15190
  success: true,
@@ -14577,8 +15220,8 @@ function createMemoryImportHandler(deps) {
14577
15220
  return async (input) => {
14578
15221
  logger.info("[MCP] memory_import called", { input });
14579
15222
  try {
14580
- const { path: path5 } = input;
14581
- const absolutePath = resolveImportPath(deps.pathResolver, path5);
15223
+ const { path: path6 } = input;
15224
+ const absolutePath = resolveImportPath(deps.pathResolver, path6);
14582
15225
  const imported = await deps.memoryManager.importFromJSON(absolutePath);
14583
15226
  const result = {
14584
15227
  success: true,
@@ -15601,9 +16244,9 @@ var ProgressIndicator = class {
15601
16244
  // src/cli/commands/memory.ts
15602
16245
  var DEFAULT_DB_PATH = ".automatosx/memory/memory.db";
15603
16246
  function getMemoryManager(dbPath) {
15604
- const path5 = dbPath || DEFAULT_DB_PATH;
16247
+ const path6 = dbPath || DEFAULT_DB_PATH;
15605
16248
  return new LazyMemoryManager({
15606
- dbPath: resolve(path5),
16249
+ dbPath: resolve(path6),
15607
16250
  maxEntries: 1e5,
15608
16251
  autoCleanup: false,
15609
16252
  trackAccess: true
@@ -19071,6 +19714,7 @@ init_claude_provider();
19071
19714
  init_gemini_provider();
19072
19715
  init_openai_provider();
19073
19716
  init_logger();
19717
+ init_provider_limit_manager();
19074
19718
  var VERSION = getVersion();
19075
19719
  var statusCommand3 = {
19076
19720
  command: "status",
@@ -19284,6 +19928,49 @@ var statusCommand3 = {
19284
19928
  }
19285
19929
  }
19286
19930
  console.log();
19931
+ console.log(chalk27.cyan("Provider Limits:"));
19932
+ try {
19933
+ const limitManager = getProviderLimitManager();
19934
+ await limitManager.initialize();
19935
+ const limitStates = limitManager.getAllStates();
19936
+ const manualOverride = limitManager.getManualOverride();
19937
+ const now = Date.now();
19938
+ if (limitStates.size === 0 && !manualOverride) {
19939
+ console.log(chalk27.gray(" No limits detected. All providers available."));
19940
+ } else {
19941
+ if (limitStates.size > 0) {
19942
+ for (const [name, state] of limitStates.entries()) {
19943
+ const remainingMs = state.resetAtMs - now;
19944
+ const hours = Math.ceil(remainingMs / (1e3 * 60 * 60));
19945
+ console.log(chalk27.yellow(` \u26A0\uFE0F ${name}: LIMITED (resets in ${hours}h)`));
19946
+ if (argv2.verbose) {
19947
+ const resetDate = new Date(state.resetAtMs);
19948
+ console.log(chalk27.gray(` Window: ${state.window}`));
19949
+ console.log(chalk27.gray(` Resets: ${resetDate.toLocaleString()}`));
19950
+ if (state.reason) {
19951
+ console.log(chalk27.gray(` Reason: ${state.reason}`));
19952
+ }
19953
+ }
19954
+ }
19955
+ }
19956
+ if (manualOverride) {
19957
+ const expiresText = manualOverride.expiresAtMs ? `expires in ${Math.ceil((manualOverride.expiresAtMs - now) / (1e3 * 60))}m` : "no expiry";
19958
+ console.log(chalk27.cyan(` \u{1F527} Manual Override: ${manualOverride.provider} (${expiresText})`));
19959
+ }
19960
+ const availableCount = status.providers.filter((p) => {
19961
+ const isLimited = limitStates.has(p.name);
19962
+ const isOverridden = manualOverride && manualOverride.provider !== p.name;
19963
+ return p.available && !isLimited && !isOverridden;
19964
+ }).length;
19965
+ if (availableCount > 0) {
19966
+ console.log(chalk27.green(` \u2705 ${availableCount} provider(s) available`));
19967
+ }
19968
+ }
19969
+ } catch (error) {
19970
+ logger.warn("Failed to get provider limits", { error: error.message });
19971
+ console.log(chalk27.gray(" (Unable to load limit status)"));
19972
+ }
19973
+ console.log();
19287
19974
  console.log(chalk27.cyan("ENV Variable Overrides:"));
19288
19975
  const envVars = [
19289
19976
  { name: "CLAUDE_CLI", provider: "claude-code" },
@@ -19535,11 +20222,11 @@ async function getCurrentVersion() {
19535
20222
  return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
19536
20223
  } catch (error) {
19537
20224
  const { readFile: readFile9 } = await import('fs/promises');
19538
- const { dirname: dirname13, join: join26 } = await import('path');
20225
+ const { dirname: dirname14, join: join27 } = await import('path');
19539
20226
  const { fileURLToPath: fileURLToPath9 } = await import('url');
19540
20227
  const __filename6 = fileURLToPath9(import.meta.url);
19541
- const __dirname7 = dirname13(__filename6);
19542
- const pkgPath = join26(__dirname7, "../../../package.json");
20228
+ const __dirname7 = dirname14(__filename6);
20229
+ const pkgPath = join27(__dirname7, "../../../package.json");
19543
20230
  const content = await readFile9(pkgPath, "utf-8");
19544
20231
  const pkg = JSON.parse(content);
19545
20232
  return pkg.version;
@@ -22263,11 +22950,11 @@ var qmarksTestNoExtDot = ([$0]) => {
22263
22950
  return (f) => f.length === len && f !== "." && f !== "..";
22264
22951
  };
22265
22952
  var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
22266
- var path4 = {
22953
+ var path5 = {
22267
22954
  win32: { sep: "\\" },
22268
22955
  posix: { sep: "/" }
22269
22956
  };
22270
- var sep2 = defaultPlatform === "win32" ? path4.win32.sep : path4.posix.sep;
22957
+ var sep2 = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
22271
22958
  minimatch.sep = sep2;
22272
22959
  var GLOBSTAR = Symbol("globstar **");
22273
22960
  minimatch.GLOBSTAR = GLOBSTAR;
@@ -25470,12 +26157,12 @@ var PathBase = class {
25470
26157
  /**
25471
26158
  * Get the Path object referenced by the string path, resolved from this Path
25472
26159
  */
25473
- resolve(path5) {
25474
- if (!path5) {
26160
+ resolve(path6) {
26161
+ if (!path6) {
25475
26162
  return this;
25476
26163
  }
25477
- const rootPath = this.getRootString(path5);
25478
- const dir = path5.substring(rootPath.length);
26164
+ const rootPath = this.getRootString(path6);
26165
+ const dir = path6.substring(rootPath.length);
25479
26166
  const dirParts = dir.split(this.splitSep);
25480
26167
  const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
25481
26168
  return result;
@@ -26227,8 +26914,8 @@ var PathWin32 = class _PathWin32 extends PathBase {
26227
26914
  /**
26228
26915
  * @internal
26229
26916
  */
26230
- getRootString(path5) {
26231
- return win32.parse(path5).root;
26917
+ getRootString(path6) {
26918
+ return win32.parse(path6).root;
26232
26919
  }
26233
26920
  /**
26234
26921
  * @internal
@@ -26274,8 +26961,8 @@ var PathPosix = class _PathPosix extends PathBase {
26274
26961
  /**
26275
26962
  * @internal
26276
26963
  */
26277
- getRootString(path5) {
26278
- return path5.startsWith("/") ? "/" : "";
26964
+ getRootString(path6) {
26965
+ return path6.startsWith("/") ? "/" : "";
26279
26966
  }
26280
26967
  /**
26281
26968
  * @internal
@@ -26324,8 +27011,8 @@ var PathScurryBase = class {
26324
27011
  *
26325
27012
  * @internal
26326
27013
  */
26327
- constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs4 = defaultFS } = {}) {
26328
- this.#fs = fsFromOption(fs4);
27014
+ constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs5 = defaultFS } = {}) {
27015
+ this.#fs = fsFromOption(fs5);
26329
27016
  if (cwd instanceof URL || cwd.startsWith("file://")) {
26330
27017
  cwd = fileURLToPath(cwd);
26331
27018
  }
@@ -26364,11 +27051,11 @@ var PathScurryBase = class {
26364
27051
  /**
26365
27052
  * Get the depth of a provided path, string, or the cwd
26366
27053
  */
26367
- depth(path5 = this.cwd) {
26368
- if (typeof path5 === "string") {
26369
- path5 = this.cwd.resolve(path5);
27054
+ depth(path6 = this.cwd) {
27055
+ if (typeof path6 === "string") {
27056
+ path6 = this.cwd.resolve(path6);
26370
27057
  }
26371
- return path5.depth();
27058
+ return path6.depth();
26372
27059
  }
26373
27060
  /**
26374
27061
  * Return the cache of child entries. Exposed so subclasses can create
@@ -26855,9 +27542,9 @@ var PathScurryBase = class {
26855
27542
  process2();
26856
27543
  return results;
26857
27544
  }
26858
- chdir(path5 = this.cwd) {
27545
+ chdir(path6 = this.cwd) {
26859
27546
  const oldCwd = this.cwd;
26860
- this.cwd = typeof path5 === "string" ? this.cwd.resolve(path5) : path5;
27547
+ this.cwd = typeof path6 === "string" ? this.cwd.resolve(path6) : path6;
26861
27548
  this.cwd[setAsCwd](oldCwd);
26862
27549
  }
26863
27550
  };
@@ -26883,8 +27570,8 @@ var PathScurryWin32 = class extends PathScurryBase {
26883
27570
  /**
26884
27571
  * @internal
26885
27572
  */
26886
- newRoot(fs4) {
26887
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs4 });
27573
+ newRoot(fs5) {
27574
+ return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs5 });
26888
27575
  }
26889
27576
  /**
26890
27577
  * Return true if the provided path string is an absolute path
@@ -26912,8 +27599,8 @@ var PathScurryPosix = class extends PathScurryBase {
26912
27599
  /**
26913
27600
  * @internal
26914
27601
  */
26915
- newRoot(fs4) {
26916
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs4 });
27602
+ newRoot(fs5) {
27603
+ return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs5 });
26917
27604
  }
26918
27605
  /**
26919
27606
  * Return true if the provided path string is an absolute path
@@ -27219,8 +27906,8 @@ var MatchRecord = class {
27219
27906
  }
27220
27907
  // match, absolute, ifdir
27221
27908
  entries() {
27222
- return [...this.store.entries()].map(([path5, n]) => [
27223
- path5,
27909
+ return [...this.store.entries()].map(([path6, n]) => [
27910
+ path6,
27224
27911
  !!(n & 2),
27225
27912
  !!(n & 1)
27226
27913
  ]);
@@ -27425,9 +28112,9 @@ var GlobUtil = class {
27425
28112
  signal;
27426
28113
  maxDepth;
27427
28114
  includeChildMatches;
27428
- constructor(patterns, path5, opts) {
28115
+ constructor(patterns, path6, opts) {
27429
28116
  this.patterns = patterns;
27430
- this.path = path5;
28117
+ this.path = path6;
27431
28118
  this.opts = opts;
27432
28119
  this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
27433
28120
  this.includeChildMatches = opts.includeChildMatches !== false;
@@ -27446,11 +28133,11 @@ var GlobUtil = class {
27446
28133
  });
27447
28134
  }
27448
28135
  }
27449
- #ignored(path5) {
27450
- return this.seen.has(path5) || !!this.#ignore?.ignored?.(path5);
28136
+ #ignored(path6) {
28137
+ return this.seen.has(path6) || !!this.#ignore?.ignored?.(path6);
27451
28138
  }
27452
- #childrenIgnored(path5) {
27453
- return !!this.#ignore?.childrenIgnored?.(path5);
28139
+ #childrenIgnored(path6) {
28140
+ return !!this.#ignore?.childrenIgnored?.(path6);
27454
28141
  }
27455
28142
  // backpressure mechanism
27456
28143
  pause() {
@@ -27665,8 +28352,8 @@ var GlobUtil = class {
27665
28352
  };
27666
28353
  var GlobWalker = class extends GlobUtil {
27667
28354
  matches = /* @__PURE__ */ new Set();
27668
- constructor(patterns, path5, opts) {
27669
- super(patterns, path5, opts);
28355
+ constructor(patterns, path6, opts) {
28356
+ super(patterns, path6, opts);
27670
28357
  }
27671
28358
  matchEmit(e) {
27672
28359
  this.matches.add(e);
@@ -27703,8 +28390,8 @@ var GlobWalker = class extends GlobUtil {
27703
28390
  };
27704
28391
  var GlobStream = class extends GlobUtil {
27705
28392
  results;
27706
- constructor(patterns, path5, opts) {
27707
- super(patterns, path5, opts);
28393
+ constructor(patterns, path6, opts) {
28394
+ super(patterns, path6, opts);
27708
28395
  this.results = new Minipass({
27709
28396
  signal: this.signal,
27710
28397
  objectMode: true
@@ -28329,8 +29016,8 @@ var ConfigManager = class {
28329
29016
  * @returns User configuration or empty object if not found
28330
29017
  */
28331
29018
  async readUserConfig() {
28332
- const path5 = getUserConfigPath();
28333
- return this.readConfig(path5, "user");
29019
+ const path6 = getUserConfigPath();
29020
+ return this.readConfig(path6, "user");
28334
29021
  }
28335
29022
  /**
28336
29023
  * Read project-level Gemini CLI configuration
@@ -28338,8 +29025,8 @@ var ConfigManager = class {
28338
29025
  * @returns Project configuration or empty object if not found
28339
29026
  */
28340
29027
  async readProjectConfig() {
28341
- const path5 = getProjectConfigPath();
28342
- return this.readConfig(path5, "project");
29028
+ const path6 = getProjectConfigPath();
29029
+ return this.readConfig(path6, "project");
28343
29030
  }
28344
29031
  /**
28345
29032
  * Read configuration from a specific path with caching
@@ -28349,7 +29036,7 @@ var ConfigManager = class {
28349
29036
  * @returns Configuration object
28350
29037
  * @private
28351
29038
  */
28352
- async readConfig(path5, scope) {
29039
+ async readConfig(path6, scope) {
28353
29040
  const cached = this.cache.get(scope);
28354
29041
  if (cached && Date.now() - cached.timestamp < this.ttl) {
28355
29042
  return cached.data;
@@ -28360,13 +29047,13 @@ var ConfigManager = class {
28360
29047
  }
28361
29048
  const readOperation = (async () => {
28362
29049
  try {
28363
- const exists = await fileExists(path5);
29050
+ const exists = await fileExists(path6);
28364
29051
  if (!exists) {
28365
29052
  const emptyConfig = {};
28366
29053
  this.cache.set(scope, { data: emptyConfig, timestamp: Date.now() });
28367
29054
  return emptyConfig;
28368
29055
  }
28369
- const config = await readJsonFile(path5);
29056
+ const config = await readJsonFile(path6);
28370
29057
  this.cache.set(scope, { data: config, timestamp: Date.now() });
28371
29058
  return config;
28372
29059
  } catch (error) {
@@ -28375,8 +29062,8 @@ var ConfigManager = class {
28375
29062
  }
28376
29063
  throw new GeminiCLIError(
28377
29064
  "INVALID_CONFIG" /* INVALID_CONFIG */,
28378
- `Failed to read configuration from ${path5}`,
28379
- { path: path5, originalError: error }
29065
+ `Failed to read configuration from ${path6}`,
29066
+ { path: path6, originalError: error }
28380
29067
  );
28381
29068
  } finally {
28382
29069
  this.pendingReads.delete(scope);
@@ -29044,8 +29731,8 @@ var CommandTranslator = class {
29044
29731
  paths.push(getProjectCommandsPath());
29045
29732
  }
29046
29733
  }
29047
- for (const path5 of paths) {
29048
- const discovered = await this.scanDirectory(path5);
29734
+ for (const path6 of paths) {
29735
+ const discovered = await this.scanDirectory(path6);
29049
29736
  commands.push(...discovered);
29050
29737
  }
29051
29738
  return commands;
@@ -29086,8 +29773,8 @@ var CommandTranslator = class {
29086
29773
  const results = [];
29087
29774
  for (const name of commandNames) {
29088
29775
  try {
29089
- const path5 = await this.importCommand(name, outputDir, options);
29090
- results.push(path5);
29776
+ const path6 = await this.importCommand(name, outputDir, options);
29777
+ results.push(path6);
29091
29778
  } catch (error) {
29092
29779
  if (error instanceof GeminiCLIError) {
29093
29780
  console.error(`Failed to import ${name}: ${error.message}`);
@@ -29973,12 +30660,109 @@ var geminiCommand = {
29973
30660
  }
29974
30661
  };
29975
30662
 
30663
+ // src/cli/commands/provider-limits.ts
30664
+ init_esm_shims();
30665
+ init_provider_limit_manager();
30666
+ init_logger();
30667
+ function formatDuration(ms) {
30668
+ const hours = Math.floor(ms / (1e3 * 60 * 60));
30669
+ const minutes = Math.floor(ms % (1e3 * 60 * 60) / (1e3 * 60));
30670
+ if (hours > 0) {
30671
+ if (minutes > 0) {
30672
+ return `${hours}h ${minutes}m`;
30673
+ }
30674
+ return `${hours}h`;
30675
+ }
30676
+ if (minutes > 0) {
30677
+ return `${minutes}m`;
30678
+ }
30679
+ return "< 1m";
30680
+ }
30681
+ var providerLimitsCommand = {
30682
+ command: "provider-limits",
30683
+ aliases: ["pl", "limits"],
30684
+ describe: "Show current provider limit status",
30685
+ builder: (yargs2) => {
30686
+ return yargs2.option("json", {
30687
+ describe: "Output in JSON format",
30688
+ type: "boolean",
30689
+ default: false
30690
+ }).example("$0 provider-limits", "Show provider limits").example("$0 provider-limits --json", "Show limits in JSON format");
30691
+ },
30692
+ handler: async (argv2) => {
30693
+ try {
30694
+ const limitManager = getProviderLimitManager();
30695
+ await limitManager.initialize();
30696
+ const states = limitManager.getAllStates();
30697
+ const manualOverride = limitManager.getManualOverride();
30698
+ const now = Date.now();
30699
+ if (argv2.json) {
30700
+ const output = {
30701
+ limits: Array.from(states.entries()).map(([name, state]) => ({
30702
+ provider: name,
30703
+ status: state.status,
30704
+ window: state.window,
30705
+ detectedAtMs: state.detectedAtMs,
30706
+ resetAtMs: state.resetAtMs,
30707
+ remainingMs: state.resetAtMs - now,
30708
+ reason: state.reason,
30709
+ manualHold: state.manualHold
30710
+ })),
30711
+ manualOverride: manualOverride || null,
30712
+ timestamp: Date.now()
30713
+ };
30714
+ console.log(JSON.stringify(output, null, 2));
30715
+ } else {
30716
+ console.log();
30717
+ console.log(chalk27.bold("\u{1F4CA} Provider Limits Status"));
30718
+ console.log();
30719
+ if (states.size === 0) {
30720
+ console.log(chalk27.green(" \u2705 No limits detected. All providers available."));
30721
+ } else {
30722
+ for (const [name, state] of states.entries()) {
30723
+ const resetDate = new Date(state.resetAtMs);
30724
+ const remainingMs = state.resetAtMs - now;
30725
+ const remainingStr = formatDuration(remainingMs);
30726
+ console.log(chalk27.yellow(` \u26A0\uFE0F ${name}:`));
30727
+ console.log(` Status: ${state.status}`);
30728
+ console.log(` Window: ${state.window}`);
30729
+ console.log(` Resets: ${resetDate.toLocaleString()} (${remainingStr})`);
30730
+ if (state.reason) {
30731
+ console.log(` Reason: ${state.reason}`);
30732
+ }
30733
+ console.log();
30734
+ }
30735
+ }
30736
+ if (manualOverride) {
30737
+ console.log(chalk27.cyan(` \u{1F527} Manual Override: ${manualOverride.provider}`));
30738
+ if (manualOverride.expiresAtMs) {
30739
+ const expiresDate = new Date(manualOverride.expiresAtMs);
30740
+ const expiresIn = formatDuration(manualOverride.expiresAtMs - now);
30741
+ console.log(` Expires: ${expiresDate.toLocaleString()} (${expiresIn})`);
30742
+ } else {
30743
+ console.log(` Expires: Never`);
30744
+ }
30745
+ console.log();
30746
+ }
30747
+ console.log(chalk27.gray(' Use "ax status" for detailed provider information.'));
30748
+ console.log();
30749
+ }
30750
+ } catch (error) {
30751
+ logger.error("Failed to get provider limits", {
30752
+ error: error.message
30753
+ });
30754
+ console.error(chalk27.red(`Error: ${error.message}`));
30755
+ process.exit(1);
30756
+ }
30757
+ }
30758
+ };
30759
+
29976
30760
  // src/cli/index.ts
29977
30761
  installExitHandlers();
29978
30762
  var VERSION2 = getVersion();
29979
30763
  globalTracker.mark("cli_start");
29980
30764
  globalTracker.mark("yargs_parse_start");
29981
- var argv = await yargs(hideBin(process.argv)).scriptName("automatosx").usage("$0 <command> [options]").usage("\nAI Agent Orchestration Platform").example("$0 init", "Initialize project").example("$0 agent create backend --template developer", "Create agent from template").example("$0 agent list", "List all agents").example('$0 run assistant "Hello"', "Run assistant agent").example('$0 run backend "task" --interactive', "Run with interactive checkpoints").example("$0 resume <run-id>", "Resume from checkpoint").example("$0 runs list", "List checkpoint runs").example('$0 session create "Build API" backend', "Create multi-agent session").example("$0 session list", "List all sessions").example("$0 workspace stats", "Show workspace statistics").example("$0 list agents", "List available agents").example('$0 memory search "topic"', "Search memory").example("$0 cache status", "View cache statistics").example("$0 config --list", "View configuration").example("$0 mcp", "Start MCP server for Claude Code").example("$0 update", "Update to latest version").example("$0 gemini status", "Show Gemini CLI integration status").option("debug", {
30765
+ var argv = await yargs(hideBin(process.argv)).scriptName("automatosx").usage("$0 <command> [options]").usage("\nAI Agent Orchestration Platform").example("$0 init", "Initialize project").example("$0 agent create backend --template developer", "Create agent from template").example("$0 agent list", "List all agents").example('$0 run assistant "Hello"', "Run assistant agent").example('$0 run backend "task" --interactive', "Run with interactive checkpoints").example("$0 resume <run-id>", "Resume from checkpoint").example("$0 runs list", "List checkpoint runs").example('$0 session create "Build API" backend', "Create multi-agent session").example("$0 session list", "List all sessions").example("$0 workspace stats", "Show workspace statistics").example("$0 list agents", "List available agents").example('$0 memory search "topic"', "Search memory").example("$0 cache status", "View cache statistics").example("$0 config --list", "View configuration").example("$0 provider-limits", "Show provider usage limits").example("$0 mcp", "Start MCP server for Claude Code").example("$0 update", "Update to latest version").example("$0 gemini status", "Show Gemini CLI integration status").option("debug", {
29982
30766
  alias: "D",
29983
30767
  type: "boolean",
29984
30768
  description: "Enable debug mode with verbose output",
@@ -29993,7 +30777,7 @@ var argv = await yargs(hideBin(process.argv)).scriptName("automatosx").usage("$0
29993
30777
  type: "string",
29994
30778
  description: "Path to custom config file",
29995
30779
  global: true
29996
- }).command(initCommand).command(agentCommand).command(listCommand).command(runCommand).command(resumeCommand).command(runsCommand).command(sessionCommand).command(workspaceCommand).command(cacheCommand).command(configCommand).command(statusCommand3).command(memoryCommand).command(mcpCommand).command(geminiCommand).command(updateCommand).demandCommand(1, "You must provide a command. Run --help for usage.").help().version(VERSION2).alias("h", "help").alias("v", "version").strict().wrap(Math.min(120, yargs().terminalWidth())).parse();
30780
+ }).command(initCommand).command(agentCommand).command(listCommand).command(runCommand).command(resumeCommand).command(runsCommand).command(sessionCommand).command(workspaceCommand).command(cacheCommand).command(configCommand).command(statusCommand3).command(memoryCommand).command(mcpCommand).command(geminiCommand).command(providerLimitsCommand).command(updateCommand).demandCommand(1, "You must provide a command. Run --help for usage.").help().version(VERSION2).alias("h", "help").alias("v", "version").strict().wrap(Math.min(120, yargs().terminalWidth())).parse();
29997
30781
  globalTracker.mark("yargs_parse_end");
29998
30782
  globalTracker.measure("yargs_parsing", "yargs_parse_start", "yargs_parse_end");
29999
30783
  globalTracker.mark("options_setup_start");