nexus-agents 10.2.0 → 11.0.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 (179) hide show
  1. package/README.md +4 -1
  2. package/dist/{ast-rule-runner-RI7ZJ4ZI.js → ast-rule-runner-3QRCP53C.js} +5 -5
  3. package/dist/{child-mcp-config-73BX4OTR.js → child-mcp-config-NWPUDUVE.js} +5 -5
  4. package/dist/{chunk-GAX75XCV.js → chunk-2BZXKNLN.js} +2 -2
  5. package/dist/{chunk-QG3VDSWD.js → chunk-2HYJTIDY.js} +3 -3
  6. package/dist/{chunk-OMSKBBL7.js → chunk-377L2ZEK.js} +18 -19
  7. package/dist/chunk-377L2ZEK.js.map +1 -0
  8. package/dist/{chunk-VLOPXV47.js → chunk-53XY7YJD.js} +7 -7
  9. package/dist/chunk-5CUI42NO.js +1046 -0
  10. package/dist/chunk-5CUI42NO.js.map +1 -0
  11. package/dist/{chunk-23QA6GQD.js → chunk-66BE4KF4.js} +7 -7
  12. package/dist/{chunk-TDUXUVUU.js → chunk-6LWUJOAS.js} +2 -2
  13. package/dist/{chunk-XDUTAUK3.js → chunk-7H3SOL4O.js} +65 -57
  14. package/dist/chunk-7H3SOL4O.js.map +1 -0
  15. package/dist/{chunk-JGY5HMWI.js → chunk-7HHXY7LM.js} +2 -2
  16. package/dist/{chunk-J44RKRGQ.js → chunk-AFJE7K6Q.js} +4 -4
  17. package/dist/{chunk-WD4DENPC.js → chunk-B3RAA47W.js} +3 -3
  18. package/dist/{chunk-TW6PJYTP.js → chunk-C73VZSC5.js} +1 -1
  19. package/dist/chunk-C73VZSC5.js.map +1 -0
  20. package/dist/{chunk-LWOX65CP.js → chunk-CH6IBWXH.js} +31 -31
  21. package/dist/chunk-CH6IBWXH.js.map +1 -0
  22. package/dist/{chunk-RIYFVDYN.js → chunk-CSDGNGAG.js} +2 -2
  23. package/dist/{chunk-KASBBKAQ.js → chunk-D33IH5VS.js} +54 -54
  24. package/dist/chunk-D33IH5VS.js.map +1 -0
  25. package/dist/{chunk-U37AITDA.js → chunk-DCPCO2XG.js} +2 -2
  26. package/dist/{chunk-SS53PZO6.js → chunk-EFZRQ67X.js} +5 -5
  27. package/dist/{chunk-WKFPKPEM.js → chunk-GPRYEKAA.js} +434 -124
  28. package/dist/chunk-GPRYEKAA.js.map +1 -0
  29. package/dist/{chunk-7EV3QAKO.js → chunk-HUYVIUF3.js} +2 -2
  30. package/dist/{chunk-V26YQQK4.js → chunk-I7VX7JWV.js} +2 -2
  31. package/dist/{chunk-LXYT2P2Y.js → chunk-IGYZQEKM.js} +3 -3
  32. package/dist/{chunk-XSAUVC6D.js → chunk-NLFCAEEK.js} +2 -2
  33. package/dist/{chunk-2TA6KTRF.js → chunk-OJ3PXTN3.js} +16 -18
  34. package/dist/{chunk-2TA6KTRF.js.map → chunk-OJ3PXTN3.js.map} +1 -1
  35. package/dist/{chunk-QRC3QLUQ.js → chunk-PY6HJQYC.js} +1086 -14
  36. package/dist/chunk-PY6HJQYC.js.map +1 -0
  37. package/dist/{chunk-QM5W3T3O.js → chunk-PZMODRBC.js} +2 -2
  38. package/dist/{chunk-GX5ZHZR2.js → chunk-QARJHIQM.js} +7 -7
  39. package/dist/{chunk-EJ5EBTAV.js → chunk-QTRPH2ZF.js} +2 -2
  40. package/dist/{chunk-FNHVTDHZ.js → chunk-R75NZRON.js} +3 -3
  41. package/dist/{chunk-UE7A2XHM.js → chunk-RUHKJC3X.js} +2 -2
  42. package/dist/{chunk-4JJI34RV.js → chunk-S6XQFO4A.js} +3 -3
  43. package/dist/{chunk-LP5ISXKM.js → chunk-SFWQ72ZS.js} +5 -5
  44. package/dist/{chunk-KLSNDPU6.js → chunk-SKM75Y7E.js} +17 -32
  45. package/dist/chunk-SKM75Y7E.js.map +1 -0
  46. package/dist/{chunk-D6E3LJGP.js → chunk-T4WTZTZY.js} +4 -4
  47. package/dist/{chunk-JPFRRNRR.js → chunk-U7HL7FGS.js} +2 -2
  48. package/dist/{chunk-SX2XWCGA.js → chunk-V6AJCRAU.js} +22 -7
  49. package/dist/chunk-V6AJCRAU.js.map +1 -0
  50. package/dist/{chunk-2DYCHHWW.js → chunk-VVMSE5LF.js} +4 -4
  51. package/dist/{chunk-QS5HGPCN.js → chunk-VZ7YZ2EF.js} +2 -2
  52. package/dist/{chunk-PXYSAG5B.js → chunk-WFMMJWMO.js} +2 -2
  53. package/dist/{chunk-FWQSOPBA.js → chunk-YOT3XEZM.js} +6 -6
  54. package/dist/{chunk-JUUGLXSN.js → chunk-YRZV6KXZ.js} +5 -5
  55. package/dist/{cli-auth-probe-A7ANEHJ7.js → cli-auth-probe-GUK3NXW3.js} +2 -2
  56. package/dist/{cli-circuit-breaker-62YOQQT3.js → cli-circuit-breaker-VWARWNGU.js} +5 -5
  57. package/dist/{cli-readiness-O6EOQV4L.js → cli-readiness-S23DWZCS.js} +9 -9
  58. package/dist/cli-server-gateway-DJXSHHXA.js +34 -0
  59. package/dist/cli.d.ts +1 -1
  60. package/dist/cli.js +80 -1215
  61. package/dist/cli.js.map +1 -1
  62. package/dist/{composite-router-YPZ3D5Y6.js → composite-router-3CBLBUGZ.js} +4 -4
  63. package/dist/{consensus-vote-RNQQUBXO.js → consensus-vote-JMWTJO4M.js} +21 -22
  64. package/dist/{consensus-vote-types-DexdhOdB.d.ts → consensus-vote-types-Ca0BY3kZ.d.ts} +5 -5
  65. package/dist/{context-retriever-HVPLM6DH.js → context-retriever-P6ZE6UXS.js} +11 -11
  66. package/dist/{doctor-deep-UZQTD65D.js → doctor-deep-HBIDPGWI.js} +5 -5
  67. package/dist/doctor-gateway-report-FWV7UN2Q.js +20 -0
  68. package/dist/{doctor-live-QFLT5SDB.js → doctor-live-676TWMP6.js} +18 -16
  69. package/dist/doctor-live-676TWMP6.js.map +1 -0
  70. package/dist/{expert-bridge-O3UZXLGB.js → expert-bridge-3VWOHLC7.js} +6 -6
  71. package/dist/factory-A44THDRG.js +31 -0
  72. package/dist/factory-YPJXDMN4.js +17 -0
  73. package/dist/{improvement-review-7IABFAU3.js → improvement-review-FIKPTCPE.js} +8 -8
  74. package/dist/index.d.ts +69 -77
  75. package/dist/index.js +65 -56
  76. package/dist/index.js.map +1 -1
  77. package/dist/{init-opencode-RYPBCM4P.js → init-opencode-YRHLGHIE.js} +7 -7
  78. package/dist/issue-triage-BLZHWC6U.js +20 -0
  79. package/dist/{learning-persistence-FB4U5AX2.js → learning-persistence-X24OOBYY.js} +3 -3
  80. package/dist/{model-config-helpers-QNV3KD2W.js → model-config-helpers-OQEZU2ZG.js} +4 -4
  81. package/dist/{openai-compat-adapter-GPZDIR3D.js → openai-compat-adapter-GRJA6RPZ.js} +7 -7
  82. package/dist/{outcome-store-adapter-WRONSTUD.js → outcome-store-adapter-7CKYWUZV.js} +4 -4
  83. package/dist/{pr-reviewer-helpers-7SMNNDFJ.js → pr-reviewer-helpers-LKHPRW3X.js} +6 -6
  84. package/dist/{registry-command-U7K6HIBK.js → registry-command-4LP4OSND.js} +4 -4
  85. package/dist/{repo-security-plan-LFWLVEL2.js → repo-security-plan-SOFUCHO4.js} +5 -5
  86. package/dist/research-helpers-synthesize-SXNAJWHX.js +16 -0
  87. package/dist/{routing-memory-OCIVXKOU.js → routing-memory-TWCXFPAD.js} +4 -4
  88. package/dist/{session-memory-4LMEWO3X.js → session-memory-YVT5M4AN.js} +5 -5
  89. package/dist/setup-config-JGHFR4P3.js +13 -0
  90. package/dist/{setup-custom-api-QEPUCHOX.js → setup-custom-api-EFAWNJ2D.js} +6 -6
  91. package/dist/setup-data-dir-Z4IQNMRC.js +34 -0
  92. package/dist/{tool-memory-EFGFXF65.js → tool-memory-5Y3HKHCB.js} +8 -8
  93. package/dist/{tool-memory-registry-adapters-FO6JVQSX.js → tool-memory-registry-adapters-IIORYBCC.js} +3 -3
  94. package/dist/unified-registry-IDUII5J7.js +35 -0
  95. package/dist/{weather-report-PMYQ3WBW.js → weather-report-KA5RT57Q.js} +4 -4
  96. package/dist/weather-report-KA5RT57Q.js.map +1 -0
  97. package/package.json +1 -1
  98. package/dist/chunk-KASBBKAQ.js.map +0 -1
  99. package/dist/chunk-KLSNDPU6.js.map +0 -1
  100. package/dist/chunk-LWOX65CP.js.map +0 -1
  101. package/dist/chunk-OMSKBBL7.js.map +0 -1
  102. package/dist/chunk-QRC3QLUQ.js.map +0 -1
  103. package/dist/chunk-SX2XWCGA.js.map +0 -1
  104. package/dist/chunk-TW6PJYTP.js.map +0 -1
  105. package/dist/chunk-UZJ6IPJR.js +0 -1057
  106. package/dist/chunk-UZJ6IPJR.js.map +0 -1
  107. package/dist/chunk-WKFPKPEM.js.map +0 -1
  108. package/dist/chunk-XDUTAUK3.js.map +0 -1
  109. package/dist/doctor-gateway-report-LRBPPJD4.js +0 -20
  110. package/dist/doctor-live-QFLT5SDB.js.map +0 -1
  111. package/dist/factory-KCKQQAEH.js +0 -31
  112. package/dist/factory-LUKUIB6C.js +0 -17
  113. package/dist/issue-triage-YBVJVAUU.js +0 -20
  114. package/dist/research-helpers-synthesize-X6MVAAKT.js +0 -16
  115. package/dist/setup-config-YNNNR4AE.js +0 -13
  116. package/dist/setup-data-dir-XM5DWH7R.js +0 -35
  117. package/dist/unified-registry-KLNC24HA.js +0 -36
  118. /package/dist/{ast-rule-runner-RI7ZJ4ZI.js.map → ast-rule-runner-3QRCP53C.js.map} +0 -0
  119. /package/dist/{child-mcp-config-73BX4OTR.js.map → child-mcp-config-NWPUDUVE.js.map} +0 -0
  120. /package/dist/{chunk-GAX75XCV.js.map → chunk-2BZXKNLN.js.map} +0 -0
  121. /package/dist/{chunk-QG3VDSWD.js.map → chunk-2HYJTIDY.js.map} +0 -0
  122. /package/dist/{chunk-VLOPXV47.js.map → chunk-53XY7YJD.js.map} +0 -0
  123. /package/dist/{chunk-23QA6GQD.js.map → chunk-66BE4KF4.js.map} +0 -0
  124. /package/dist/{chunk-TDUXUVUU.js.map → chunk-6LWUJOAS.js.map} +0 -0
  125. /package/dist/{chunk-JGY5HMWI.js.map → chunk-7HHXY7LM.js.map} +0 -0
  126. /package/dist/{chunk-J44RKRGQ.js.map → chunk-AFJE7K6Q.js.map} +0 -0
  127. /package/dist/{chunk-WD4DENPC.js.map → chunk-B3RAA47W.js.map} +0 -0
  128. /package/dist/{chunk-RIYFVDYN.js.map → chunk-CSDGNGAG.js.map} +0 -0
  129. /package/dist/{chunk-U37AITDA.js.map → chunk-DCPCO2XG.js.map} +0 -0
  130. /package/dist/{chunk-SS53PZO6.js.map → chunk-EFZRQ67X.js.map} +0 -0
  131. /package/dist/{chunk-7EV3QAKO.js.map → chunk-HUYVIUF3.js.map} +0 -0
  132. /package/dist/{chunk-V26YQQK4.js.map → chunk-I7VX7JWV.js.map} +0 -0
  133. /package/dist/{chunk-LXYT2P2Y.js.map → chunk-IGYZQEKM.js.map} +0 -0
  134. /package/dist/{chunk-XSAUVC6D.js.map → chunk-NLFCAEEK.js.map} +0 -0
  135. /package/dist/{chunk-QM5W3T3O.js.map → chunk-PZMODRBC.js.map} +0 -0
  136. /package/dist/{chunk-GX5ZHZR2.js.map → chunk-QARJHIQM.js.map} +0 -0
  137. /package/dist/{chunk-EJ5EBTAV.js.map → chunk-QTRPH2ZF.js.map} +0 -0
  138. /package/dist/{chunk-FNHVTDHZ.js.map → chunk-R75NZRON.js.map} +0 -0
  139. /package/dist/{chunk-UE7A2XHM.js.map → chunk-RUHKJC3X.js.map} +0 -0
  140. /package/dist/{chunk-4JJI34RV.js.map → chunk-S6XQFO4A.js.map} +0 -0
  141. /package/dist/{chunk-LP5ISXKM.js.map → chunk-SFWQ72ZS.js.map} +0 -0
  142. /package/dist/{chunk-D6E3LJGP.js.map → chunk-T4WTZTZY.js.map} +0 -0
  143. /package/dist/{chunk-JPFRRNRR.js.map → chunk-U7HL7FGS.js.map} +0 -0
  144. /package/dist/{chunk-2DYCHHWW.js.map → chunk-VVMSE5LF.js.map} +0 -0
  145. /package/dist/{chunk-QS5HGPCN.js.map → chunk-VZ7YZ2EF.js.map} +0 -0
  146. /package/dist/{chunk-PXYSAG5B.js.map → chunk-WFMMJWMO.js.map} +0 -0
  147. /package/dist/{chunk-FWQSOPBA.js.map → chunk-YOT3XEZM.js.map} +0 -0
  148. /package/dist/{chunk-JUUGLXSN.js.map → chunk-YRZV6KXZ.js.map} +0 -0
  149. /package/dist/{cli-auth-probe-A7ANEHJ7.js.map → cli-auth-probe-GUK3NXW3.js.map} +0 -0
  150. /package/dist/{cli-circuit-breaker-62YOQQT3.js.map → cli-circuit-breaker-VWARWNGU.js.map} +0 -0
  151. /package/dist/{cli-readiness-O6EOQV4L.js.map → cli-readiness-S23DWZCS.js.map} +0 -0
  152. /package/dist/{composite-router-YPZ3D5Y6.js.map → cli-server-gateway-DJXSHHXA.js.map} +0 -0
  153. /package/dist/{consensus-vote-RNQQUBXO.js.map → composite-router-3CBLBUGZ.js.map} +0 -0
  154. /package/dist/{context-retriever-HVPLM6DH.js.map → consensus-vote-JMWTJO4M.js.map} +0 -0
  155. /package/dist/{doctor-deep-UZQTD65D.js.map → context-retriever-P6ZE6UXS.js.map} +0 -0
  156. /package/dist/{doctor-gateway-report-LRBPPJD4.js.map → doctor-deep-HBIDPGWI.js.map} +0 -0
  157. /package/dist/{expert-bridge-O3UZXLGB.js.map → doctor-gateway-report-FWV7UN2Q.js.map} +0 -0
  158. /package/dist/{factory-KCKQQAEH.js.map → expert-bridge-3VWOHLC7.js.map} +0 -0
  159. /package/dist/{factory-LUKUIB6C.js.map → factory-A44THDRG.js.map} +0 -0
  160. /package/dist/{improvement-review-7IABFAU3.js.map → factory-YPJXDMN4.js.map} +0 -0
  161. /package/dist/{issue-triage-YBVJVAUU.js.map → improvement-review-FIKPTCPE.js.map} +0 -0
  162. /package/dist/{init-opencode-RYPBCM4P.js.map → init-opencode-YRHLGHIE.js.map} +0 -0
  163. /package/dist/{learning-persistence-FB4U5AX2.js.map → issue-triage-BLZHWC6U.js.map} +0 -0
  164. /package/dist/{model-config-helpers-QNV3KD2W.js.map → learning-persistence-X24OOBYY.js.map} +0 -0
  165. /package/dist/{openai-compat-adapter-GPZDIR3D.js.map → model-config-helpers-OQEZU2ZG.js.map} +0 -0
  166. /package/dist/{pr-reviewer-helpers-7SMNNDFJ.js.map → openai-compat-adapter-GRJA6RPZ.js.map} +0 -0
  167. /package/dist/{outcome-store-adapter-WRONSTUD.js.map → outcome-store-adapter-7CKYWUZV.js.map} +0 -0
  168. /package/dist/{repo-security-plan-LFWLVEL2.js.map → pr-reviewer-helpers-LKHPRW3X.js.map} +0 -0
  169. /package/dist/{registry-command-U7K6HIBK.js.map → registry-command-4LP4OSND.js.map} +0 -0
  170. /package/dist/{research-helpers-synthesize-X6MVAAKT.js.map → repo-security-plan-SOFUCHO4.js.map} +0 -0
  171. /package/dist/{routing-memory-OCIVXKOU.js.map → research-helpers-synthesize-SXNAJWHX.js.map} +0 -0
  172. /package/dist/{session-memory-4LMEWO3X.js.map → routing-memory-TWCXFPAD.js.map} +0 -0
  173. /package/dist/{setup-config-YNNNR4AE.js.map → session-memory-YVT5M4AN.js.map} +0 -0
  174. /package/dist/{setup-data-dir-XM5DWH7R.js.map → setup-config-JGHFR4P3.js.map} +0 -0
  175. /package/dist/{setup-custom-api-QEPUCHOX.js.map → setup-custom-api-EFAWNJ2D.js.map} +0 -0
  176. /package/dist/{tool-memory-EFGFXF65.js.map → setup-data-dir-Z4IQNMRC.js.map} +0 -0
  177. /package/dist/{tool-memory-registry-adapters-FO6JVQSX.js.map → tool-memory-5Y3HKHCB.js.map} +0 -0
  178. /package/dist/{unified-registry-KLNC24HA.js.map → tool-memory-registry-adapters-IIORYBCC.js.map} +0 -0
  179. /package/dist/{weather-report-PMYQ3WBW.js.map → unified-registry-IDUII5J7.js.map} +0 -0
