nexus-agents 2.106.0 → 2.109.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/{child-mcp-config-KP2XBOIB.js → child-mcp-config-VF736CNG.js} +2 -2
  2. package/dist/{chunk-DLFJQ6W6.js → chunk-3NON6THG.js} +2 -2
  3. package/dist/{chunk-KZWJ52HH.js → chunk-4QH4IHCY.js} +2 -2
  4. package/dist/{chunk-75NKH2VD.js → chunk-5H3YYSMY.js} +4 -4
  5. package/dist/{chunk-R2VTDJO5.js → chunk-6R5IEZGJ.js} +5 -5
  6. package/dist/{chunk-CNIRLXNH.js → chunk-7PW7DTAV.js} +3 -3
  7. package/dist/{chunk-E3JGMUGY.js → chunk-AESMMLSZ.js} +4 -4
  8. package/dist/{chunk-CUX775NF.js → chunk-C5OTCDF6.js} +4 -4
  9. package/dist/{chunk-BJQA6DSN.js → chunk-DLWY7U6K.js} +2 -2
  10. package/dist/{chunk-EBJJGYOQ.js → chunk-EAQLDAKX.js} +3 -3
  11. package/dist/{chunk-PRPHNXUM.js → chunk-H47QJVMR.js} +3 -3
  12. package/dist/{chunk-KMASI7UC.js → chunk-H6Q3EPQG.js} +2 -2
  13. package/dist/{chunk-3YWBA5GE.js → chunk-HBPDYYIT.js} +7 -7
  14. package/dist/{chunk-RWAI74H6.js → chunk-HL22AVD4.js} +7 -7
  15. package/dist/{chunk-HNBA265P.js → chunk-ICOKYKLH.js} +31 -31
  16. package/dist/{chunk-M6JRWJZW.js → chunk-IJZGHD7O.js} +2 -2
  17. package/dist/{chunk-BEWKRRNZ.js → chunk-K3UILZ5X.js} +21 -3
  18. package/dist/chunk-K3UILZ5X.js.map +1 -0
  19. package/dist/{chunk-OP4VPTQZ.js → chunk-LLM3HY54.js} +3 -3
  20. package/dist/{chunk-U3Y6AWG5.js → chunk-LQLFZEOP.js} +2 -2
  21. package/dist/{chunk-GMHCPEHS.js → chunk-N5I4PC3P.js} +105 -27
  22. package/dist/chunk-N5I4PC3P.js.map +1 -0
  23. package/dist/{chunk-YL7SS6JZ.js → chunk-PFZNK25E.js} +2 -2
  24. package/dist/{chunk-GRHCGUBQ.js → chunk-RCN76RFJ.js} +157 -17
  25. package/dist/chunk-RCN76RFJ.js.map +1 -0
  26. package/dist/{chunk-MYXT7VPF.js → chunk-RMTFEDQV.js} +2 -2
  27. package/dist/{chunk-SFIGDPAE.js → chunk-RROQMZW2.js} +3 -3
  28. package/dist/{chunk-WPF7G56I.js → chunk-SXI5ZFBR.js} +2 -2
  29. package/dist/{chunk-5UU6BQJ3.js → chunk-TJUGEDQI.js} +2 -2
  30. package/dist/{chunk-VHIRBRZF.js → chunk-UIFDDBIG.js} +2 -2
  31. package/dist/{chunk-2J6HSR6X.js → chunk-V5UOHHOD.js} +2 -2
  32. package/dist/{chunk-2DHISFDY.js → chunk-VO7FEIXD.js} +5 -4
  33. package/dist/{chunk-2DHISFDY.js.map → chunk-VO7FEIXD.js.map} +1 -1
  34. package/dist/{chunk-VIXH6P3Y.js → chunk-YDPEK6NQ.js} +3 -3
  35. package/dist/{chunk-DOIOSEDU.js → chunk-ZFDG2CDY.js} +2 -2
  36. package/dist/{cli-circuit-breaker-PE3AYA56.js → cli-circuit-breaker-X4DTCCLH.js} +4 -4
  37. package/dist/cli.js +37 -37
  38. package/dist/{composite-router-GXLSZZQU.js → composite-router-QJRU2JU5.js} +2 -2
  39. package/dist/{consensus-vote-ZS4IDC5T.js → consensus-vote-HK5G3DYU.js} +12 -12
  40. package/dist/{context-retriever-52RLYDVE.js → context-retriever-3N7NVP57.js} +6 -6
  41. package/dist/{doctor-deep-3463IMLO.js → doctor-deep-6DKSM74K.js} +3 -3
  42. package/dist/{expert-bridge-OBOSFIHR.js → expert-bridge-N7PLXYBV.js} +4 -4
  43. package/dist/{factory-VXD4XO7R.js → factory-U23DIWPS.js} +5 -5
  44. package/dist/factory-UVXXFLOI.js +21 -0
  45. package/dist/{improvement-review-Y4DSR2AF.js → improvement-review-5DRHKSXV.js} +4 -4
  46. package/dist/index.d.ts +13 -4
  47. package/dist/index.js +38 -39
  48. package/dist/index.js.map +1 -1
  49. package/dist/{init-opencode-3FW2TAOR.js → init-opencode-YAYDQ36A.js} +5 -5
  50. package/dist/{issue-triage-KAVEQE5V.js → issue-triage-L3MTN6RS.js} +5 -5
  51. package/dist/{pr-reviewer-helpers-KORDKN6O.js → pr-reviewer-helpers-H4R57FMV.js} +4 -4
  52. package/dist/{registry-command-WZK7PYVZ.js → registry-command-7TVGPC43.js} +2 -2
  53. package/dist/{repo-security-plan-EO5R3S7C.js → repo-security-plan-543K7VCE.js} +3 -3
  54. package/dist/{research-helpers-synthesize-6HFD32XJ.js → research-helpers-synthesize-4BQHGJ3O.js} +3 -3
  55. package/dist/{routing-memory-XPJT3Y6Z.js → routing-memory-QN3PS4SS.js} +2 -2
  56. package/dist/{session-memory-TTE5YPV4.js → session-memory-Q3MMU45M.js} +3 -3
  57. package/dist/{setup-command-VVHIWCD4.js → setup-command-BDBPSHZD.js} +11 -11
  58. package/dist/{setup-config-NEM4KPUA.js → setup-config-D6WCNU6M.js} +3 -3
  59. package/dist/{setup-custom-api-GMMHEJJ5.js → setup-custom-api-SMLJ27IY.js} +3 -3
  60. package/dist/{tool-memory-TTLQ7MHZ.js → tool-memory-H6W6KNR3.js} +5 -5
  61. package/dist/{weather-report-KGAHUQFF.js → weather-report-R3KUDV4W.js} +2 -2
  62. package/package.json +1 -1
  63. package/dist/chunk-BEWKRRNZ.js.map +0 -1
  64. package/dist/chunk-GMHCPEHS.js.map +0 -1
  65. package/dist/chunk-GRHCGUBQ.js.map +0 -1
  66. package/dist/factory-SM7AIAFL.js +0 -21
  67. /package/dist/{child-mcp-config-KP2XBOIB.js.map → child-mcp-config-VF736CNG.js.map} +0 -0
  68. /package/dist/{chunk-DLFJQ6W6.js.map → chunk-3NON6THG.js.map} +0 -0
  69. /package/dist/{chunk-KZWJ52HH.js.map → chunk-4QH4IHCY.js.map} +0 -0
  70. /package/dist/{chunk-75NKH2VD.js.map → chunk-5H3YYSMY.js.map} +0 -0
  71. /package/dist/{chunk-R2VTDJO5.js.map → chunk-6R5IEZGJ.js.map} +0 -0
  72. /package/dist/{chunk-CNIRLXNH.js.map → chunk-7PW7DTAV.js.map} +0 -0
  73. /package/dist/{chunk-E3JGMUGY.js.map → chunk-AESMMLSZ.js.map} +0 -0
  74. /package/dist/{chunk-CUX775NF.js.map → chunk-C5OTCDF6.js.map} +0 -0
  75. /package/dist/{chunk-BJQA6DSN.js.map → chunk-DLWY7U6K.js.map} +0 -0
  76. /package/dist/{chunk-EBJJGYOQ.js.map → chunk-EAQLDAKX.js.map} +0 -0
  77. /package/dist/{chunk-PRPHNXUM.js.map → chunk-H47QJVMR.js.map} +0 -0
  78. /package/dist/{chunk-KMASI7UC.js.map → chunk-H6Q3EPQG.js.map} +0 -0
  79. /package/dist/{chunk-3YWBA5GE.js.map → chunk-HBPDYYIT.js.map} +0 -0
  80. /package/dist/{chunk-RWAI74H6.js.map → chunk-HL22AVD4.js.map} +0 -0
  81. /package/dist/{chunk-HNBA265P.js.map → chunk-ICOKYKLH.js.map} +0 -0
  82. /package/dist/{chunk-M6JRWJZW.js.map → chunk-IJZGHD7O.js.map} +0 -0
  83. /package/dist/{chunk-OP4VPTQZ.js.map → chunk-LLM3HY54.js.map} +0 -0
  84. /package/dist/{chunk-U3Y6AWG5.js.map → chunk-LQLFZEOP.js.map} +0 -0
  85. /package/dist/{chunk-YL7SS6JZ.js.map → chunk-PFZNK25E.js.map} +0 -0
  86. /package/dist/{chunk-MYXT7VPF.js.map → chunk-RMTFEDQV.js.map} +0 -0
  87. /package/dist/{chunk-SFIGDPAE.js.map → chunk-RROQMZW2.js.map} +0 -0
  88. /package/dist/{chunk-WPF7G56I.js.map → chunk-SXI5ZFBR.js.map} +0 -0
  89. /package/dist/{chunk-5UU6BQJ3.js.map → chunk-TJUGEDQI.js.map} +0 -0
  90. /package/dist/{chunk-VHIRBRZF.js.map → chunk-UIFDDBIG.js.map} +0 -0
  91. /package/dist/{chunk-2J6HSR6X.js.map → chunk-V5UOHHOD.js.map} +0 -0
  92. /package/dist/{chunk-VIXH6P3Y.js.map → chunk-YDPEK6NQ.js.map} +0 -0
  93. /package/dist/{chunk-DOIOSEDU.js.map → chunk-ZFDG2CDY.js.map} +0 -0
  94. /package/dist/{cli-circuit-breaker-PE3AYA56.js.map → cli-circuit-breaker-X4DTCCLH.js.map} +0 -0
  95. /package/dist/{composite-router-GXLSZZQU.js.map → composite-router-QJRU2JU5.js.map} +0 -0
  96. /package/dist/{consensus-vote-ZS4IDC5T.js.map → consensus-vote-HK5G3DYU.js.map} +0 -0
  97. /package/dist/{context-retriever-52RLYDVE.js.map → context-retriever-3N7NVP57.js.map} +0 -0
  98. /package/dist/{doctor-deep-3463IMLO.js.map → doctor-deep-6DKSM74K.js.map} +0 -0
  99. /package/dist/{expert-bridge-OBOSFIHR.js.map → expert-bridge-N7PLXYBV.js.map} +0 -0
  100. /package/dist/{factory-SM7AIAFL.js.map → factory-U23DIWPS.js.map} +0 -0
  101. /package/dist/{factory-VXD4XO7R.js.map → factory-UVXXFLOI.js.map} +0 -0
  102. /package/dist/{improvement-review-Y4DSR2AF.js.map → improvement-review-5DRHKSXV.js.map} +0 -0
  103. /package/dist/{init-opencode-3FW2TAOR.js.map → init-opencode-YAYDQ36A.js.map} +0 -0
  104. /package/dist/{issue-triage-KAVEQE5V.js.map → issue-triage-L3MTN6RS.js.map} +0 -0
  105. /package/dist/{pr-reviewer-helpers-KORDKN6O.js.map → pr-reviewer-helpers-H4R57FMV.js.map} +0 -0
  106. /package/dist/{registry-command-WZK7PYVZ.js.map → registry-command-7TVGPC43.js.map} +0 -0
  107. /package/dist/{repo-security-plan-EO5R3S7C.js.map → repo-security-plan-543K7VCE.js.map} +0 -0
  108. /package/dist/{research-helpers-synthesize-6HFD32XJ.js.map → research-helpers-synthesize-4BQHGJ3O.js.map} +0 -0
  109. /package/dist/{routing-memory-XPJT3Y6Z.js.map → routing-memory-QN3PS4SS.js.map} +0 -0
  110. /package/dist/{session-memory-TTE5YPV4.js.map → session-memory-Q3MMU45M.js.map} +0 -0
  111. /package/dist/{setup-command-VVHIWCD4.js.map → setup-command-BDBPSHZD.js.map} +0 -0
  112. /package/dist/{setup-config-NEM4KPUA.js.map → setup-config-D6WCNU6M.js.map} +0 -0
  113. /package/dist/{setup-custom-api-GMMHEJJ5.js.map → setup-custom-api-SMLJ27IY.js.map} +0 -0
  114. /package/dist/{tool-memory-TTLQ7MHZ.js.map → tool-memory-H6W6KNR3.js.map} +0 -0
  115. /package/dist/{weather-report-KGAHUQFF.js.map → weather-report-R3KUDV4W.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-adapters/circuit-breaker-types.ts","../src/cli-adapters/circuit-breaker.ts"],"sourcesContent":["/**\n * nexus-agents/cli-adapters - Circuit Breaker Types\n *\n * Type definitions and error classes for the circuit breaker pattern.\n *\n * (Source: Issue #81 - Circuit breaker for CLI failures)\n */\n\nimport { NexusError, ErrorCode } from '../core/errors.js';\nimport type { CliName } from './types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Circuit breaker states.\n */\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\n/**\n * Categories of failures for circuit breaker decisions.\n */\nexport type FailureCategory =\n | 'timeout' // CLI didn't respond in time\n | 'crash' // Process crashed or exited unexpectedly\n | 'authentication' // OAuth/auth failure\n | 'rate_limit' // Rate limit exceeded\n | 'connection' // MCP connection failed\n | 'unknown'; // Uncategorized failure\n\n/**\n * Configuration options for circuit breaker.\n */\nexport interface CircuitBreakerConfig {\n /** Number of failures before opening circuit (default: 5) */\n readonly failureThreshold: number;\n /** Time in ms before attempting recovery (default: 30000) */\n readonly resetTimeoutMs: number;\n /** Successful calls needed in half-open to close (default: 2) */\n readonly halfOpenSuccessThreshold: number;\n /** Whether to count timeouts as failures (default: true) */\n readonly countTimeoutsAsFailures: boolean;\n /** Whether to count auth failures as failures (default: false) */\n readonly countAuthFailuresAsFailures: boolean;\n /** Whether to count rate limit errors as failures (default: false) */\n readonly countRateLimitsAsFailures: boolean;\n /** Maximum number of requests allowed in half-open state (default: 3) */\n readonly halfOpenMaxRequests: number;\n}\n\n/**\n * Circuit breaker state snapshot.\n */\nexport interface CircuitBreakerSnapshot {\n /** Current state */\n readonly state: CircuitState;\n /** Total failure count since last closed */\n readonly failureCount: number;\n /** Success count in half-open state */\n readonly successCount: number;\n /** Timestamp of last failure */\n readonly lastFailureTime: number | null;\n /** Timestamp of last state change */\n readonly lastStateChange: number;\n /** Requests in current half-open window */\n readonly halfOpenRequests: number;\n /** Configuration */\n readonly config: CircuitBreakerConfig;\n}\n\n/**\n * Event emitted on circuit state changes.\n */\nexport interface CircuitStateChangeEvent {\n /** CLI name */\n readonly cliName: CliName;\n /** Previous state */\n readonly previousState: CircuitState;\n /** New state */\n readonly newState: CircuitState;\n /** Timestamp of change */\n readonly timestamp: number;\n /** Failure count at time of change */\n readonly failureCount: number;\n /** Reason for state change */\n readonly reason: string;\n}\n\n/**\n * Event listener for circuit state changes.\n */\nexport type CircuitStateChangeListener = (event: CircuitStateChangeEvent) => void;\n\n/**\n * Interface for circuit breaker operations.\n */\nexport interface ICircuitBreaker {\n /**\n * Executes a function with circuit breaker protection.\n */\n execute<T>(fn: () => Promise<T>): Promise<import('../core/index.js').Result<T, CircuitError>>;\n\n /**\n * Gets the current circuit state.\n */\n getState(): CircuitState;\n\n /**\n * Gets a full snapshot of circuit breaker state.\n */\n getSnapshot(): CircuitBreakerSnapshot;\n\n /**\n * Manually resets the circuit breaker to closed state.\n */\n reset(): void;\n\n /**\n * Records a failure manually (for external failure detection).\n */\n recordFailure(category: FailureCategory): void;\n\n /**\n * Records a success manually (for external success detection).\n */\n recordSuccess(): void;\n}\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\n/**\n * Error codes specific to circuit breaker.\n */\nexport const CircuitErrorCode = {\n CIRCUIT_OPEN: 'CIRCUIT_OPEN',\n CIRCUIT_HALF_OPEN_REJECTED: 'CIRCUIT_HALF_OPEN_REJECTED',\n EXECUTION_FAILED: 'EXECUTION_FAILED',\n} as const;\n\nexport type CircuitErrorCode = (typeof CircuitErrorCode)[keyof typeof CircuitErrorCode];\n\n// ============================================================================\n// Error Class\n// ============================================================================\n\n/**\n * Error thrown when circuit breaker blocks a request.\n */\nexport class CircuitError extends NexusError {\n readonly circuitErrorCode: CircuitErrorCode;\n readonly cliName: CliName;\n readonly circuitState: CircuitState;\n readonly failureCategory?: FailureCategory;\n\n constructor(\n message: string,\n options: {\n circuitErrorCode: CircuitErrorCode;\n cliName: CliName;\n circuitState: CircuitState;\n failureCategory?: FailureCategory;\n cause?: Error;\n }\n ) {\n const baseOptions: { code: ErrorCode; cause?: Error; context: Record<string, unknown> } = {\n code: ErrorCode.INTERNAL_ERROR,\n context: {\n circuitErrorCode: options.circuitErrorCode,\n cliName: options.cliName,\n circuitState: options.circuitState,\n },\n };\n if (options.cause !== undefined) {\n baseOptions.cause = options.cause;\n }\n if (options.failureCategory !== undefined) {\n baseOptions.context['failureCategory'] = options.failureCategory;\n }\n super(message, baseOptions);\n this.name = 'CircuitError';\n this.circuitErrorCode = options.circuitErrorCode;\n this.cliName = options.cliName;\n this.circuitState = options.circuitState;\n if (options.failureCategory !== undefined) {\n this.failureCategory = options.failureCategory;\n }\n }\n}\n\n// ============================================================================\n// Default Configuration\n// ============================================================================\n\n/**\n * Default circuit breaker configuration.\n */\nexport const DEFAULT_CIRCUIT_BREAKER_CONFIG: CircuitBreakerConfig = {\n failureThreshold: 5,\n resetTimeoutMs: 30_000,\n halfOpenSuccessThreshold: 2,\n countTimeoutsAsFailures: true,\n countAuthFailuresAsFailures: false,\n countRateLimitsAsFailures: true,\n halfOpenMaxRequests: 3,\n} as const;\n\n// ============================================================================\n// Error Categorization Patterns\n// ============================================================================\n\n/**\n * Pattern matchers for categorizing errors.\n */\nconst TIMEOUT_PATTERNS = ['timeout', 'timed out'];\nconst AUTH_PATTERNS = ['auth', 'unauthorized', 'forbidden', 'oauth'];\nconst RATE_LIMIT_PATTERNS = ['rate limit', 'too many requests', '429'];\nconst CONNECTION_PATTERNS = [\n 'connection',\n 'econnrefused',\n 'enotfound',\n 'mcp',\n 'eaddrinuse',\n 'address already in use',\n];\nconst CRASH_PATTERNS = ['crash', 'exited', 'killed', 'sigterm', 'sigkill'];\n\n/**\n * Checks if text contains any of the patterns.\n */\nfunction matchesPatterns(text: string, patterns: string[]): boolean {\n return patterns.some((pattern) => text.includes(pattern));\n}\n\n/**\n * Categorizes an error into a failure category.\n */\nexport function categorizeError(error: unknown): FailureCategory {\n if (!(error instanceof Error)) {\n return 'unknown';\n }\n\n const message = error.message.toLowerCase();\n const name = error.name.toLowerCase();\n const combined = `${message} ${name}`;\n\n if (matchesPatterns(combined, TIMEOUT_PATTERNS)) return 'timeout';\n if (matchesPatterns(combined, AUTH_PATTERNS)) return 'authentication';\n if (matchesPatterns(combined, RATE_LIMIT_PATTERNS)) return 'rate_limit';\n if (matchesPatterns(combined, CONNECTION_PATTERNS)) return 'connection';\n if (matchesPatterns(combined, CRASH_PATTERNS)) return 'crash';\n\n return 'unknown';\n}\n","/**\n * nexus-agents/cli-adapters - Circuit Breaker Implementation\n *\n * Implements the circuit breaker pattern to handle CLI failures gracefully\n * and prevent cascade failures in the multi-CLI mesh.\n *\n * (Source: Issue #81 - Circuit breaker for CLI failures)\n * (Source: Martin Fowler's Circuit Breaker pattern)\n */\n\nimport type { Result } from '../core/index.js';\nimport { getErrorMessage, err, ok, getTimeProvider } from '../core/index.js';\n\nimport { ErrorCode, type ModelError } from '../core/errors.js';\n\nimport type { CliName, CliErrorCode } from './types.js';\nimport {\n CircuitError,\n CircuitErrorCode,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n categorizeError,\n type CircuitState,\n type FailureCategory,\n type CircuitBreakerConfig,\n type CircuitBreakerSnapshot,\n type CircuitStateChangeEvent,\n type CircuitStateChangeListener,\n type ICircuitBreaker,\n} from './circuit-breaker-types.js';\n\n// Re-export all types for convenience\nexport {\n CircuitError,\n CircuitErrorCode,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n categorizeError,\n type CircuitState,\n type FailureCategory,\n type CircuitBreakerConfig,\n type CircuitBreakerSnapshot,\n type CircuitStateChangeEvent,\n type CircuitStateChangeListener,\n type ICircuitBreaker,\n} from './circuit-breaker-types.js';\n\n// ============================================================================\n// Circuit Breaker Implementation\n// ============================================================================\n\n/**\n * Circuit breaker implementation for CLI adapters.\n *\n * Provides protection against cascading failures by:\n * 1. Tracking failure counts\n * 2. Opening circuit when threshold exceeded\n * 3. Allowing gradual recovery through half-open state\n */\nexport class CliCircuitBreaker implements ICircuitBreaker {\n private state: CircuitState = 'closed';\n private failureCount = 0;\n private successCount = 0;\n private lastFailureTime: number | null = null;\n private lastStateChange: number;\n private halfOpenRequests = 0;\n private readonly listeners: Set<CircuitStateChangeListener> = new Set();\n\n constructor(\n private readonly cliName: CliName,\n private readonly config: CircuitBreakerConfig = DEFAULT_CIRCUIT_BREAKER_CONFIG\n ) {\n this.lastStateChange = getTimeProvider().now();\n }\n\n /**\n * Executes a function with circuit breaker protection.\n */\n async execute<T>(fn: () => Promise<T>): Promise<Result<T, CircuitError>> {\n const canExecute = this.canExecute();\n if (!canExecute.ok) {\n return canExecute;\n }\n\n try {\n const result = await fn();\n this.onSuccess();\n return ok(result);\n } catch (error) {\n const category = categorizeError(error);\n if (this.shouldCountFailure(category)) {\n this.onFailure(category);\n }\n return err(this.createExecutionError(error, category));\n }\n }\n\n getState(): CircuitState {\n this.checkStateTransition();\n return this.state;\n }\n\n getSnapshot(): CircuitBreakerSnapshot {\n this.checkStateTransition();\n return {\n state: this.state,\n failureCount: this.failureCount,\n successCount: this.successCount,\n lastFailureTime: this.lastFailureTime,\n lastStateChange: this.lastStateChange,\n halfOpenRequests: this.halfOpenRequests,\n config: this.config,\n };\n }\n\n reset(): void {\n const previousState = this.state;\n this.state = 'closed';\n this.failureCount = 0;\n this.successCount = 0;\n this.lastFailureTime = null;\n this.halfOpenRequests = 0;\n this.lastStateChange = getTimeProvider().now();\n\n if (previousState !== 'closed') {\n this.emitStateChange(previousState, 'closed', 'Manual reset');\n }\n }\n\n recordFailure(category: FailureCategory): void {\n if (this.shouldCountFailure(category)) {\n this.onFailure(category);\n }\n }\n\n recordSuccess(): void {\n this.onSuccess();\n }\n\n addStateChangeListener(listener: CircuitStateChangeListener): void {\n this.listeners.add(listener);\n }\n\n removeStateChangeListener(listener: CircuitStateChangeListener): void {\n this.listeners.delete(listener);\n }\n\n // -------------------------------------------------------------------------\n // Private Methods\n // -------------------------------------------------------------------------\n\n private canExecute(): Result<true, CircuitError> {\n this.checkStateTransition();\n\n if (this.state === 'closed') {\n return ok(true);\n }\n\n if (this.state === 'open') {\n return err(\n new CircuitError(`Circuit is open for CLI: ${this.cliName}`, {\n circuitErrorCode: CircuitErrorCode.CIRCUIT_OPEN,\n cliName: this.cliName,\n circuitState: this.state,\n })\n );\n }\n\n // half-open state\n if (this.halfOpenRequests >= this.config.halfOpenMaxRequests) {\n return err(\n new CircuitError(`Circuit half-open request limit reached for CLI: ${this.cliName}`, {\n circuitErrorCode: CircuitErrorCode.CIRCUIT_HALF_OPEN_REJECTED,\n cliName: this.cliName,\n circuitState: this.state,\n })\n );\n }\n this.halfOpenRequests++;\n return ok(true);\n }\n\n private checkStateTransition(): void {\n if (this.state === 'open' && this.lastFailureTime !== null) {\n const elapsed = getTimeProvider().now() - this.lastFailureTime;\n if (elapsed >= this.config.resetTimeoutMs) {\n this.transitionTo('half-open', 'Reset timeout elapsed');\n }\n }\n }\n\n private onSuccess(): void {\n if (this.state === 'closed') {\n this.failureCount = 0;\n } else if (this.state === 'half-open') {\n this.successCount++;\n if (this.successCount >= this.config.halfOpenSuccessThreshold) {\n const reason = `${String(this.successCount)} consecutive successes in half-open state`;\n this.transitionTo('closed', reason);\n }\n }\n }\n\n private onFailure(category: FailureCategory): void {\n this.failureCount++;\n this.lastFailureTime = getTimeProvider().now();\n\n if (this.state === 'closed' && this.failureCount >= this.config.failureThreshold) {\n const reason = `Failure threshold (${String(this.config.failureThreshold)}) exceeded`;\n this.transitionTo('open', reason);\n } else if (this.state === 'half-open') {\n this.transitionTo('open', `Failure during half-open recovery (category: ${category})`);\n }\n }\n\n private transitionTo(newState: CircuitState, reason: string): void {\n const previousState = this.state;\n this.state = newState;\n this.lastStateChange = getTimeProvider().now();\n\n if (newState === 'closed') {\n this.failureCount = 0;\n this.successCount = 0;\n this.halfOpenRequests = 0;\n } else if (newState === 'half-open') {\n // #3026 finding 5: pre-fix `failureCount` carried over across\n // recovery cycles. transitionTo('open',…) only zeroes counts on\n // 'closed', so a half-open→open→half-open flaky pattern grew\n // `failureCount` monotonically across cycles. The count served\n // its threshold purpose when we opened the circuit; resetting on\n // 'half-open' restores the documented per-cycle semantic that\n // operator dashboards / alerts read from `getSnapshot()`.\n this.failureCount = 0;\n this.successCount = 0;\n this.halfOpenRequests = 0;\n }\n\n this.emitStateChange(previousState, newState, reason);\n }\n\n private emitStateChange(\n previousState: CircuitState,\n newState: CircuitState,\n reason: string\n ): void {\n const event: CircuitStateChangeEvent = {\n cliName: this.cliName,\n previousState,\n newState,\n timestamp: this.lastStateChange,\n failureCount: this.failureCount,\n reason,\n };\n\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // Ignore listener errors to prevent cascade\n }\n }\n }\n\n private shouldCountFailure(category: FailureCategory): boolean {\n if (category === 'timeout') return this.config.countTimeoutsAsFailures;\n if (category === 'authentication') return this.config.countAuthFailuresAsFailures;\n if (category === 'rate_limit') return this.config.countRateLimitsAsFailures;\n return true;\n }\n\n private createExecutionError(error: unknown, category: FailureCategory): CircuitError {\n const message = getErrorMessage(error);\n const cause = error instanceof Error ? error : new Error(String(error));\n return new CircuitError(`CLI execution failed: ${message}`, {\n circuitErrorCode: CircuitErrorCode.EXECUTION_FAILED,\n cliName: this.cliName,\n circuitState: this.state,\n failureCategory: category,\n cause,\n });\n }\n}\n\n// ============================================================================\n// Registry\n// ============================================================================\n\n/**\n * Registry for managing per-CLI circuit breakers.\n */\nexport class CircuitBreakerRegistry {\n private readonly breakers: Map<CliName, CliCircuitBreaker> = new Map();\n private readonly globalListeners: Set<CircuitStateChangeListener> = new Set();\n\n constructor(private readonly defaultConfig: Partial<CircuitBreakerConfig> = {}) {}\n\n getBreaker(cliName: CliName, config?: Partial<CircuitBreakerConfig>): CliCircuitBreaker {\n let breaker = this.breakers.get(cliName);\n\n if (!breaker) {\n const mergedConfig: CircuitBreakerConfig = {\n ...DEFAULT_CIRCUIT_BREAKER_CONFIG,\n ...this.defaultConfig,\n ...config,\n };\n breaker = new CliCircuitBreaker(cliName, mergedConfig);\n\n for (const listener of this.globalListeners) {\n breaker.addStateChangeListener(listener);\n }\n\n this.breakers.set(cliName, breaker);\n }\n\n return breaker;\n }\n\n isOpen(cliName: CliName): boolean {\n return this.breakers.get(cliName)?.getState() === 'open';\n }\n\n getAllSnapshots(): Map<CliName, CircuitBreakerSnapshot> {\n const snapshots = new Map<CliName, CircuitBreakerSnapshot>();\n for (const [name, breaker] of this.breakers) {\n snapshots.set(name, breaker.getSnapshot());\n }\n return snapshots;\n }\n\n resetAll(): void {\n for (const breaker of this.breakers.values()) {\n breaker.reset();\n }\n }\n\n reset(cliName: CliName): void {\n this.breakers.get(cliName)?.reset();\n }\n\n addGlobalStateChangeListener(listener: CircuitStateChangeListener): void {\n this.globalListeners.add(listener);\n for (const breaker of this.breakers.values()) {\n breaker.addStateChangeListener(listener);\n }\n }\n\n removeGlobalStateChangeListener(listener: CircuitStateChangeListener): void {\n this.globalListeners.delete(listener);\n for (const breaker of this.breakers.values()) {\n breaker.removeStateChangeListener(listener);\n }\n }\n\n getHealthyClis(): CliName[] {\n const healthy: CliName[] = [];\n for (const [name, breaker] of this.breakers) {\n if (breaker.getState() === 'closed') {\n healthy.push(name);\n }\n }\n return healthy;\n }\n\n getUnhealthyClis(): CliName[] {\n const unhealthy: CliName[] = [];\n for (const [name, breaker] of this.breakers) {\n const state = breaker.getState();\n if (state === 'open' || state === 'half-open') {\n unhealthy.push(name);\n }\n }\n return unhealthy;\n }\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Maps CLI error codes to failure categories.\n */\nexport function mapCliErrorToCategory(errorCode: CliErrorCode): FailureCategory {\n const mapping: Record<string, FailureCategory> = {\n TIMEOUT: 'timeout',\n NOT_AUTHENTICATED: 'authentication',\n RATE_LIMITED: 'rate_limit',\n CONNECTION_ERROR: 'connection',\n };\n return mapping[errorCode] ?? 'unknown';\n}\n\n/**\n * Maps a direct-API {@link ModelError} to a circuit-breaker failure category.\n *\n * Mirrors {@link mapCliErrorToCategory} for the IModelAdapter layer (#3423):\n * API failures must drive the same breaker degradation/failover learning that\n * CLI subprocess failures already get. Maps by the structured `error.code`\n * first, then falls back to message-pattern categorization via\n * {@link categorizeError} (which returns `'unknown'` if nothing matches).\n *\n * The fallback is what turns a generic `MODEL_ERROR` whose message reads\n * \"connection refused\" into a `connection` category instead of `unknown`.\n */\nexport function mapModelErrorToCategory(error: ModelError): FailureCategory {\n switch (error.code) {\n case ErrorCode.MODEL_TIMEOUT:\n case ErrorCode.TIMEOUT_ERROR:\n return 'timeout';\n case ErrorCode.UNAUTHORIZED:\n return 'authentication';\n case ErrorCode.MODEL_RATE_LIMITED:\n case ErrorCode.RATE_LIMIT_ERROR:\n return 'rate_limit';\n case ErrorCode.MODEL_UNAVAILABLE:\n case ErrorCode.MODEL_NOT_FOUND:\n // Intentional reuse of `connection`: there is no dedicated \"endpoint gone /\n // model retired\" category, and `connection` is a counted, failover-driving\n // category — the right behaviour for an unavailable/missing model.\n return 'connection';\n default:\n // MODEL_ERROR and any other code: fall back to message-pattern matching.\n return categorizeError(error);\n }\n}\n\n// `createCircuitBreakerRegistryWithMetrics` and the\n// `Capacity Monitor Integration` section\n// (`integrateCapacityMonitorWithCircuitBreaker` + the\n// `CapacityMonitorIntegrationConfig` interface and its default config)\n// were removed in #3018 — both had only test-file callers in the tree.\n// The `CircuitBreakerRegistry` above is what production adapters use;\n// if metrics-logging or capacity-monitor integration come back as real\n// requirements, reintroduce them alongside the consumer code in the\n// same PR (activation-or-delete YAGNI — same pattern as #2937–#2940).\n"],"mappings":"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","names":[]}
@@ -3,14 +3,14 @@ import {
3
3
  createStream,
4
4
  requireApiKey,
5
5
  validateApiKeyPresence
6
- } from "./chunk-KMASI7UC.js";
6
+ } from "./chunk-H6Q3EPQG.js";
7
7
  import {
8
8
  ModelCapability,
9
9
  err,
10
10
  getCliModelName,
11
11
  getTokenEstimator,
12
12
  ok
13
- } from "./chunk-2DHISFDY.js";
13
+ } from "./chunk-VO7FEIXD.js";
14
14
 
15
15
  // src/adapters/openai-types.ts
16
16
  var OPENAI_MODELS = {
@@ -553,4 +553,4 @@ export {
553
553
  OpenAIAdapter,
554
554
  createOpenAIAdapter
555
555
  };
556
- //# sourceMappingURL=chunk-OP4VPTQZ.js.map
556
+ //# sourceMappingURL=chunk-LLM3HY54.js.map
@@ -3,7 +3,7 @@ import {
3
3
  err,
4
4
  getErrorMessage,
5
5
  ok
6
- } from "./chunk-2DHISFDY.js";
6
+ } from "./chunk-VO7FEIXD.js";
7
7
 
8
8
  // src/scm/types.ts
9
9
  var ScmError = class extends Error {
@@ -258,4 +258,4 @@ export {
258
258
  ScmError,
259
259
  GitHubProvider
260
260
  };
261
- //# sourceMappingURL=chunk-U3Y6AWG5.js.map
261
+ //# sourceMappingURL=chunk-LQLFZEOP.js.map
@@ -4,23 +4,23 @@ import {
4
4
  createStream,
5
5
  requireApiKey,
6
6
  validateApiKeyPresence
7
- } from "./chunk-KMASI7UC.js";
7
+ } from "./chunk-H6Q3EPQG.js";
8
8
  import {
9
9
  CUSTOM_API_BASE_URL_ENV,
10
10
  CUSTOM_API_DEFAULT_MODEL,
11
11
  PROVIDER_ENV_KEYS,
12
12
  validateCustomApiBaseUrl
13
- } from "./chunk-DOIOSEDU.js";
13
+ } from "./chunk-ZFDG2CDY.js";
14
14
  import {
15
15
  CliCircuitBreaker,
16
16
  DEFAULT_CIRCUIT_BREAKER_CONFIG
17
- } from "./chunk-BEWKRRNZ.js";
17
+ } from "./chunk-K3UILZ5X.js";
18
18
  import {
19
19
  getAvailabilityCache
20
- } from "./chunk-MYXT7VPF.js";
20
+ } from "./chunk-RMTFEDQV.js";
21
21
  import {
22
22
  generateHyphenId
23
- } from "./chunk-M6JRWJZW.js";
23
+ } from "./chunk-IJZGHD7O.js";
24
24
  import {
25
25
  CLI_SUBPROCESS_TIMEOUTS,
26
26
  CLI_TIMEOUTS,
@@ -54,7 +54,7 @@ import {
54
54
  loadModelsDevSnapshot,
55
55
  ok,
56
56
  resolveCliAlias
57
- } from "./chunk-2DHISFDY.js";
57
+ } from "./chunk-VO7FEIXD.js";
58
58
 