@@ -1,1057 +0,0 @@
1
- import {
2
- createAutoAdapter
3
- } from "./chunk-QRC3QLUQ.js";
4
- import {
5
- ensureGatewayDiscovered,
6
- gatewayDiscoveryGeneration
7
- } from "./chunk-LXYT2P2Y.js";
8
- import {
9
- getDefaultCliCircuitBreakerRegistry
10
- } from "./chunk-SX2XWCGA.js";
11
- import {
12
- CircularBuffer,
13
- ConfigError,
14
- ModelError,
15
- UNRESOLVED_MODEL_ID,
16
- breakerKeys,
17
- createLogger,
18
- err,
19
- getDefaultModelForCli,
20
- getErrorMessage,
21
- getInTreeCapabilitiesMatrix,
22
- getRandomProvider,
23
- getTimeProvider,
24
- isCallerCancelled,
25
- isCliName,
26
- isDurableCapacityError,
27
- isEndpointArmId,
28
- isGatewayArmId,
29
- isRateLimitLikeError,
30
- mapModelErrorToCategory,
31
- ok,
32
- recordRateLimitEvent,
33
- toRateLimitError,
34
- warnIfGatewayCostUndeclared
35
- } from "./chunk-WKFPKPEM.js";
36
- import {
37
- TASK_SPECIALIZATION_MATRIX,
38
- detectTaskCategory
39
- } from "./chunk-4PN3UVZV.js";
40
-
41
- // src/agents/collaboration/event-bus-helpers.ts
42
- import { randomUUID } from "node:crypto";
43
- var DEFAULT_MAX_HISTORY_SIZE = 1e3;
44
- var MAX_SUBSCRIPTIONS = 500;
45
- var MAX_TOPIC_PATTERN_LENGTH = 200;
46
- function patternToRegex(pattern) {
47
- if (pattern.length > MAX_TOPIC_PATTERN_LENGTH) {
48
- throw new Error(`Topic pattern exceeds maximum length of ${String(MAX_TOPIC_PATTERN_LENGTH)}`);
49
- }
50
- if (pattern === "*") {
51
- return /^.+$/;
52
- }
53
- const WILDCARD_PLACEHOLDER = "\0WILDCARD\0";
54
- const withPlaceholder = pattern.replace(/\*/g, WILDCARD_PLACEHOLDER);
55
- const escaped = withPlaceholder.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
56
- const withWildcards = escaped.replace(new RegExp(WILDCARD_PLACEHOLDER, "g"), "[^.]+");
57
- return new RegExp(`^${withWildcards}$`);
58
- }
59
- function topicMatchesPattern(topic, regex) {
60
- return regex.test(topic);
61
- }
62
- function generateEventId() {
63
- return `evt-${String(getTimeProvider().now())}-${randomUUID().slice(0, 8)}`;
64
- }
65
- function generateSubscriptionId() {
66
- return `sub-${String(getTimeProvider().now())}-${randomUUID().slice(0, 8)}`;
67
- }
68
- function applyHistoryFilters(history, filter) {
69
- let result = history;
70
- if (filter.topic !== void 0 && filter.topic !== "") {
71
- const regex = patternToRegex(filter.topic);
72
- result = result.filter((e) => topicMatchesPattern(e.topic, regex));
73
- }
74
- if (filter.sessionId !== void 0 && filter.sessionId !== "") {
75
- result = result.filter((e) => e.sessionId === filter.sessionId);
76
- }
77
- if (filter.correlationId !== void 0 && filter.correlationId !== "") {
78
- result = result.filter((e) => e.correlationId === filter.correlationId);
79
- }
80
- return applyTimestampFilters(result, filter);
81
- }
82
- function applyTimestampFilters(result, filter) {
83
- let filtered = result;
84
- if (filter.after !== void 0 && filter.after !== "") {
85
- const afterTimestamp = filter.after;
86
- filtered = filtered.filter((e) => e.timestamp > afterTimestamp);
87
- }
88
- if (filter.before !== void 0 && filter.before !== "") {
89
- const beforeTimestamp = filter.before;
90
- filtered = filtered.filter((e) => e.timestamp < beforeTimestamp);
91
- }
92
- return filtered;
93
- }
94
- function applyHistoryLimit(result, filter) {
95
- if (filter.limit !== void 0 && filter.limit > 0) {
96
- return result.slice(-filter.limit);
97
- }
98
- return result;
99
- }
100
- function enrichEvent(event) {
101
- return {
102
- ...event,
103
- eventId: event.eventId || generateEventId(),
104
- timestamp: event.timestamp || getTimeProvider().nowIso()
105
- };
106
- }
107
- function countMatchingSubscribers(topic, subscriptions) {
108
- let count = 0;
109
- for (const record of subscriptions.values()) {
110
- if (topicMatchesPattern(topic, record.regex)) {
111
- count++;
112
- }
113
- }
114
- return count;
115
- }
116
-
117
- // src/agents/collaboration/event-bus.ts
118
- var CollaborationEventBus = class {
119
- subscriptions = /* @__PURE__ */ new Map();
120
- history;
121
- asyncHandling;
122
- logger;
123
- stats = {
124
- eventsEmitted: 0,
125
- subscriptionsCreated: 0,
126
- activeSubscriptions: 0,
127
- historySize: 0,
128
- errorCount: 0
129
- };
130
- constructor(options = {}) {
131
- const maxHistorySize = options.maxHistorySize ?? DEFAULT_MAX_HISTORY_SIZE;
132
- this.history = new CircularBuffer(maxHistorySize);
133
- this.asyncHandling = options.asyncHandling ?? false;
134
- this.logger = options.logger;
135
- }
136
- /**
137
- * Emit an event synchronously to all matching subscribers.
138
- */
139
- emit(event) {
140
- const enrichedEvent = enrichEvent(event);
141
- this.addToHistory(enrichedEvent);
142
- this.stats.eventsEmitted++;
143
- this.logger?.debug("Event emitted", {
144
- topic: enrichedEvent.topic,
145
- eventId: enrichedEvent.eventId,
146
- subscriberCount: countMatchingSubscribers(enrichedEvent.topic, this.subscriptions)
147
- });
148
- for (const record of this.subscriptions.values()) {
149
- if (topicMatchesPattern(enrichedEvent.topic, record.regex)) {
150
- this.invokeListener(record, enrichedEvent);
151
- }
152
- }
153
- }
154
- /**
155
- * Emit an event and wait for all async handlers to complete.
156
- */
157
- async emitAsync(event) {
158
- const enrichedEvent = enrichEvent(event);
159
- this.addToHistory(enrichedEvent);
160
- this.stats.eventsEmitted++;
161
- this.logger?.debug("Async event emitted", {
162
- topic: enrichedEvent.topic,
163
- eventId: enrichedEvent.eventId
164
- });
165
- const promises = [];
166
- for (const record of this.subscriptions.values()) {
167
- if (topicMatchesPattern(enrichedEvent.topic, record.regex)) {
168
- promises.push(this.invokeListenerAsync(record, enrichedEvent));
169
- }
170
- }
171
- await Promise.all(promises);
172
- }
173
- /**
174
- * Subscribe to events matching a topic pattern.
175
- */
176
- subscribe(pattern, listener) {
177
- if (this.subscriptions.size >= MAX_SUBSCRIPTIONS) {
178
- throw new Error(
179
- `Maximum subscription limit reached (${String(MAX_SUBSCRIPTIONS)}). Unsubscribe from unused patterns before adding new ones.`
180
- );
181
- }
182
- const id = generateSubscriptionId();
183
- const regex = patternToRegex(pattern);
184
- const record = {
185
- id,
186
- pattern,
187
- regex,
188
- listener
189
- };
190
- this.subscriptions.set(id, record);
191
- this.stats.subscriptionsCreated++;
192
- this.stats.activeSubscriptions = this.subscriptions.size;
193
- this.logger?.debug("Subscription created", { id, pattern });
194
- return {
195
- id,
196
- pattern,
197
- unsubscribe: () => {
198
- this.unsubscribe(id);
199
- }
200
- };
201
- }
202
- /**
203
- * Unsubscribe from events.
204
- */
205
- unsubscribe(subscriptionId) {
206
- const existed = this.subscriptions.delete(subscriptionId);
207
- if (existed) {
208
- this.stats.activeSubscriptions = this.subscriptions.size;
209
- this.logger?.debug("Subscription removed", { subscriptionId });
210
- }
211
- }
212
- /**
213
- * Get event history matching filter criteria.
214
- */
215
- getHistory(filter) {
216
- const historyArray = this.history.toArray();
217
- if (filter === void 0) {
218
- return historyArray;
219
- }
220
- const filtered = applyHistoryFilters(historyArray, filter);
221
- return applyHistoryLimit(filtered, filter);
222
- }
223
- /**
224
- * Clear event history.
225
- */
226
- clearHistory() {
227
- this.history.clear();
228
- this.stats.historySize = 0;
229
- this.logger?.debug("History cleared");
230
- }
231
- /**
232
- * Get event bus statistics.
233
- */
234
- getStats() {
235
- return { ...this.stats };
236
- }
237
- /**
238
- * Check if a topic pattern has any subscribers.
239
- */
240
- hasSubscribers(pattern) {
241
- const regex = patternToRegex(pattern);
242
- for (const record of this.subscriptions.values()) {
243
- if (topicMatchesPattern(pattern, record.regex) || topicMatchesPattern(record.pattern, regex)) {
244
- return true;
245
- }
246
- }
247
- return false;
248
- }
249
- /**
250
- * Add event to history, maintaining size limit via O(1) circular buffer.
251
- * @see Issue #407 - Performance optimization
252
- */
253
- addToHistory(event) {
254
- this.history.push(event);
255
- this.stats.historySize = this.history.size;
256
- }
257
- /**
258
- * Invoke a listener synchronously with error isolation.
259
- */
260
- invokeListener(record, event) {
261
- try {
262
- const result = record.listener(event);
263
- if (this.asyncHandling && result instanceof Promise) {
264
- result.catch((error) => {
265
- this.handleListenerError(record, event, error);
266
- });
267
- }
268
- } catch (error) {
269
- this.handleListenerError(record, event, error);
270
- }
271
- }
272
- /**
273
- * Invoke a listener asynchronously.
274
- */
275
- async invokeListenerAsync(record, event) {
276
- try {
277
- await record.listener(event);
278
- } catch (error) {
279
- this.handleListenerError(record, event, error);
280
- }
281
- }
282
- /**
283
- * Handle listener errors without disrupting other listeners.
284
- */
285
- handleListenerError(record, event, error) {
286
- this.stats.errorCount++;
287
- const errorObj = error instanceof Error ? error : new Error(String(error));
288
- this.logger?.error("Event listener error", errorObj, {
289
- subscriptionId: record.id,
290
- pattern: record.pattern,
291
- eventTopic: event.topic,
292
- eventId: event.eventId
293
- });
294
- }
295
- };
296
- var globalBus = null;
297
- function getGlobalEventBus(options) {
298
- globalBus ??= new CollaborationEventBus(options);
299
- return globalBus;
300
- }
301
- function createEvent(topic, payload, options) {
302
- return {
303
- eventId: generateEventId(),
304
- timestamp: getTimeProvider().nowIso(),
305
- topic,
306
- payload,
307
- sessionId: options?.sessionId,
308
- correlationId: options?.correlationId
309
- };
310
- }
311
-
312
- // src/agents/collaboration/event-bus-topics.ts
313
- var EventTopics = {
314
- // Session events
315
- SESSION_CREATED: "session.created",
316
- SESSION_STATUS_CHANGED: "session.status_changed",
317
- SESSION_PARTICIPANT_JOINED: "session.participant_joined",
318
- SESSION_RESULT_SUBMITTED: "session.result_submitted",
319
- SESSION_FINALIZED: "session.finalized",
320
- SESSION_ALL: "session.*",
321
- // Message events
322
- MESSAGE_SENT: "message.sent",
323
- MESSAGE_RECEIVED: "message.received",
324
- MESSAGE_ALL: "message.*",
325
- // Agent events
326
- AGENT_TASK_DELEGATED: "agent.task_delegated",
327
- AGENT_RESULT_BROADCAST: "agent.result_broadcast",
328
- AGENT_ALL: "agent.*",
329
- // Consensus events
330
- CONSENSUS_VOTE_REQUESTED: "consensus.vote_requested",
331
- CONSENSUS_VOTE_CAST: "consensus.vote_cast",
332
- CONSENSUS_REACHED: "consensus.reached",
333
- CONSENSUS_ALL: "consensus.*",
334
- // Protocol events
335
- PROTOCOL_STARTED: "protocol.started",
336
- PROTOCOL_ITERATION: "protocol.iteration",
337
- PROTOCOL_COMPLETED: "protocol.completed",
338
- PROTOCOL_ALL: "protocol.*",
339
- // Aegean phase events (Issue #216)
340
- AEGEAN_ROUND_STARTED: "protocol.aegean.round_started",
341
- AEGEAN_VOTE_COLLECTED: "protocol.aegean.vote_collected",
342
- AEGEAN_QUORUM_DETECTED: "protocol.aegean.quorum_detected",
343
- AEGEAN_ALL: "protocol.aegean.*",
344
- // Reflexion phase events (Issue #216)
345
- REFLEXION_CRITIQUE_STARTED: "protocol.reflexion.critique_started",
346
- REFLEXION_CRITIQUE_COMPLETED: "protocol.reflexion.critique_completed",
347
- REFLEXION_SYNTHESIS: "protocol.reflexion.synthesis",
348
- REFLEXION_ALL: "protocol.reflexion.*",
349
- // Trinity phase events (Issue #216)
350
- TRINITY_PHASE_STARTED: "protocol.trinity.phase_started",
351
- TRINITY_PHASE_COMPLETED: "protocol.trinity.phase_completed",
352
- TRINITY_ALL: "protocol.trinity.*",
353
- // Byzantine detection events (Issue #218)
354
- BYZANTINE_WEIGHT_UPDATED: "byzantine.weight_updated",
355
- BYZANTINE_PATTERN_DETECTED: "byzantine.pattern_detected",
356
- BYZANTINE_AGENT_FLAGGED: "byzantine.agent_flagged",
357
- BYZANTINE_COLLUSION_SUSPECTED: "byzantine.collusion_suspected",
358
- BYZANTINE_ALL: "byzantine.*",
359
- // Wildcard
360
- ALL: "*"
361
- };
362
-
363
- // src/adapters/resilient-adapter.ts
364
- function createResilientAdapter(config) {
365
- return new ResilientAdapter(config);
366
- }
367
- var ResilientAdapter = class {
368
- logger;
369
- currentAdapter;
370
- currentSelection;
371
- hasEverDetected = false;
372
- health;
373
- preferredCli;
374
- failoverCallbacks = /* @__PURE__ */ new Set();
375
- circuitBreakerRegistry;
376
- circuitListener;
377
- disposed = false;
378
- defaultCliTimeoutMs;
379
- /** Inflight detection promise for coalescing concurrent calls (Issue #1423). */
380
- detectionPromise;
381
- /** The {@link gatewayDiscoveryGeneration} the current selection was made under (#6659). */
382
- detectedAtGeneration = 0;
383
- constructor(config) {
384
- this.logger = config?.logger ?? createLogger({ component: "resilient-adapter" });
385
- this.preferredCli = config?.preferredCli;
386
- this.defaultCliTimeoutMs = config?.defaultCliTimeoutMs;
387
- if (config?.circuitBreakerRegistry !== void 0) {
388
- this.attachCircuitBreakerRegistry(config.circuitBreakerRegistry);
389
- }
390
- }
391
- // --- IModelAdapter properties (forwarded) ---
392
- /**
393
- * Before detection a CLI-pinned proxy identifies as the CLI it was asked
394
- * for (`cli-<name>`, the id its detected `CliToModelAdapter` will carry);
395
- * an unpinned proxy stays `resilient-proxy`.
396
- *
397
- * #6119: the voter fallover (#3587) keys seats by this id and skips a seat
398
- * whose key equals the fallback's. When codex never detected, the pinned
399
- * seat and the unpinned default were BOTH `resilient-proxy`, so the seat was
400
- * read as "already on the fallback" and errored instead of falling over.
401
- */
402
- get providerId() {
403
- if (this.currentAdapter !== void 0) return this.currentAdapter.providerId;
404
- return this.preferredCli === void 0 ? "resilient-proxy" : `cli-${this.preferredCli}`;
405
- }
406
- get modelId() {
407
- return this.currentAdapter?.modelId ?? UNRESOLVED_MODEL_ID;
408
- }
409
- get capabilities() {
410
- return this.currentAdapter?.capabilities ?? [];
411
- }
412
- /**
413
- * The gateway arm of the adapter currently serving this proxy, when a
414
- * gateway model serves it (#6604 family slots); otherwise undefined. A
415
- * telemetry writer handed this proxy reads it (`isGatewayModelAdapter`) to
416
- * price the call by the gateway's `NEXUS_GATEWAY_COST` declaration instead
417
- * of a list price.
418
- */
419
- get gatewayArm() {
420
- const arm = this.currentAdapter?.gatewayArm;
421
- return typeof arm === "string" && isEndpointArmId(arm) ? arm : void 0;
422
- }
423
- // --- IModelAdapter methods ---
424
- async complete(request) {
425
- const adapter = await this.ensureAdapter();
426
- if (adapter === void 0) {
427
- return err(new ModelError("No model adapter available"));
428
- }
429
- const result = await adapter.complete(request);
430
- if (!result.ok) {
431
- if (isRateLimitLikeError(result.error)) {
432
- const rlError = toRateLimitError(result.error, adapter.providerId);
433
- recordRateLimitEvent({
434
- provider: adapter.providerId,
435
- timestamp: getTimeProvider().now(),
436
- retryAfterMs: rlError.retryAfterMs
437
- });
438
- this.logger.warn("Rate limit detected", {
439
- provider: adapter.providerId,
440
- retryAfterMs: rlError.retryAfterMs
441
- });
442
- }
443
- this.recordBreakerFailure(adapter, result.error);
444
- }
445
- return result;
446
- }
447
- async *stream(request) {
448
- const adapter = await this.ensureAdapter();
449
- if (adapter === void 0) {
450
- throw new ModelError("No model adapter available");
451
- }
452
- yield* adapter.stream(request);
453
- }
454
- async countTokens(text) {
455
- const adapter = await this.ensureAdapter();
456
- if (adapter === void 0) {
457
- return 0;
458
- }
459
- return adapter.countTokens(text);
460
- }
461
- validateConfig() {
462
- if (this.currentAdapter !== void 0) {
463
- return this.currentAdapter.validateConfig();
464
- }
465
- return ok(void 0);
466
- }
467
- // --- IResilientAdapter methods ---
468
- getHealth() {
469
- return this.health;
470
- }
471
- async refresh() {
472
- this.currentAdapter = void 0;
473
- this.currentSelection = void 0;
474
- this.detectionPromise = void 0;
475
- await this.ensureAdapter();
476
- }
477
- setPreferredCli(cli) {
478
- this.preferredCli = cli;
479
- this.currentAdapter = void 0;
480
- this.currentSelection = void 0;
481
- }
482
- onFailover(cb) {
483
- this.failoverCallbacks.add(cb);
484
- return () => {
485
- this.failoverCallbacks.delete(cb);
486
- };
487
- }
488
- /**
489
- * Attach a circuit breaker registry for automatic failover.
490
- * When the current adapter's circuit opens, the cached adapter
491
- * is cleared so the next call triggers re-detection.
492
- */
493
- /** The registry arming failover, or undefined if this adapter is unarmed (#4659). */
494
- getCircuitBreakerRegistry() {
495
- return this.circuitBreakerRegistry;
496
- }
497
- attachCircuitBreakerRegistry(registry) {
498
- this.detachCircuitBreakerRegistry();
499
- this.circuitBreakerRegistry = registry;
500
- this.circuitListener = (event) => {
501
- this.handleCircuitStateChange(event);
502
- };
503
- registry.addGlobalStateChangeListener(this.circuitListener);
504
- }
505
- dispose() {
506
- if (this.disposed) return;
507
- this.disposed = true;
508
- this.detachCircuitBreakerRegistry();
509
- this.failoverCallbacks.clear();
510
- }
511
- // --- Private methods ---
512
- async ensureAdapter() {
513
- if (this.currentAdapter === void 0) {
514
- await ensureGatewayDiscovered();
515
- } else {
516
- void ensureGatewayDiscovered();
517
- }
518
- if (this.currentAdapter !== void 0 && this.detectedAtGeneration !== gatewayDiscoveryGeneration()) {
519
- this.currentAdapter = void 0;
520
- this.currentSelection = void 0;
521
- this.hasEverDetected = false;
522
- }
523
- if (this.currentAdapter !== void 0) {
524
- return this.currentAdapter;
525
- }
526
- this.detectionPromise ??= this.detectAdapter().finally(() => {
527
- this.detectionPromise = void 0;
528
- });
529
- return this.detectionPromise;
530
- }
531
- async detectAdapter() {
532
- const generation = gatewayDiscoveryGeneration();
533
- try {
534
- this.logger.info("Detecting model adapter (lazy)");
535
- const config = {
536
- logger: this.logger,
537
- ...this.preferredCli !== void 0 && { preferredCli: this.preferredCli },
538
- ...this.defaultCliTimeoutMs !== void 0 && {
539
- defaultCliTimeoutMs: this.defaultCliTimeoutMs
540
- }
541
- };
542
- const selection = await createAutoAdapter(config);
543
- this.applySelection(selection);
544
- this.detectedAtGeneration = generation;
545
- return this.currentAdapter;
546
- } catch (error) {
547
- const message = getErrorMessage(error);
548
- this.logger.warn("No model adapter available", { error: message });
549
- this.health = {
550
- source: "api",
551
- state: "unavailable",
552
- selectedAt: /* @__PURE__ */ new Date(),
553
- failoverCount: this.health?.failoverCount ?? 0,
554
- lastError: message
555
- };
556
- return void 0;
557
- }
558
- }
559
- applySelection(selection) {
560
- const isFailover = this.hasEverDetected;
561
- const outgoingHealth = this.health;
562
- this.currentAdapter = selection.adapter;
563
- this.currentSelection = selection;
564
- this.hasEverDetected = true;
565
- const source = mapSelectionSource(selection);
566
- const failoverCount = (this.health?.failoverCount ?? 0) + (isFailover ? 1 : 0);
567
- this.health = {
568
- source,
569
- state: "healthy",
570
- selectedAt: /* @__PURE__ */ new Date(),
571
- failoverCount,
572
- ...selection.modelVerified !== void 0 && { modelVerified: selection.modelVerified }
573
- };
574
- this.logger.info("Adapter selected", {
575
- source: selection.source,
576
- name: selection.name,
577
- model: selection.adapter.modelId,
578
- provider: selection.adapter.providerId,
579
- failover: isFailover
580
- });
581
- if (isFailover) {
582
- this.emitFailover(
583
- outgoingHealth === void 0 ? void 0 : { ...outgoingHealth, failoverCount }
584
- );
585
- }
586
- }
587
- /**
588
- * Record a direct-API failure to the circuit breaker so API adapters get the
589
- * same degradation/failover learning that CLI subprocess failures get (#3423).
590
- *
591
- * The shared internal key policy preserves selected CLI slots, prefixes API
592
- * vendor names, and isolates gateway endpoints by slot (#7070).
593
- *
594
- * A CLI selection is NOT recorded here (#6712). Its CLI adapter's retry loop
595
- * (`executeCliRetryLoop`) has already recorded the failure in the same
596
- * registry under its route key, with the category taken from
597
- * the CliError code. Recording it again counted every CLI failure twice (the
598
- * second time usually as `unknown`), so the breaker opened at about half its
599
- * configured threshold. Only a direct-API selection, which has no inner
600
- * recorder, is recorded by this layer.
601
- *
602
- * Rate-limit failures are deliberately skipped here: they are already recorded
603
- * by the `recordRateLimitEvent` telemetry branch in `complete()`, and the
604
- * breaker counts rate limits as failures by default
605
- * (`countRateLimitsAsFailures`), so recording them again would double-count.
606
- *
607
- * Security (#3422 constraint): the logged payload carries only `provider` and
608
- * `category` — never `error.message`, the request, or the ModelError object,
609
- * any of which could embed credentials.
610
- */
611
- recordBreakerFailure(adapter, error) {
612
- if (this.circuitBreakerRegistry === void 0 || this.currentSelection === void 0) {
613
- return;
614
- }
615
- if (this.currentSelection.source === "cli") return;
616
- if (isCallerCancelled(error)) return;
617
- const category = mapModelErrorToCategory(error);
618
- const breaker = this.circuitBreakerRegistry.getArmBreaker(
619
- breakerKeys.forArm({
620
- name: this.currentSelection.name,
621
- model: adapter.modelId,
622
- gatewayArm: this.gatewayArm
623
- })
624
- );
625
- if (isDurableCapacityError(error)) {
626
- breaker.recordFailure(category);
627
- this.logger.warn("Durable capacity cap recorded to circuit breaker", {
628
- provider: adapter.providerId,
629
- category
630
- });
631
- return;
632
- }
633
- if (category === "rate_limit" || isRateLimitLikeError(error)) {
634
- return;
635
- }
636
- breaker.recordFailure(category);
637
- this.logger.warn("API failure recorded to circuit breaker", {
638
- provider: adapter.providerId,
639
- category
640
- });
641
- }
642
- handleCircuitStateChange(event) {
643
- if (event.newState !== "open") return;
644
- const selection = this.currentSelection;
645
- if (selection === void 0 || breakerKeys.forArm({
646
- name: selection.name,
647
- model: this.currentAdapter?.modelId,
648
- gatewayArm: this.gatewayArm
649
- }) !== event.armId) {
650
- return;
651
- }
652
- this.logger.warn("Current adapter circuit opened, clearing cache for re-detection", {
653
- cli: event.cliName,
654
- reason: event.reason
655
- });
656
- this.currentAdapter = void 0;
657
- this.currentSelection = void 0;
658
- if (this.health !== void 0) {
659
- this.health = {
660
- ...this.health,
661
- state: "degraded",
662
- lastError: event.reason
663
- };
664
- }
665
- }
666
- /**
667
- * Emit the failover signal describing the adapter we failed AWAY FROM (#4670).
668
- *
669
- * This used to read `this.health`, which `applySelection` had just replaced
670
- * with the incoming adapter's `state: 'healthy'`. So every `adapter.failover`
671
- * payload said healthy, `unhealthyCliFrom` (`observability/failover-signals.ts`)
672
- * returned undefined for all of them, and `signal.swarm_unhealthy` was never
673
- * produced by this — the more reliable of its two producers.
674
- *
675
- * An undefined outgoing health emits NOTHING rather than falling back to the
676
- * current one. Falling back would silently restore the old behaviour, and a
677
- * failover whose prior state we cannot describe is not evidence that the
678
- * prior adapter was healthy.
679
- */
680
- emitFailover(outgoingHealth) {
681
- const info = outgoingHealth;
682
- if (info === void 0) return;
683
- for (const cb of this.failoverCallbacks) {
684
- try {
685
- cb(info);
686
- } catch (error) {
687
- this.logger.debug("Failover callback error", { error: getErrorMessage(error) });
688
- }
689
- }
690
- try {
691
- const eventBus = getGlobalEventBus();
692
- eventBus.emit({
693
- eventId: `failover-${getTimeProvider().now().toString(36)}-${getRandomProvider().random().toString(36).slice(2, 8)}`,
694
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
695
- topic: "adapter.failover",
696
- payload: info
697
- });
698
- } catch (error) {
699
- this.logger.debug("CollaborationEventBus emit failed during failover", {
700
- error: getErrorMessage(error)
701
- });
702
- }
703
- }
704
- detachCircuitBreakerRegistry() {
705
- if (this.circuitBreakerRegistry !== void 0 && this.circuitListener !== void 0) {
706
- this.circuitBreakerRegistry.removeGlobalStateChangeListener(this.circuitListener);
707
- }
708
- this.circuitBreakerRegistry = void 0;
709
- this.circuitListener = void 0;
710
- }
711
- };
712
- function mapSelectionSource(selection) {
713
- if (selection.source === "cli") {
714
- return selection.name;
715
- }
716
- return "api";
717
- }
718
-
719
- // src/adapters/model-bound-adapter.ts
720
- var ModelBoundAdapter = class {
721
- constructor(slot, boundModel) {
722
- this.slot = slot;
723
- this.boundModel = boundModel;
724
- }
725
- slot;
726
- boundModel;
727
- get providerId() {
728
- return this.slot.providerId;
729
- }
730
- get modelId() {
731
- return this.slot.modelId;
732
- }
733
- get capabilities() {
734
- return this.slot.capabilities;
735
- }
736
- bind(request) {
737
- return request.model !== void 0 ? request : { ...request, model: this.boundModel };
738
- }
739
- complete(request) {
740
- return this.slot.complete(this.bind(request));
741
- }
742
- stream(request) {
743
- return this.slot.stream(this.bind(request));
744
- }
745
- countTokens(text) {
746
- return this.slot.countTokens(text);
747
- }
748
- validateConfig() {
749
- return this.slot.validateConfig();
750
- }
751
- getHealth() {
752
- return this.slot.getHealth();
753
- }
754
- refresh() {
755
- return this.slot.refresh();
756
- }
757
- setPreferredCli(cli) {
758
- this.slot.setPreferredCli(cli);
759
- }
760
- onFailover(cb) {
761
- return this.slot.onFailover(cb);
762
- }
763
- getCircuitBreakerRegistry() {
764
- return this.slot.getCircuitBreakerRegistry?.();
765
- }
766
- /** No-op: the shared slot adapter belongs to the registry, which disposes it. */
767
- dispose() {
768
- }
769
- };
770
-
771
- // src/adapters/unified-registry.ts
772
- var UnifiedAdapterRegistry = class {
773
- logger;
774
- defaultCliTimeoutMs;
775
- /**
776
- * Per-arm adapter cache. CLI slots are created lazily by
777
- * {@link getAdapterForCli}; `api:*` arms enter only through
778
- * {@link registerApiArm} and are never synthesised (#4392).
779
- */
780
- cliAdapters = /* @__PURE__ */ new Map();
781
- /** Default adapter for unscoped requests. */
782
- defaultAdapter;
783
- constructor(config) {
784
- this.logger = config?.logger ?? createLogger({ component: "unified-registry" });
785
- this.defaultCliTimeoutMs = config?.defaultCliTimeoutMs;
786
- this.logger.info("UnifiedAdapterRegistry initialized", {
787
- categories: TASK_SPECIALIZATION_MATRIX.length,
788
- models: getInTreeCapabilitiesMatrix().models.length
789
- });
790
- }
791
- // --------------------------------------------------------------------------
792
- // Public API
793
- // --------------------------------------------------------------------------
794
- /** Logger used by this registry. Exposed so singleton helpers can warn. */
795
- getLogger() {
796
- return this.logger;
797
- }
798
- /**
799
- * Get adapter for a task category. Routing is re-resolved on every read
800
- * (#3185) so a post-startup overlay/registry update propagates without a
801
- * restart. Falls back to default adapter if category unknown.
802
- */
803
- getAdapter(category) {
804
- const routing = this.getRouting(category);
805
- if (routing === void 0) {
806
- this.logger.warn("Unknown task category, using default", { category });
807
- return this.getDefault();
808
- }
809
- return this.getAdapterForCli(routing.primaryCli);
810
- }
811
- /**
812
- * Get adapter for a free-text task description.
813
- * Detects category from keywords, falls back to default.
814
- */
815
- getAdapterForTask(taskDescription) {
816
- const match = detectTaskCategory(taskDescription);
817
- if (match !== null) {
818
- this.logger.debug("Task category detected", {
819
- category: match.category,
820
- cli: match.primaryCli
821
- });
822
- return this.getAdapterForCli(match.primaryCli);
823
- }
824
- return this.getDefault();
825
- }
826
- /**
827
- * Get adapter pinned to a specific CLI.
828
- * Creates and caches one IResilientAdapter per CLI.
829
- */
830
- getAdapterForCli(cli) {
831
- const cached = this.cliAdapters.get(cli);
832
- if (cached !== void 0) return cached;
833
- const raw = createResilientAdapter({
834
- logger: this.logger,
835
- preferredCli: cli,
836
- // #4659: the SHARED registry — the same one that already gates
837
- // voter-panel availability (#4330). Per-adapter registries would let one
838
- // adapter keep routing to a CLI another has already seen fail.
839
- circuitBreakerRegistry: getDefaultCliCircuitBreakerRegistry(),
840
- ...this.defaultCliTimeoutMs !== void 0 && {
841
- defaultCliTimeoutMs: this.defaultCliTimeoutMs
842
- }
843
- });
844
- const adapter = raw;
845
- this.cliAdapters.set(cli, adapter);
846
- this.logger.info("Created CLI-specific adapter", {
847
- cli
848
- });
849
- return adapter;
850
- }
851
- /**
852
- * Get the adapter for a routing arm (#4392). A CLI slot resolves exactly as
853
- * {@link getAdapterForCli} (created lazily, cached, armed with the shared
854
- * breaker registry). An `api:*` arm resolves to what {@link registerApiArm}
855
- * supplied, or `undefined` — never to a CLI slot, and never by creating one.
856
- */
857
- getAdapterForArm(arm) {
858
- if (isCliName(arm)) {
859
- return this.getAdapterForCli(arm);
860
- }
861
- return this.cliAdapters.get(arm);
862
- }
863
- /**
864
- * Register an `api:*` arm's adapter under its endpoint identity (#4392).
865
- * Refuses built-in vendor arms (`api:anthropic`, `api:openai`, `api:google`, `api:gemini`)
866
- * to prevent hijacking or redirecting vendor traffic without authorization.
867
- * Accepts a dynamic `EndpointArmId`; the id is re-validated at runtime
868
- * because the `EndpointArmId` type admits any `api:` string, so a cast from an
869
- * unvalidated name is exactly what this refuses.
870
- * Registering an id twice replaces (and disposes) the earlier adapter.
871
- * CLI-slot behaviour is untouched. A registered endpoint arm is observable
872
- * here and in the breaker registry but is NOT a `RoutingArmId`: it cannot
873
- * enter outcome records until #6291. A gateway arm registered without a
874
- * `NEXUS_GATEWAY_COST` declaration is warned about here, once, at the
875
- * moment it becomes routable (#4392 increment 2).
876
- */
877
- registerApiArm(arm, adapter) {
878
- if (!isEndpointArmId(arm)) {
879
- throw new Error(`Invalid api arm id: ${JSON.stringify(arm)} (expected api:<endpoint>)`);
880
- }
881
- if (!isGatewayArmId(arm) || arm === "api:gemini") {
882
- throw new Error(`Cannot overwrite built-in vendor arm: ${arm}`);
883
- }
884
- this.cliAdapters.get(arm)?.dispose();
885
- this.cliAdapters.set(arm, adapter);
886
- this.logger.info("Registered api arm adapter", { arm });
887
- warnIfGatewayCostUndeclared(arm, this.logger);
888
- }
889
- /**
890
- * Get adapter for a model preference string (e.g., "claude-opus-4-6").
891
- * Resolves the model to its CLI via the canonical registry.
892
- * Falls back to default adapter if model not recognized.
893
- *
894
- * An exact match returns the CLI slot adapter bound to the matched registry
895
- * id, so the model reaches the CLI instead of its default (#6599). A prefix
896
- * match routes to the CLI only, as before: the name was not an entry.
897
- */
898
- getAdapterForModel(modelPreference) {
899
- const allModels = getInTreeCapabilitiesMatrix().models;
900
- const exact = allModels.find(
901
- (m) => m.id === modelPreference || m.cliAlias === modelPreference || m.cliModelName === modelPreference
902
- );
903
- const prefix = exact ?? [...allModels].filter((m) => modelPreference.startsWith(m.id)).sort((a, b) => b.id.length - a.id.length)[0];
904
- const model = prefix;
905
- if (model !== void 0) {
906
- this.logger.debug("Model resolved to CLI", {
907
- model: modelPreference,
908
- cli: model.cliName
909
- });
910
- const slot = this.getAdapterForCli(model.cliName);
911
- return exact !== void 0 ? new ModelBoundAdapter(slot, exact.id) : slot;
912
- }
913
- this.logger.debug("Model not in registry, using default", {
914
- model: modelPreference
915
- });
916
- return this.getDefault();
917
- }
918
- /**
919
- * Get adapter for an expert role (e.g., "code_expert").
920
- * Uses ROLE_TO_TASK_CATEGORY mapping → task specialization → CLI.
921
- */
922
- getAdapterForRole(role) {
923
- const category = ROLE_TO_CATEGORY[role];
924
- if (category !== void 0) return this.getAdapter(category);
925
- return this.getDefault();
926
- }
927
- /**
928
- * Get the default adapter (no CLI preference — auto-detection priority).
929
- */
930
- getDefault() {
931
- if (this.defaultAdapter !== void 0) return this.defaultAdapter;
932
- const raw = createResilientAdapter({
933
- logger: this.logger,
934
- circuitBreakerRegistry: getDefaultCliCircuitBreakerRegistry(),
935
- ...this.defaultCliTimeoutMs !== void 0 && {
936
- defaultCliTimeoutMs: this.defaultCliTimeoutMs
937
- }
938
- });
939
- this.defaultAdapter = raw;
940
- return this.defaultAdapter;
941
- }
942
- /**
943
- * Get snapshot of registry state for observability/debugging. Routing is
944
- * re-resolved on read (#3185) so the snapshot reflects the live registry.
945
- */
946
- getSnapshot() {
947
- return {
948
- taskRouting: TASK_SPECIALIZATION_MATRIX.map((spec) => this.resolveRouting(spec)),
949
- cachedArms: [...this.cliAdapters.keys()],
950
- availableModels: getInTreeCapabilitiesMatrix().models.length
951
- };
952
- }
953
- /**
954
- * Resolve the routing for a specific category.
955
- *
956
- * Computed on every read (#3185) rather than cached at construction, so a
957
- * post-startup model-registry / overlay update (e.g. a default-model change
958
- * surfaced via `getDefaultModelForCli`) propagates to routing decisions
959
- * without a process restart. The matrix is ~10 categories — the per-read
960
- * resolution cost is negligible.
961
- */
962
- getRouting(category) {
963
- const spec = TASK_SPECIALIZATION_MATRIX.find((s) => s.category === category);
964
- if (spec === void 0) return void 0;
965
- return this.resolveRouting(spec);
966
- }
967
- /**
968
- * Dispose all cached adapters.
969
- */
970
- dispose() {
971
- for (const adapter of this.cliAdapters.values()) {
972
- adapter.dispose();
973
- }
974
- this.cliAdapters.clear();
975
- this.defaultAdapter?.dispose();
976
- this.defaultAdapter = void 0;
977
- this.logger.info("UnifiedAdapterRegistry disposed");
978
- }
979
- // --------------------------------------------------------------------------
980
- // Private
981
- // --------------------------------------------------------------------------
982
- /**
983
- * Resolve one specialization-matrix row into a routing entry, re-reading the
984
- * primary model from the (overlay-aware) model registry each call (#3185).
985
- */
986
- resolveRouting(spec) {
987
- return {
988
- category: spec.category,
989
- primaryCli: spec.primaryCli,
990
- secondaryCli: spec.secondaryCli,
991
- primaryModel: resolveDefaultModel(spec.primaryCli)
992
- };
993
- }
994
- };
995
- var ROLE_TO_CATEGORY = {
996
- code_expert: "code_generation",
997
- architecture_expert: "architecture",
998
- security_expert: "security_review",
999
- documentation_expert: "documentation",
1000
- testing_expert: "testing",
1001
- devops_expert: "devops",
1002
- research_expert: "research",
1003
- pm_expert: "planning",
1004
- ux_expert: "planning",
1005
- infrastructure_expert: "devops"
1006
- };
1007
- function resolveDefaultModel(cli) {
1008
- if (isCliName(cli)) {
1009
- return getDefaultModelForCli(cli);
1010
- }
1011
- return cli;
1012
- }
1013
- var globalRegistry;
1014
- function createUnifiedRegistry(config) {
1015
- return new UnifiedAdapterRegistry(config);
1016
- }
1017
- function claimGlobalRegistry(logger) {
1018
- globalRegistry ??= new UnifiedAdapterRegistry({ logger });
1019
- return globalRegistry;
1020
- }
1021
- var RegistryAlreadyInitializedError = class extends ConfigError {
1022
- constructor(providedKeys) {
1023
- super(
1024
- `UnifiedAdapterRegistry singleton is already initialized, so getGlobalRegistry() cannot apply the supplied config (keys: ${providedKeys.join(", ")}). Call resetGlobalRegistry() first if reconfiguration is intentional, or getGlobalRegistry() with no config to use the existing instance; claimGlobalRegistry(logger) is the idempotent way to name the logger.`,
1025
- { context: { providedKeys: [...providedKeys] } }
1026
- );
1027
- this.name = "RegistryAlreadyInitializedError";
1028
- }
1029
- };
1030
- function getGlobalRegistry(config) {
1031
- if (globalRegistry === void 0) {
1032
- globalRegistry = new UnifiedAdapterRegistry(config);
1033
- return globalRegistry;
1034
- }
1035
- if (config !== void 0 && Object.keys(config).length > 0) {
1036
- throw new RegistryAlreadyInitializedError(Object.keys(config));
1037
- }
1038
- return globalRegistry;
1039
- }
1040
- function resetGlobalRegistry() {
1041
- globalRegistry?.dispose();
1042
- globalRegistry = void 0;
1043
- }
1044
-
1045
- export {
1046
- getGlobalEventBus,
1047
- createEvent,
1048
- EventTopics,
1049
- ModelBoundAdapter,
1050
- UnifiedAdapterRegistry,
1051
- createUnifiedRegistry,
1052
- claimGlobalRegistry,
1053
- RegistryAlreadyInitializedError,
1054
- getGlobalRegistry,
1055
- resetGlobalRegistry
1056
- };
1057
- //# sourceMappingURL=chunk-UZJ6IPJR.js.map