59
59
  // src/cli-adapters/cli-to-model-adapter.ts
60
60
  var CliToModelAdapter = class {
@@ -344,6 +344,7 @@ var DEFAULT_MAX_TOKENS = 4096;
344
344
 
345
345
  // src/adapters/claude-adapter-helpers.ts
346
346
  import "@anthropic-ai/sdk";
347
+ var RESPOND_TOOL_NAME = "respond";
347
348
  function mapStopReason(anthropicReason) {
348
349
  switch (anthropicReason) {
349
350
  case "end_turn":
@@ -415,6 +416,26 @@ function mapTool(tool) {
415
416
  input_schema: tool.inputSchema
416
417
  };
417
418
  }
419
+ function forcesResponseTool(request) {
420
+ return request.responseFormat !== void 0 && request.responseFormat.type !== "text";
421
+ }
422
+ function buildRespondTool(format) {
423
+ const inputSchema = format?.type === "json_schema" ? format.schema : { type: "object" };
424
+ return mapTool({
425
+ name: RESPOND_TOOL_NAME,
426
+ description: "Return the structured response for this request. Call this tool exactly once with the full answer as its arguments.",
427
+ inputSchema
428
+ });
429
+ }
430
+ function mapResponseWithRespondTool(blocks) {
431
+ const respondBlock = blocks.find(
432
+ (block) => block.type === "tool_use" && block.name === RESPOND_TOOL_NAME
433
+ );
434
+ if (respondBlock?.type === "tool_use") {
435
+ return [{ type: "text", text: JSON.stringify(respondBlock.input) }];
436
+ }
437
+ return blocks.map(mapContentBlock);
438
+ }
418
439
  function resolveModelId(modelId) {
419
440
  const registryId = resolveCliAlias(modelId);
420
441
  if (registryId !== void 0) return getCliModelName(registryId);
@@ -591,7 +612,7 @@ var ClaudeAdapter = class extends BaseAdapter {
591
612
  async executeCompletion(request) {
592
613
  const params = this.buildRequestParams(request);
593
614
  const response = request.signal !== void 0 ? await this.client.messages.create(params, { signal: request.signal }) : await this.client.messages.create(params);
594
- return this.mapResponse(response);
615
+ return this.mapResponse(response, forcesResponseTool(request));
595
616
  }
596
617
  /**
597
618
  * Executes streaming completion and pushes chunks to the controller.
@@ -642,18 +663,17 @@ var ClaudeAdapter = class extends BaseAdapter {
642
663
  if (request.tools !== void 0 && request.tools.length > 0) {
643
664
  params.tools = request.tools.map(mapTool);
644
665
  }
645
- if (request.responseFormat !== void 0 && request.responseFormat.type !== "text") {
646
- this.logger.warn("responseFormat is not supported by Claude adapter", {
647
- requestedFormat: request.responseFormat.type,
648
- suggestion: "Use tool use or prompt engineering for structured output"
649
- });
666
+ if (forcesResponseTool(request)) {
667
+ const respondTool = buildRespondTool(request.responseFormat);
668
+ params.tools = [...params.tools ?? [], respondTool];
669
+ params.tool_choice = { type: "tool", name: RESPOND_TOOL_NAME };
650
670
  }
651
671
  }
652
672
  /**
653
673
  * Maps Anthropic API response to our CompletionResponse format.
654
674
  */
655
- mapResponse(response) {
656
- const content = response.content.map(mapContentBlock);
675
+ mapResponse(response, surfaceRespondTool = false) {
676
+ const content = surfaceRespondTool ? mapResponseWithRespondTool(response.content) : response.content.map(mapContentBlock);
657
677
  const usage = {
658
678
  inputTokens: response.usage.input_tokens,
659
679
  outputTokens: response.usage.output_tokens,
@@ -802,17 +822,39 @@ function extractProviderFactory(mod, factoryName) {
802
822
  function extractAiSdkFunctions(mod) {
803
823
  const generateText = mod["generateText"];
804
824
  const streamText = mod["streamText"];
825
+ const generateObject = mod["generateObject"];
826
+ const jsonSchema = mod["jsonSchema"];
805
827
  if (typeof generateText !== "function") {
806
828
  throw new Error("AI SDK module missing expected export: 'generateText'");
807
829
  }
808
830
  if (typeof streamText !== "function") {
809
831
  throw new Error("AI SDK module missing expected export: 'streamText'");
810
832
  }
833
+ if (typeof generateObject !== "function") {
834
+ throw new Error("AI SDK module missing expected export: 'generateObject'");
835
+ }
836
+ if (typeof jsonSchema !== "function") {
837
+ throw new Error("AI SDK module missing expected export: 'jsonSchema'");
838
+ }
811
839
  return {
812
840
  generateText,
813
- streamText
841
+ streamText,
842
+ generateObject,
843
+ jsonSchema
814
844
  };
815
845
  }
846
+ function isGenerateObjectResult(value) {
847
+ if (typeof value !== "object" || value === null) return false;
848
+ const record = value;
849
+ if (!("object" in record)) return false;
850
+ if (typeof record["finishReason"] !== "string") return false;
851
+ const usage = record["usage"];
852
+ if (typeof usage !== "object" || usage === null) return false;
853
+ const response = record["response"];
854
+ if (typeof response !== "object" || response === null) return false;
855
+ if (typeof response["modelId"] !== "string") return false;
856
+ return true;
857
+ }
816
858
  function resolveApiKey(providerId, configKey) {
817
859
  if (configKey !== void 0) return configKey;
818
860
  const envVar = PROVIDER_ENV_KEYS[providerId];
@@ -970,6 +1012,50 @@ var SdkAdapter = class extends BaseAdapter {
970
1012
  }
971
1013
  return options;
972
1014
  }
1015
+ /**
1016
+ * generateText path (text / absent responseFormat) — unchanged behavior.
1017
+ */
1018
+ async completeText(sdk, options) {
1019
+ const result = await sdk.generateText(options);
1020
+ return {
1021
+ content: [{ type: "text", text: result.text }],
1022
+ usage: {
1023
+ inputTokens: result.usage.inputTokens ?? 0,
1024
+ outputTokens: result.usage.outputTokens ?? 0,
1025
+ totalTokens: result.usage.totalTokens ?? 0
1026
+ },
1027
+ stopReason: mapFinishReason(result.finishReason),
1028
+ model: result.response.modelId
1029
+ };
1030
+ }
1031
+ /**
1032
+ * generateObject path (#3433) — json_object / json_schema responseFormat.
1033
+ *
1034
+ * Uses the AI SDK `jsonSchema` helper to build the schema handle
1035
+ * (permissive `{ type: 'object' }` for json_object), then stringifies the
1036
+ * returned object into a text content block so downstream parsers /
1037
+ * extractTextFromResponse keep working unchanged.
1038
+ */
1039
+ async completeStructured(sdk, options, responseFormat) {
1040
+ const rawSchema = responseFormat.type === "json_schema" ? responseFormat.schema : { type: "object" };
1041
+ const schema = sdk.jsonSchema(rawSchema);
1042
+ const result = await sdk.generateObject({ ...options, schema });
1043
+ if (!isGenerateObjectResult(result)) {
1044
+ throw new Error(
1045
+ "AI SDK generateObject returned an unexpected result shape (missing object/usage/finishReason/response.modelId)"
1046
+ );
1047
+ }
1048
+ return {
1049
+ content: [{ type: "text", text: JSON.stringify(result.object) }],
1050
+ usage: {
1051
+ inputTokens: result.usage.inputTokens ?? 0,
1052
+ outputTokens: result.usage.outputTokens ?? 0,
1053
+ totalTokens: result.usage.totalTokens ?? 0
1054
+ },
1055
+ stopReason: mapFinishReason(result.finishReason),
1056
+ model: result.response.modelId
1057
+ };
1058
+ }
973
1059
  async complete(request) {
974
1060
  try {
975
1061
  await this.ensureInitialized();
@@ -981,17 +1067,8 @@ var SdkAdapter = class extends BaseAdapter {
981
1067
  );
982
1068
  }
983
1069
  const options = this.buildSdkOptions(request);
984
- const result = await sdk.generateText(options);
985
- const response = {
986
- content: [{ type: "text", text: result.text }],
987
- usage: {
988
- inputTokens: result.usage.inputTokens ?? 0,
989
- outputTokens: result.usage.outputTokens ?? 0,
990
- totalTokens: result.usage.totalTokens ?? 0
991
- },
992
- stopReason: mapFinishReason(result.finishReason),
993
- model: result.response.modelId
994
- };
1070
+ const responseFormat = request.responseFormat;
1071
+ const response = responseFormat !== void 0 && responseFormat.type !== "text" ? await this.completeStructured(sdk, options, responseFormat) : await this.completeText(sdk, options);
995
1072
  this.logResponse(response);
996
1073
  return ok(response);
997
1074
  } catch (error) {
@@ -1014,6 +1091,7 @@ var SdkAdapter = class extends BaseAdapter {
1014
1091
  let index = 0;
1015
1092
  yield { type: "content_block_start", index, contentBlock: { type: "text", text: "" } };
1016
1093
  for await (const text of result.textStream) {
1094
+ if (text === "") continue;
1017
1095
  yield {
1018
1096
  type: "content_block_delta",
1019
1097
  index,
@@ -4687,4 +4765,4 @@ export {
4687
4765
  SdkAdapter,
4688
4766
  createAutoAdapter
4689
4767
  };
4690
- //# sourceMappingURL=chunk-GMHCPEHS.js.map
4768
+ //# sourceMappingURL=chunk-N5I4PC3P.js.map