nexus-agents 2.131.1 → 2.132.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +1 -1
  2. package/dist/{child-mcp-config-WREL6AH5.js → child-mcp-config-R67B2KM6.js} +4 -4
  3. package/dist/{chunk-K3CES6CG.js → chunk-27DRQWON.js} +7 -123
  4. package/dist/chunk-27DRQWON.js.map +1 -0
  5. package/dist/{chunk-HM77WNJG.js → chunk-2CLFX73Y.js} +2 -2
  6. package/dist/{chunk-4SNH5EMQ.js → chunk-4CLBJ2NA.js} +3 -3
  7. package/dist/{chunk-DRYU676G.js → chunk-4DLHM6LI.js} +6 -2
  8. package/dist/{chunk-DRYU676G.js.map → chunk-4DLHM6LI.js.map} +1 -1
  9. package/dist/{chunk-JPBPQN4U.js → chunk-7BONPRR2.js} +2 -2
  10. package/dist/{chunk-O563YLBH.js → chunk-CI5KGCX5.js} +3 -3
  11. package/dist/{chunk-PARI43LB.js → chunk-E4IYX7LD.js} +2 -2
  12. package/dist/{chunk-JSYFPXID.js → chunk-ECDJM6UB.js} +2 -2
  13. package/dist/{chunk-ZLQCJRYK.js → chunk-EEQVXGZ2.js} +607 -332
  14. package/dist/chunk-EEQVXGZ2.js.map +1 -0
  15. package/dist/{chunk-WUJCVZIQ.js → chunk-EFDXX5MC.js} +3 -3
  16. package/dist/{chunk-3P252QNW.js → chunk-FKTLAPXJ.js} +3 -3
  17. package/dist/{chunk-TRTCKEHS.js → chunk-GET2MTLW.js} +2 -2
  18. package/dist/{chunk-NINNWQ42.js → chunk-GYDO2WOR.js} +13 -5
  19. package/dist/{chunk-NINNWQ42.js.map → chunk-GYDO2WOR.js.map} +1 -1
  20. package/dist/{chunk-DEL26PXS.js → chunk-I2SW34K6.js} +403 -84
  21. package/dist/chunk-I2SW34K6.js.map +1 -0
  22. package/dist/{chunk-3TTMRJUQ.js → chunk-J435FU54.js} +6 -4
  23. package/dist/{chunk-3TTMRJUQ.js.map → chunk-J435FU54.js.map} +1 -1
  24. package/dist/{chunk-IPPWZNTH.js → chunk-K5WBFAR4.js} +4 -4
  25. package/dist/{chunk-KMNAZ5RC.js → chunk-MHIJSCPR.js} +446 -162
  26. package/dist/chunk-MHIJSCPR.js.map +1 -0
  27. package/dist/{chunk-STIX3VTX.js → chunk-NXTFB3HA.js} +6 -6
  28. package/dist/{chunk-PGGSGUO4.js → chunk-NZGNOXXW.js} +3 -2
  29. package/dist/{chunk-PGGSGUO4.js.map → chunk-NZGNOXXW.js.map} +1 -1
  30. package/dist/{chunk-PID22PMZ.js → chunk-P6FBH2UG.js} +8 -8
  31. package/dist/{chunk-UZWZ72X7.js → chunk-PM63X3QG.js} +3 -3
  32. package/dist/{chunk-4KBG4ICZ.js → chunk-QJIHSQZX.js} +790 -27
  33. package/dist/chunk-QJIHSQZX.js.map +1 -0
  34. package/dist/{chunk-HS3K2B7Q.js → chunk-QLRH6KOT.js} +2 -2
  35. package/dist/{chunk-DI6SRBCT.js → chunk-QMCHVYZB.js} +2 -2
  36. package/dist/{chunk-GVBEF6LQ.js → chunk-RAWF4JOT.js} +8 -8
  37. package/dist/{chunk-RF7QCPNA.js → chunk-RLYXX3FQ.js} +2 -2
  38. package/dist/{chunk-ZGLIHPGJ.js → chunk-RUSP5PJR.js} +9 -1
  39. package/dist/chunk-RUSP5PJR.js.map +1 -0
  40. package/dist/{chunk-FCPANF6R.js → chunk-S4U3Z6ZS.js} +9 -6
  41. package/dist/{chunk-FCPANF6R.js.map → chunk-S4U3Z6ZS.js.map} +1 -1
  42. package/dist/chunk-TGGR6ST5.js +126 -0
  43. package/dist/chunk-TGGR6ST5.js.map +1 -0
  44. package/dist/{chunk-2QFUW2SP.js → chunk-VZCQRLXT.js} +7 -7
  45. package/dist/{chunk-JTECUWWI.js → chunk-WHZD2W6Q.js} +2 -2
  46. package/dist/{chunk-BDERGLIX.js → chunk-XIFOAZ5B.js} +4 -4
  47. package/dist/{chunk-PR4QN5HX.js → chunk-ZPWHCABL.js} +6 -2
  48. package/dist/{chunk-4DJNJGIB.js → chunk-ZQEQVCIU.js} +2 -2
  49. package/dist/{chunk-VZ3TTUWP.js → chunk-ZTRBMKXK.js} +5 -2
  50. package/dist/{chunk-VZ3TTUWP.js.map → chunk-ZTRBMKXK.js.map} +1 -1
  51. package/dist/cli-circuit-breaker-J6UFONNE.js +13 -0
  52. package/dist/cli.d.ts +1 -1
  53. package/dist/cli.js +258 -150
  54. package/dist/cli.js.map +1 -1
  55. package/dist/{codebase-search-ZFJUVMVR.js → codebase-search-PWMWLU7O.js} +2 -2
  56. package/dist/{composite-router-Y6UCDBDE.js → composite-router-HQUSCJO4.js} +4 -4
  57. package/dist/consensus-vote-E2WJFEIJ.js +37 -0
  58. package/dist/{consensus-vote-types-DdruG9-X.d.ts → consensus-vote-types-CYBHW8vY.d.ts} +99 -2
  59. package/dist/{context-retriever-GRMH7WSS.js → context-retriever-5OVRAWNZ.js} +10 -10
  60. package/dist/{dist-QZLOLGIK.js → dist-A5STUO26.js} +792 -622
  61. package/dist/dist-A5STUO26.js.map +1 -0
  62. package/dist/{dist-OFXI4GYH.js → dist-C4457XNQ.js} +2 -2
  63. package/dist/doctor-deep-HJ3NQ365.js +12 -0
  64. package/dist/expert-bridge-MINXBC73.js +17 -0
  65. package/dist/{expert-config-OIIUMJFB.js → expert-config-53EBA7DT.js} +2 -2
  66. package/dist/factory-KC7BYHAP.js +21 -0
  67. package/dist/factory-ZG2FZYVA.js +14 -0
  68. package/dist/{improvement-review-N2KSQSNW.js → improvement-review-TDC4QH5P.js} +6 -6
  69. package/dist/index.d.ts +731 -339
  70. package/dist/index.js +31 -30
  71. package/dist/index.js.map +1 -1
  72. package/dist/{init-opencode-6K6QNJWF.js → init-opencode-L3NT524E.js} +8 -7
  73. package/dist/{init-opencode-6K6QNJWF.js.map → init-opencode-L3NT524E.js.map} +1 -1
  74. package/dist/issue-triage-CZPFNGB7.js +16 -0
  75. package/dist/{learning-persistence-C737Q6EP.js → learning-persistence-QFCQ5POD.js} +7 -3
  76. package/dist/{outcome-store-adapter-D3UDQFNQ.js → outcome-store-adapter-K4L3RUJE.js} +2 -2
  77. package/dist/{pr-reviewer-helpers-GC6DEIBT.js → pr-reviewer-helpers-PLKDVWYF.js} +6 -6
  78. package/dist/{registry-command-7RRDCDAU.js → registry-command-HPKNLYUL.js} +4 -4
  79. package/dist/{repo-analyze-JZEMBE6R.js → repo-analyze-UJOWFGNM.js} +2 -2
  80. package/dist/{repo-security-plan-5V43DEGA.js → repo-security-plan-HSYGGVEJ.js} +5 -5
  81. package/dist/research-helpers-synthesize-IQHDU27S.js +14 -0
  82. package/dist/{routing-memory-BQ6MUC5Y.js → routing-memory-OGUOL7UF.js} +4 -4
  83. package/dist/{session-memory-MJHN5FXI.js → session-memory-HQWAXH3C.js} +5 -5
  84. package/dist/{setup-command-YAQE2KRH.js → setup-command-MFKD5IJT.js} +13 -13
  85. package/dist/setup-config-HYX3BRSE.js +10 -0
  86. package/dist/{setup-custom-api-3JEENEKA.js → setup-custom-api-W7EDVKWR.js} +5 -5
  87. package/dist/{tool-memory-NKBBVA7O.js → tool-memory-Z2B7CAH7.js} +7 -7
  88. package/dist/unified-registry-UFNSWKZA.js +24 -0
  89. package/dist/{weather-report-7WC3RKE7.js → weather-report-GX7OIUL2.js} +4 -4
  90. package/package.json +12 -12
  91. package/dist/chunk-4KBG4ICZ.js.map +0 -1
  92. package/dist/chunk-DEL26PXS.js.map +0 -1
  93. package/dist/chunk-K3CES6CG.js.map +0 -1
  94. package/dist/chunk-KMNAZ5RC.js.map +0 -1
  95. package/dist/chunk-ZGLIHPGJ.js.map +0 -1
  96. package/dist/chunk-ZLQCJRYK.js.map +0 -1
  97. package/dist/cli-circuit-breaker-WLYICYAZ.js +0 -13
  98. package/dist/consensus-vote-PEWNN4WK.js +0 -36
  99. package/dist/dist-QZLOLGIK.js.map +0 -1
  100. package/dist/doctor-deep-HXKGXUI4.js +0 -12
  101. package/dist/expert-bridge-BW55FRAH.js +0 -17
  102. package/dist/factory-4EV6EDZF.js +0 -21
  103. package/dist/factory-YKXAMYW6.js +0 -14
  104. package/dist/issue-triage-PYHGDQHK.js +0 -16
  105. package/dist/research-helpers-synthesize-EDT6REQJ.js +0 -14
  106. package/dist/setup-config-OFC6SD7H.js +0 -10
  107. package/dist/unified-registry-XXAVF4AQ.js +0 -24
  108. /package/dist/{child-mcp-config-WREL6AH5.js.map → child-mcp-config-R67B2KM6.js.map} +0 -0
  109. /package/dist/{chunk-HM77WNJG.js.map → chunk-2CLFX73Y.js.map} +0 -0
  110. /package/dist/{chunk-4SNH5EMQ.js.map → chunk-4CLBJ2NA.js.map} +0 -0
  111. /package/dist/{chunk-JPBPQN4U.js.map → chunk-7BONPRR2.js.map} +0 -0
  112. /package/dist/{chunk-O563YLBH.js.map → chunk-CI5KGCX5.js.map} +0 -0
  113. /package/dist/{chunk-PARI43LB.js.map → chunk-E4IYX7LD.js.map} +0 -0
  114. /package/dist/{chunk-JSYFPXID.js.map → chunk-ECDJM6UB.js.map} +0 -0
  115. /package/dist/{chunk-WUJCVZIQ.js.map → chunk-EFDXX5MC.js.map} +0 -0
  116. /package/dist/{chunk-3P252QNW.js.map → chunk-FKTLAPXJ.js.map} +0 -0
  117. /package/dist/{chunk-TRTCKEHS.js.map → chunk-GET2MTLW.js.map} +0 -0
  118. /package/dist/{chunk-IPPWZNTH.js.map → chunk-K5WBFAR4.js.map} +0 -0
  119. /package/dist/{chunk-STIX3VTX.js.map → chunk-NXTFB3HA.js.map} +0 -0
  120. /package/dist/{chunk-PID22PMZ.js.map → chunk-P6FBH2UG.js.map} +0 -0
  121. /package/dist/{chunk-UZWZ72X7.js.map → chunk-PM63X3QG.js.map} +0 -0
  122. /package/dist/{chunk-HS3K2B7Q.js.map → chunk-QLRH6KOT.js.map} +0 -0
  123. /package/dist/{chunk-DI6SRBCT.js.map → chunk-QMCHVYZB.js.map} +0 -0
  124. /package/dist/{chunk-GVBEF6LQ.js.map → chunk-RAWF4JOT.js.map} +0 -0
  125. /package/dist/{chunk-RF7QCPNA.js.map → chunk-RLYXX3FQ.js.map} +0 -0
  126. /package/dist/{chunk-2QFUW2SP.js.map → chunk-VZCQRLXT.js.map} +0 -0
  127. /package/dist/{chunk-JTECUWWI.js.map → chunk-WHZD2W6Q.js.map} +0 -0
  128. /package/dist/{chunk-BDERGLIX.js.map → chunk-XIFOAZ5B.js.map} +0 -0
  129. /package/dist/{chunk-PR4QN5HX.js.map → chunk-ZPWHCABL.js.map} +0 -0
  130. /package/dist/{chunk-4DJNJGIB.js.map → chunk-ZQEQVCIU.js.map} +0 -0
  131. /package/dist/{cli-circuit-breaker-WLYICYAZ.js.map → cli-circuit-breaker-J6UFONNE.js.map} +0 -0
  132. /package/dist/{codebase-search-ZFJUVMVR.js.map → codebase-search-PWMWLU7O.js.map} +0 -0
  133. /package/dist/{composite-router-Y6UCDBDE.js.map → composite-router-HQUSCJO4.js.map} +0 -0
  134. /package/dist/{consensus-vote-PEWNN4WK.js.map → consensus-vote-E2WJFEIJ.js.map} +0 -0
  135. /package/dist/{context-retriever-GRMH7WSS.js.map → context-retriever-5OVRAWNZ.js.map} +0 -0
  136. /package/dist/{dist-OFXI4GYH.js.map → dist-C4457XNQ.js.map} +0 -0
  137. /package/dist/{doctor-deep-HXKGXUI4.js.map → doctor-deep-HJ3NQ365.js.map} +0 -0
  138. /package/dist/{expert-bridge-BW55FRAH.js.map → expert-bridge-MINXBC73.js.map} +0 -0
  139. /package/dist/{expert-config-OIIUMJFB.js.map → expert-config-53EBA7DT.js.map} +0 -0
  140. /package/dist/{factory-4EV6EDZF.js.map → factory-KC7BYHAP.js.map} +0 -0
  141. /package/dist/{factory-YKXAMYW6.js.map → factory-ZG2FZYVA.js.map} +0 -0
  142. /package/dist/{improvement-review-N2KSQSNW.js.map → improvement-review-TDC4QH5P.js.map} +0 -0
  143. /package/dist/{issue-triage-PYHGDQHK.js.map → issue-triage-CZPFNGB7.js.map} +0 -0
  144. /package/dist/{learning-persistence-C737Q6EP.js.map → learning-persistence-QFCQ5POD.js.map} +0 -0
  145. /package/dist/{outcome-store-adapter-D3UDQFNQ.js.map → outcome-store-adapter-K4L3RUJE.js.map} +0 -0
  146. /package/dist/{pr-reviewer-helpers-GC6DEIBT.js.map → pr-reviewer-helpers-PLKDVWYF.js.map} +0 -0
  147. /package/dist/{registry-command-7RRDCDAU.js.map → registry-command-HPKNLYUL.js.map} +0 -0
  148. /package/dist/{repo-analyze-JZEMBE6R.js.map → repo-analyze-UJOWFGNM.js.map} +0 -0
  149. /package/dist/{repo-security-plan-5V43DEGA.js.map → repo-security-plan-HSYGGVEJ.js.map} +0 -0
  150. /package/dist/{research-helpers-synthesize-EDT6REQJ.js.map → research-helpers-synthesize-IQHDU27S.js.map} +0 -0
  151. /package/dist/{routing-memory-BQ6MUC5Y.js.map → routing-memory-OGUOL7UF.js.map} +0 -0
  152. /package/dist/{session-memory-MJHN5FXI.js.map → session-memory-HQWAXH3C.js.map} +0 -0
  153. /package/dist/{setup-command-YAQE2KRH.js.map → setup-command-MFKD5IJT.js.map} +0 -0
  154. /package/dist/{setup-config-OFC6SD7H.js.map → setup-config-HYX3BRSE.js.map} +0 -0
  155. /package/dist/{setup-custom-api-3JEENEKA.js.map → setup-custom-api-W7EDVKWR.js.map} +0 -0
  156. /package/dist/{tool-memory-NKBBVA7O.js.map → tool-memory-Z2B7CAH7.js.map} +0 -0
  157. /package/dist/{unified-registry-XXAVF4AQ.js.map → unified-registry-UFNSWKZA.js.map} +0 -0
  158. /package/dist/{weather-report-7WC3RKE7.js.map → weather-report-GX7OIUL2.js.map} +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Nexus Agents
2
2
 
3
- > Governance substrate for your AI coding agents — adversarial review, drift-detected rules, immutable audit, closed-loop telemetry
3
+ > Governance substrate for your AI coding agents — adversarial review, drift-detected rules, tamper-evident append-only audit chain, human-gated closed-loop tuning (autonomous demotion, earned promotion)
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D22.0.0-brightgreen)](https://nodejs.org)
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createLogger
3
- } from "./chunk-4KBG4ICZ.js";
4
- import "./chunk-ZGLIHPGJ.js";
5
- import "./chunk-PR4QN5HX.js";
3
+ } from "./chunk-QJIHSQZX.js";
4
+ import "./chunk-RUSP5PJR.js";
5
+ import "./chunk-ZPWHCABL.js";
6
6
 
7
7
  // src/cli-adapters/child-mcp-config.ts
8
8
  import { writeFile, mkdtemp, rm } from "fs/promises";
@@ -59,4 +59,4 @@ export {
59
59
  generateMcpConfig,
60
60
  getDefaultAllowedTools
61
61
  };
62
- //# sourceMappingURL=child-mcp-config-WREL6AH5.js.map
62
+ //# sourceMappingURL=child-mcp-config-R67B2KM6.js.map
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  OpenAIAdapter
3
- } from "./chunk-UZWZ72X7.js";
3
+ } from "./chunk-PM63X3QG.js";
4
+ import {
5
+ computeCostUSD,
6
+ recordUsageEvent
7
+ } from "./chunk-TGGR6ST5.js";
4
8
  import {
5
9
  ConfigError,
6
10
  createLogger,
7
11
  err,
8
12
  getErrorMessage,
9
13
  getTimeProvider,
10
- lookupInTreeCapability,
11
14
  ok
12
- } from "./chunk-4KBG4ICZ.js";
13
- import {
14
- getNexusDataDir
15
- } from "./chunk-ZGLIHPGJ.js";
15
+ } from "./chunk-QJIHSQZX.js";
16
16
 
17
17
  // src/config/opencode-bridge.ts
18
18
  import { readFileSync } from "fs";
@@ -85,120 +85,6 @@ function resolveEnvInterpolation(value) {
85
85
 
86
86
  // src/adapters/openai-compat-adapter.ts
87
87
  import OpenAI from "openai";
88
-
89
- // src/learning/usage-log.ts
90
- import { appendFileSync, existsSync, mkdirSync, readFileSync as readFileSync2, readdirSync } from "fs";
91
- import { dirname, join } from "path";
92
- function computeCostUSD(modelId, inputTokens, outputTokens) {
93
- const cap = lookupInTreeCapability(modelId);
94
- if (cap === void 0) return 0;
95
- const inputPer1M = cap.pricing?.inputPer1M ?? 0;
96
- const outputPer1M = cap.pricing?.outputPer1M ?? 0;
97
- const microUsd = Math.round(
98
- inputTokens * inputPer1M + outputTokens * outputPer1M
99
- // micro-USD per million scaled
100
- );
101
- return microUsd / 1e6;
102
- }
103
- function getUsageLogPath(date = /* @__PURE__ */ new Date()) {
104
- const year = date.getUTCFullYear();
105
- const month = String(date.getUTCMonth() + 1).padStart(2, "0");
106
- return join(getNexusDataDir(), "usage", `usage-${String(year)}-${month}.jsonl`);
107
- }
108
- function recordUsageEvent(event) {
109
- try {
110
- const path = getUsageLogPath(new Date(event.timestamp));
111
- mkdirSync(dirname(path), { recursive: true });
112
- appendFileSync(path, `${JSON.stringify(event)}
113
- `, "utf-8");
114
- } catch {
115
- }
116
- }
117
- function listUsageFiles(dir) {
118
- if (!existsSync(dir)) return [];
119
- try {
120
- return readdirSync(dir).filter((f) => f.startsWith("usage-") && f.endsWith(".jsonl"));
121
- } catch {
122
- return [];
123
- }
124
- }
125
- function eventMatches(parsed, f) {
126
- const ts = Date.parse(parsed.timestamp);
127
- if (ts < f.sinceMs || ts >= f.untilMs) return false;
128
- if (f.modelId !== void 0 && parsed.modelId !== f.modelId) return false;
129
- if (f.category !== void 0 && parsed.category !== f.category) return false;
130
- return true;
131
- }
132
- function parseFileLines(filePath, filter) {
133
- let content;
134
- try {
135
- content = readFileSync2(filePath, "utf-8");
136
- } catch {
137
- return [];
138
- }
139
- const out = [];
140
- for (const line of content.split("\n")) {
141
- if (line.trim() === "") continue;
142
- try {
143
- const parsed = JSON.parse(line);
144
- if (eventMatches(parsed, filter)) out.push(parsed);
145
- } catch {
146
- continue;
147
- }
148
- }
149
- return out;
150
- }
151
- function loadUsageEvents(opts = {}) {
152
- const dir = join(getNexusDataDir(), "usage");
153
- const files = listUsageFiles(dir);
154
- if (files.length === 0) return [];
155
- const filter = {
156
- sinceMs: opts.sinceIso !== void 0 ? Date.parse(opts.sinceIso) : Number.NEGATIVE_INFINITY,
157
- untilMs: opts.untilIso !== void 0 ? Date.parse(opts.untilIso) : Number.POSITIVE_INFINITY,
158
- modelId: opts.modelId,
159
- category: opts.category
160
- };
161
- const events = [];
162
- for (const f of files) {
163
- events.push(...parseFileLines(join(dir, f), filter));
164
- }
165
- return events;
166
- }
167
- function rollupByModel(events) {
168
- const groups = /* @__PURE__ */ new Map();
169
- for (const e of events) {
170
- const arr = groups.get(e.modelId);
171
- if (arr === void 0) groups.set(e.modelId, [e]);
172
- else arr.push(e);
173
- }
174
- const rollups = [];
175
- for (const [modelId, group] of groups) {
176
- const callCount = group.length;
177
- const successCount = group.filter((e) => e.success).length;
178
- const totalInputTokens = group.reduce((s, e) => s + e.inputTokens, 0);
179
- const totalOutputTokens = group.reduce((s, e) => s + e.outputTokens, 0);
180
- const totalUsdCost = group.reduce((s, e) => s + e.usdCost, 0);
181
- const totalLatency = group.reduce((s, e) => s + e.latencyMs, 0);
182
- const successRate = callCount === 0 ? 0 : successCount / callCount;
183
- const avgLatencyMs = callCount === 0 ? 0 : totalLatency / callCount;
184
- const costPerSuccessUsd = successCount === 0 ? totalUsdCost : totalUsdCost / successCount;
185
- rollups.push({
186
- modelId,
187
- providerId: group[0]?.providerId ?? "unknown",
188
- callCount,
189
- successCount,
190
- successRate,
191
- totalInputTokens,
192
- totalOutputTokens,
193
- totalUsdCost,
194
- avgLatencyMs,
195
- costPerSuccessUsd
196
- });
197
- }
198
- return rollups.sort((a, b) => b.totalUsdCost - a.totalUsdCost);
199
- }
200
-
201
- // src/adapters/openai-compat-adapter.ts
202
88
  function readOpenAICompatEnv() {
203
89
  const fromEnv = readGatewayFromEnv();
204
90
  if (fromEnv !== null) return fromEnv;
@@ -300,11 +186,9 @@ async function buildOpenAICompatAdapters() {
300
186
  }
301
187
 
302
188
  export {
303
- loadUsageEvents,
304
- rollupByModel,
305
189
  readOpencodeGateway,
306
190
  readOpenAICompatEnv,
307
191
  discoverModels,
308
192
  buildOpenAICompatAdapters
309
193
  };
310
- //# sourceMappingURL=chunk-K3CES6CG.js.map
194
+ //# sourceMappingURL=chunk-27DRQWON.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/opencode-bridge.ts","../src/adapters/openai-compat-adapter.ts"],"sourcesContent":["/**\n * opencode.json gateway-config bridge (#2503, child 3 of epic #2500).\n *\n * When nexus-agents runs as an MCP loaded by OpenCode (typically inside a\n * Docker sandbox), OpenCode's `opencode.json` already declares the\n * OpenAI-compatible gateway the harness uses. Reading it from there saves\n * the operator from re-declaring the same `baseURL` + `apiKey` as\n * NEXUS_OPENAI_COMPAT_* env vars.\n *\n * Scope (per direction discussion 2026-05-09):\n * - **Only** reads `providers.openai-compat.options.{baseURL, apiKey}`.\n * Does NOT read OpenCode's MCP config, model list, logging, or other\n * settings. nexus-agents' own behaviour stays driven by\n * `nexus-agents.yaml` + `NEXUS_*` env vars.\n * - Resolves OpenCode's `{env:VAR}` interpolation in the apiKey field.\n * - Returns `null` on any failure path (missing file, parse error,\n * missing fields, unset interpolated env var) — caller falls back to\n * env-var precedence; we don't throw.\n *\n * Precedence (applied in `openai-compat-adapter.ts`):\n * 1. `NEXUS_OPENAI_COMPAT_URL` + `NEXUS_OPENAI_COMPAT_KEY` env vars (if both set)\n * 2. `NEXUS_OPENCODE_CONFIG` → `opencode.json` → `providers.openai-compat`\n * 3. Unconfigured → `null`, no adapter registered\n *\n * Sanitised logging: never log the resolved apiKey. Only the baseURL is\n * logged on success.\n *\n * @module config/opencode-bridge\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { createLogger } from '../core/index.js';\n\nconst logger = createLogger({ component: 'opencode-bridge' });\n\nexport interface OpencodeGatewayConfig {\n readonly baseURL: string;\n readonly apiKey: string;\n}\n\n/**\n * Read `providers.openai-compat.options.{baseURL, apiKey}` from the given\n * opencode.json path. Returns `null` on any failure — the caller falls\n * back to env-var precedence.\n */\nexport function readOpencodeGateway(path: string): OpencodeGatewayConfig | null {\n const raw = readFileOrNull(path);\n if (raw === null) return null;\n\n const parsed = parseJsonOrNull(raw, path);\n if (parsed === null) return null;\n\n const options = extractOpenAICompatOptions(parsed);\n if (options === null) {\n logger.debug('opencode.json has no providers.openai-compat.options block', { path });\n return null;\n }\n\n const baseURL = typeof options.baseURL === 'string' ? options.baseURL.trim() : '';\n const apiKeyRaw = typeof options.apiKey === 'string' ? options.apiKey.trim() : '';\n if (baseURL === '' || apiKeyRaw === '') {\n logger.warn('opencode.json providers.openai-compat missing baseURL or apiKey', { path });\n return null;\n }\n\n const apiKey = resolveEnvInterpolation(apiKeyRaw);\n if (apiKey === null) {\n logger.warn(\n 'opencode.json apiKey references an env var that is not set; gateway not configured',\n { path }\n );\n return null;\n }\n\n logger.info('Gateway config sourced from opencode.json', { baseURL, path });\n return { baseURL, apiKey };\n}\n\nfunction readFileOrNull(path: string): string | null {\n try {\n return readFileSync(path, 'utf8');\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.debug('Could not read opencode.json', { path, error: msg });\n return null;\n }\n}\n\nfunction parseJsonOrNull(raw: string, path: string): unknown {\n try {\n return JSON.parse(raw);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.warn('opencode.json is not valid JSON; ignoring', { path, error: msg });\n return null;\n }\n}\n\n/**\n * Pull `providers.openai-compat.options` out of the parsed JSON without\n * coupling to a Zod schema. OpenCode's config has many keys we don't\n * care about; we only navigate to the one we need.\n */\nfunction extractOpenAICompatOptions(\n parsed: unknown\n): { baseURL?: unknown; apiKey?: unknown } | null {\n if (typeof parsed !== 'object' || parsed === null) return null;\n const root = parsed as { providers?: unknown };\n const providers = root.providers;\n if (typeof providers !== 'object' || providers === null) return null;\n const provider = (providers as Record<string, unknown>)['openai-compat'];\n if (typeof provider !== 'object' || provider === null) return null;\n const options = (provider as { options?: unknown }).options;\n if (typeof options !== 'object' || options === null) return null;\n return options;\n}\n\n/**\n * Resolve OpenCode's `{env:VAR}` interpolation. Only the literal pattern\n * is supported (no shell expansion, no defaults). Returns the string as-is\n * when no interpolation is present; returns `null` when the referenced\n * env var is not set.\n */\nfunction resolveEnvInterpolation(value: string): string | null {\n const match = /^\\{env:([A-Z0-9_]+)\\}$/.exec(value);\n if (match === null) return value;\n const envName = match[1];\n if (envName === undefined) return null;\n const resolved = process.env[envName];\n if (resolved === undefined || resolved === '') return null;\n return resolved;\n}\n","/**\n * OpenAI-compatible gateway adapter — talk to any HTTP gateway that exposes\n * the OpenAI Chat Completions API. The gateway may itself be a multi-model\n * router (Bedrock/Vertex/Azure proxy, OpenRouter, vLLM, etc.). nexus-agents\n * sees one adapter, the gateway exposes N models, and the existing routing\n * pipeline picks among them.\n *\n * Source: Issue #2468 (epic #2467 child).\n *\n * Configuration precedence (#2503, child 3 of epic #2500):\n * 1. NEXUS_OPENAI_COMPAT_URL + NEXUS_OPENAI_COMPAT_KEY env vars (both required)\n * 2. NEXUS_OPENCODE_CONFIG path → opencode.json → providers.openai-compat\n * 3. Unconfigured → adapter not built\n *\n * Models are discovered via GET {base}/v1/models at first use. Each model\n * the gateway exposes can be selected by ID; the adapter wraps the existing\n * `OpenAIAdapter` for the actual chat-completions request, so streaming +\n * tool use + the full IModelAdapter contract come for free.\n */\n\nimport OpenAI from 'openai';\n\nimport type {\n Result,\n CompletionRequest,\n CompletionResponse,\n ModelError,\n ModelMetadata,\n IModelAdapter,\n} from '../core/index.js';\nimport { ok, err, ConfigError, getErrorMessage, getTimeProvider } from '../core/index.js';\nimport { OpenAIAdapter } from './openai-adapter.js';\nimport { recordUsageEvent, computeCostUSD } from '../learning/usage-log.js';\nimport { readOpencodeGateway } from '../config/opencode-bridge.js';\n\nexport interface OpenAICompatConfig {\n /** Gateway base URL — must reach `/v1/models` and `/v1/chat/completions`. */\n readonly baseUrl: string;\n /** API key the gateway expects. */\n readonly apiKey: string;\n}\n\nexport interface DiscoveredModel {\n readonly id: string;\n /** Unix epoch seconds when the model was created (per OpenAI API). */\n readonly created?: number;\n /** Owning organization or upstream provider name. */\n readonly ownedBy?: string;\n}\n\n/**\n * Read the gateway config with the precedence chain documented in the\n * module docstring: env vars > opencode.json > unconfigured.\n *\n * The env-var path (#2468) wins when both `NEXUS_OPENAI_COMPAT_URL` and\n * `NEXUS_OPENAI_COMPAT_KEY` are set. Otherwise, when `NEXUS_OPENCODE_CONFIG`\n * names a path, the opencode.json bridge tries to source the gateway from\n * `providers.openai-compat.options.{baseURL, apiKey}` (#2503). Returns\n * `null` when neither path yields a config — caller treats unset gateway\n * as \"no adapter from this source.\"\n */\nexport function readOpenAICompatEnv(): OpenAICompatConfig | null {\n const fromEnv = readGatewayFromEnv();\n if (fromEnv !== null) return fromEnv;\n return readGatewayFromOpencode();\n}\n\nfunction readGatewayFromEnv(): OpenAICompatConfig | null {\n const envUrl = process.env['NEXUS_OPENAI_COMPAT_URL']?.trim();\n const envKey = process.env['NEXUS_OPENAI_COMPAT_KEY']?.trim();\n if (envUrl === undefined || envUrl === '') return null;\n if (envKey === undefined || envKey === '') return null;\n return { baseUrl: envUrl, apiKey: envKey };\n}\n\nfunction readGatewayFromOpencode(): OpenAICompatConfig | null {\n const opencodePath = process.env['NEXUS_OPENCODE_CONFIG']?.trim();\n if (opencodePath === undefined || opencodePath === '') return null;\n const fromFile = readOpencodeGateway(opencodePath);\n if (fromFile === null) return null;\n return { baseUrl: fromFile.baseURL, apiKey: fromFile.apiKey };\n}\n\n/**\n * Discover available models by calling `GET {baseUrl}/v1/models`. Uses the\n * official `openai` SDK's `client.models.list()` so we benefit from its\n * pagination + retry handling. The list is the strongly authoritative\n * source: nexus-agents won't try to dispatch to a model the gateway doesn't\n * expose.\n */\nexport async function discoverModels(\n config: OpenAICompatConfig\n): Promise<Result<readonly DiscoveredModel[], ConfigError>> {\n try {\n const client = new OpenAI({ baseURL: config.baseUrl, apiKey: config.apiKey });\n const list = await client.models.list();\n const models: readonly DiscoveredModel[] = list.data.map((m) => ({\n id: m.id,\n created: m.created,\n ownedBy: m.owned_by,\n }));\n return ok(models);\n } catch (e: unknown) {\n return err(\n new ConfigError(\n `Failed to discover models from ${config.baseUrl}: ${getErrorMessage(e)}. ` +\n `Verify NEXUS_OPENAI_COMPAT_URL and NEXUS_OPENAI_COMPAT_KEY, then retry.`\n )\n );\n }\n}\n\n/**\n * Create an OpenAIAdapter pointed at the gateway for a specific model ID,\n * wrapped with usage recording so every completion appends a UsageEvent\n * to the JSONL log consumed by `nexus-agents usage`.\n *\n * The wrapper is transparent — same IModelAdapter contract, same fields,\n * same error handling. Recording is best-effort (telemetry never fails\n * the user's call).\n *\n * When invoked via MCP, the host harness's model identifier is passed\n * through verbatim — nexus-agents doesn't second-guess what the host is\n * already routing.\n */\nexport function createOpenAICompatAdapter(\n modelId: string,\n config: OpenAICompatConfig\n): IModelAdapter {\n const inner = new OpenAIAdapter({ modelId, apiKey: config.apiKey, baseUrl: config.baseUrl });\n return withUsageRecording(inner);\n}\n\n/**\n * Wrap any IModelAdapter so that successful + failed `complete()` calls\n * append a UsageEvent to the on-disk usage log. Stream calls aren't yet\n * instrumented (a future PR can add streaming-aware recording).\n *\n * The returned object preserves the IModelAdapter contract identically;\n * downstream code can't tell the difference except that one extra JSONL\n * line gets written per call.\n */\nfunction withUsageRecording(inner: IModelAdapter): IModelAdapter {\n const wrapped: IModelAdapter = {\n providerId: inner.providerId,\n modelId: inner.modelId,\n capabilities: inner.capabilities,\n countTokens: (text) => inner.countTokens(text),\n validateConfig: () => inner.validateConfig(),\n stream: (request) => inner.stream(request),\n async complete(request: CompletionRequest): Promise<Result<CompletionResponse, ModelError>> {\n const start = getTimeProvider().now();\n const result = await inner.complete(request);\n const latencyMs = getTimeProvider().now() - start;\n try {\n if (result.ok) {\n const u = result.value.usage;\n recordUsageEvent({\n timestamp: new Date().toISOString(),\n modelId: inner.modelId,\n providerId: inner.providerId,\n inputTokens: u.inputTokens,\n outputTokens: u.outputTokens,\n usdCost: computeCostUSD(inner.modelId, u.inputTokens, u.outputTokens),\n latencyMs,\n success: true,\n });\n } else {\n recordUsageEvent({\n timestamp: new Date().toISOString(),\n modelId: inner.modelId,\n providerId: inner.providerId,\n inputTokens: 0,\n outputTokens: 0,\n usdCost: 0,\n latencyMs,\n success: false,\n errorCode: result.error.code,\n });\n }\n } catch {\n // Telemetry must not break user calls.\n }\n return result;\n },\n };\n attachListModels(wrapped, inner);\n return wrapped;\n}\n\n/**\n * (#2540) Forward `listModels` through the wrapper when the inner adapter\n * exposes one. Only attach when defined so the wrapper's `listModels?:`\n * hint stays accurate for the resolver. The inner reference is captured\n * by closure so the forwarded call binds `this` to the inner adapter.\n */\nfunction attachListModels(wrapped: IModelAdapter, inner: IModelAdapter): void {\n const list = inner.listModels?.bind(inner);\n if (list === undefined) return;\n wrapped.listModels = (): Promise<readonly ModelMetadata[]> => list();\n}\n\n/**\n * Convenience: read env, discover, return adapter instances for every\n * discovered model. Returns `null` (not an error) when env vars aren't set\n * — the caller treats unset gateway as \"no adapter from this source.\"\n *\n * Use case: the unified registry / factory calls this at startup; if the\n * operator has configured a gateway, every discovered model becomes a\n * dispatch target alongside the existing claude/codex/gemini/opencode\n * adapter slots.\n */\nexport async function buildOpenAICompatAdapters(): Promise<Result<\n readonly IModelAdapter[],\n ConfigError\n> | null> {\n const config = readOpenAICompatEnv();\n if (config === null) return null;\n const discovered = await discoverModels(config);\n if (!discovered.ok) return discovered;\n return ok(discovered.value.map((m) => createOpenAICompatAdapter(m.id, config)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8BA,SAAS,oBAAoB;AAI7B,IAAM,SAAS,aAAa,EAAE,WAAW,kBAAkB,CAAC;AAYrD,SAAS,oBAAoB,MAA4C;AAC9E,QAAM,MAAM,eAAe,IAAI;AAC/B,MAAI,QAAQ,KAAM,QAAO;AAEzB,QAAM,SAAS,gBAAgB,KAAK,IAAI;AACxC,MAAI,WAAW,KAAM,QAAO;AAE5B,QAAM,UAAU,2BAA2B,MAAM;AACjD,MAAI,YAAY,MAAM;AACpB,WAAO,MAAM,8DAA8D,EAAE,KAAK,CAAC;AACnF,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,OAAO,QAAQ,YAAY,WAAW,QAAQ,QAAQ,KAAK,IAAI;AAC/E,QAAM,YAAY,OAAO,QAAQ,WAAW,WAAW,QAAQ,OAAO,KAAK,IAAI;AAC/E,MAAI,YAAY,MAAM,cAAc,IAAI;AACtC,WAAO,KAAK,mEAAmE,EAAE,KAAK,CAAC;AACvF,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,wBAAwB,SAAS;AAChD,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,MACL;AAAA,MACA,EAAE,KAAK;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,6CAA6C,EAAE,SAAS,KAAK,CAAC;AAC1E,SAAO,EAAE,SAAS,OAAO;AAC3B;AAEA,SAAS,eAAe,MAA6B;AACnD,MAAI;AACF,WAAO,aAAa,MAAM,MAAM;AAAA,EAClC,SAASA,MAAc;AACrB,UAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,WAAO,MAAM,gCAAgC,EAAE,MAAM,OAAO,IAAI,CAAC;AACjE,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,KAAa,MAAuB;AAC3D,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAASA,MAAc;AACrB,UAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,WAAO,KAAK,6CAA6C,EAAE,MAAM,OAAO,IAAI,CAAC;AAC7E,WAAO;AAAA,EACT;AACF;AAOA,SAAS,2BACP,QACgD;AAChD,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,QAAM,OAAO;AACb,QAAM,YAAY,KAAK;AACvB,MAAI,OAAO,cAAc,YAAY,cAAc,KAAM,QAAO;AAChE,QAAM,WAAY,UAAsC,eAAe;AACvE,MAAI,OAAO,aAAa,YAAY,aAAa,KAAM,QAAO;AAC9D,QAAM,UAAW,SAAmC;AACpD,MAAI,OAAO,YAAY,YAAY,YAAY,KAAM,QAAO;AAC5D,SAAO;AACT;AAQA,SAAS,wBAAwB,OAA8B;AAC7D,QAAM,QAAQ,yBAAyB,KAAK,KAAK;AACjD,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,UAAU,MAAM,CAAC;AACvB,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,WAAW,QAAQ,IAAI,OAAO;AACpC,MAAI,aAAa,UAAa,aAAa,GAAI,QAAO;AACtD,SAAO;AACT;;;AChHA,OAAO,YAAY;AAyCZ,SAAS,sBAAiD;AAC/D,QAAM,UAAU,mBAAmB;AACnC,MAAI,YAAY,KAAM,QAAO;AAC7B,SAAO,wBAAwB;AACjC;AAEA,SAAS,qBAAgD;AACvD,QAAM,SAAS,QAAQ,IAAI,yBAAyB,GAAG,KAAK;AAC5D,QAAM,SAAS,QAAQ,IAAI,yBAAyB,GAAG,KAAK;AAC5D,MAAI,WAAW,UAAa,WAAW,GAAI,QAAO;AAClD,MAAI,WAAW,UAAa,WAAW,GAAI,QAAO;AAClD,SAAO,EAAE,SAAS,QAAQ,QAAQ,OAAO;AAC3C;AAEA,SAAS,0BAAqD;AAC5D,QAAM,eAAe,QAAQ,IAAI,uBAAuB,GAAG,KAAK;AAChE,MAAI,iBAAiB,UAAa,iBAAiB,GAAI,QAAO;AAC9D,QAAM,WAAW,oBAAoB,YAAY;AACjD,MAAI,aAAa,KAAM,QAAO;AAC9B,SAAO,EAAE,SAAS,SAAS,SAAS,QAAQ,SAAS,OAAO;AAC9D;AASA,eAAsB,eACpB,QAC0D;AAC1D,MAAI;AACF,UAAM,SAAS,IAAI,OAAO,EAAE,SAAS,OAAO,SAAS,QAAQ,OAAO,OAAO,CAAC;AAC5E,UAAM,OAAO,MAAM,OAAO,OAAO,KAAK;AACtC,UAAM,SAAqC,KAAK,KAAK,IAAI,CAAC,OAAO;AAAA,MAC/D,IAAI,EAAE;AAAA,MACN,SAAS,EAAE;AAAA,MACX,SAAS,EAAE;AAAA,IACb,EAAE;AACF,WAAO,GAAG,MAAM;AAAA,EAClB,SAAS,GAAY;AACnB,WAAO;AAAA,MACL,IAAI;AAAA,QACF,kCAAkC,OAAO,OAAO,KAAK,gBAAgB,CAAC,CAAC;AAAA,MAEzE;AAAA,IACF;AAAA,EACF;AACF;AAeO,SAAS,0BACd,SACA,QACe;AACf,QAAM,QAAQ,IAAI,cAAc,EAAE,SAAS,QAAQ,OAAO,QAAQ,SAAS,OAAO,QAAQ,CAAC;AAC3F,SAAO,mBAAmB,KAAK;AACjC;AAWA,SAAS,mBAAmB,OAAqC;AAC/D,QAAM,UAAyB;AAAA,IAC7B,YAAY,MAAM;AAAA,IAClB,SAAS,MAAM;AAAA,IACf,cAAc,MAAM;AAAA,IACpB,aAAa,CAAC,SAAS,MAAM,YAAY,IAAI;AAAA,IAC7C,gBAAgB,MAAM,MAAM,eAAe;AAAA,IAC3C,QAAQ,CAAC,YAAY,MAAM,OAAO,OAAO;AAAA,IACzC,MAAM,SAAS,SAA6E;AAC1F,YAAM,QAAQ,gBAAgB,EAAE,IAAI;AACpC,YAAM,SAAS,MAAM,MAAM,SAAS,OAAO;AAC3C,YAAM,YAAY,gBAAgB,EAAE,IAAI,IAAI;AAC5C,UAAI;AACF,YAAI,OAAO,IAAI;AACb,gBAAM,IAAI,OAAO,MAAM;AACvB,2BAAiB;AAAA,YACf,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,SAAS,MAAM;AAAA,YACf,YAAY,MAAM;AAAA,YAClB,aAAa,EAAE;AAAA,YACf,cAAc,EAAE;AAAA,YAChB,SAAS,eAAe,MAAM,SAAS,EAAE,aAAa,EAAE,YAAY;AAAA,YACpE;AAAA,YACA,SAAS;AAAA,UACX,CAAC;AAAA,QACH,OAAO;AACL,2BAAiB;AAAA,YACf,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,SAAS,MAAM;AAAA,YACf,YAAY,MAAM;AAAA,YAClB,aAAa;AAAA,YACb,cAAc;AAAA,YACd,SAAS;AAAA,YACT;AAAA,YACA,SAAS;AAAA,YACT,WAAW,OAAO,MAAM;AAAA,UAC1B,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,mBAAiB,SAAS,KAAK;AAC/B,SAAO;AACT;AAQA,SAAS,iBAAiB,SAAwB,OAA4B;AAC5E,QAAM,OAAO,MAAM,YAAY,KAAK,KAAK;AACzC,MAAI,SAAS,OAAW;AACxB,UAAQ,aAAa,MAAyC,KAAK;AACrE;AAYA,eAAsB,4BAGZ;AACR,QAAM,SAAS,oBAAoB;AACnC,MAAI,WAAW,KAAM,QAAO;AAC5B,QAAM,aAAa,MAAM,eAAe,MAAM;AAC9C,MAAI,CAAC,WAAW,GAAI,QAAO;AAC3B,SAAO,GAAG,WAAW,MAAM,IAAI,CAAC,MAAM,0BAA0B,EAAE,IAAI,MAAM,CAAC,CAAC;AAChF;","names":["err"]}
@@ -8,7 +8,7 @@ import {
8
8
  getErrorMessage,
9
9
  ok,
10
10
  parseBoolEnv
11
- } from "./chunk-4KBG4ICZ.js";
11
+ } from "./chunk-QJIHSQZX.js";
12
12
 
13
13
  // src/cli/research-helpers-io.ts
14
14
  import * as fs2 from "fs/promises";
@@ -146,4 +146,4 @@ export {
146
146
  loadPapersRegistry,
147
147
  savePapersRegistry
148
148
  };
149
- //# sourceMappingURL=chunk-HM77WNJG.js.map
149
+ //# sourceMappingURL=chunk-2CLFX73Y.js.map
@@ -13,10 +13,10 @@ import {
13
13
  getTimeProvider,
14
14
  ok,
15
15
  resolveToolClassGuardMs
16
- } from "./chunk-4KBG4ICZ.js";
16
+ } from "./chunk-QJIHSQZX.js";
17
17
  import {
18
18
  getNexusDataDir
19
- } from "./chunk-ZGLIHPGJ.js";
19
+ } from "./chunk-RUSP5PJR.js";
20
20
 
21
21
  // src/mcp/middleware/validation.ts
22
22
  function validateToolInput(schema, args) {
@@ -1990,4 +1990,4 @@ export {
1990
1990
  VOTING_THRESHOLDS,
1991
1991
  ConsensusMetricsSchema
1992
1992
  };
1993
- //# sourceMappingURL=chunk-4SNH5EMQ.js.map
1993
+ //# sourceMappingURL=chunk-4CLBJ2NA.js.map
@@ -4,7 +4,7 @@ import {
4
4
  err,
5
5
  getErrorMessage,
6
6
  ok
7
- } from "./chunk-4KBG4ICZ.js";
7
+ } from "./chunk-QJIHSQZX.js";
8
8
 
9
9
  // src/scm/types.ts
10
10
  var ScmError = class extends Error {
@@ -15,6 +15,9 @@ var ScmError = class extends Error {
15
15
  this.context = context;
16
16
  this.name = "ScmError";
17
17
  }
18
+ platform;
19
+ statusCode;
20
+ context;
18
21
  };
19
22
 
20
23
  // src/scm/github-provider.ts
@@ -131,6 +134,7 @@ var GitHubProvider = class {
131
134
  constructor(repo) {
132
135
  this.repo = repo;
133
136
  }
137
+ repo;
134
138
  platform = "github";
135
139
  async getIssue(number) {
136
140
  const fields = "number,title,body,labels,author,createdAt";
@@ -259,4 +263,4 @@ export {
259
263
  ScmError,
260
264
  GitHubProvider
261
265
  };
262
- //# sourceMappingURL=chunk-DRYU676G.js.map
266
+ //# sourceMappingURL=chunk-4DLHM6LI.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/scm/types.ts","../src/scm/github-provider.ts"],"sourcesContent":["/**\n * nexus-agents/scm - SCM Provider Types\n *\n * Shared types for the centralized SCM (Source Control Management) module.\n * Supports GitHub (REST API + gh CLI) with extensibility for GitLab/Gitea.\n *\n * @module scm/types\n * (Source: Issue #1136 — Centralized SCM Provider Module)\n */\n\nimport type { Result } from '../core/index.js';\n\n// ============================================================================\n// Token Types\n// ============================================================================\n\n/** Supported SCM platforms. */\nexport type ScmPlatform = 'github' | 'gitlab' | 'gitea';\n\n/** Token resolution strategy. */\nexport type TokenStrategy = 'env' | 'cli' | 'config';\n\n/** Resolved SCM token with metadata. */\nexport interface ScmToken {\n /** The raw token value */\n readonly value: string;\n /** How the token was resolved */\n readonly strategy: TokenStrategy;\n /** SCM platform this token is for */\n readonly platform: ScmPlatform;\n}\n\n/** Token resolution configuration. */\nexport interface TokenResolverConfig {\n /** Explicit token (highest priority) */\n readonly token?: string;\n /** SCM platform to resolve for */\n readonly platform?: ScmPlatform;\n /** Custom env var name override */\n readonly envVar?: string;\n}\n\n// ============================================================================\n// SCM Entity Types\n// ============================================================================\n\n/** SCM issue representation. */\nexport interface ScmIssue {\n readonly number: number;\n readonly title: string;\n readonly body: string;\n readonly labels: readonly string[];\n readonly author: string;\n readonly createdAt: string;\n}\n\n/** SCM pull/merge request representation. */\nexport interface ScmPullRequest {\n readonly number: number;\n readonly title: string;\n readonly body: string;\n readonly author: string;\n readonly base: string;\n readonly head: string;\n readonly url: string;\n}\n\n/** SCM comment representation. */\nexport interface ScmComment {\n readonly id: number;\n readonly body: string;\n readonly author: string;\n readonly createdAt: string;\n}\n\n/** PR creation options. */\nexport interface CreatePROptions {\n readonly title: string;\n readonly body: string;\n readonly head: string;\n readonly base: string;\n}\n\n/** PR merge options. */\nexport interface MergePROptions {\n readonly method?: 'merge' | 'squash' | 'rebase';\n readonly commitTitle?: string;\n readonly commitMessage?: string;\n readonly deleteBranch?: boolean;\n}\n\n/** PR status for merge eligibility. */\nexport interface PRStatus {\n readonly mergeable: boolean;\n readonly checksStatus: 'pending' | 'success' | 'failure';\n readonly reviewStatus: 'approved' | 'pending' | 'changes_requested';\n}\n\n/** Issue filter options. */\nexport interface IssueFilters {\n readonly labels?: readonly string[];\n readonly state?: 'open' | 'closed' | 'all';\n readonly limit?: number;\n}\n\n// ============================================================================\n// SCM Error\n// ============================================================================\n\n/** Unified SCM error with platform-aware context. */\nexport class ScmError extends Error {\n constructor(\n message: string,\n readonly platform: ScmPlatform,\n readonly statusCode?: number,\n readonly context?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'ScmError';\n }\n}\n\n// ============================================================================\n// Extended Entity Types (Trait support)\n// ============================================================================\n\n/** File change in a pull request. */\nexport interface ScmFileChange {\n readonly filename: string;\n readonly status: 'added' | 'removed' | 'modified' | 'renamed' | 'copied';\n readonly additions: number;\n readonly deletions: number;\n readonly patch?: string;\n readonly previousFilename?: string;\n}\n\n/** Extended PR with file diffs and stats. Used by IScmReviewer. */\nexport interface ScmPullRequestDetail extends ScmPullRequest {\n readonly draft: boolean;\n readonly authorAssociation: string;\n readonly labels: readonly string[];\n readonly files: readonly ScmFileChange[];\n readonly additions: number;\n readonly deletions: number;\n readonly headSha: string;\n}\n\n/** Extended issue with association and state. Used by IScmReviewer. */\nexport interface ScmIssueDetail extends ScmIssue {\n readonly authorAssociation: string;\n readonly state: string;\n readonly url: string;\n}\n\n/** Extended comment with author association. */\nexport interface ScmCommentDetail extends ScmComment {\n readonly authorAssociation: string;\n}\n\n/** Review decision for a pull request. */\nexport type ScmReviewDecision = 'approve' | 'request_changes' | 'comment';\n\n/** User metadata for reputation assessment. */\nexport interface ScmUserMetadata {\n readonly login: string;\n readonly name: string | null;\n readonly company: string | null;\n readonly followers: number;\n readonly following: number;\n readonly publicRepos: number;\n readonly createdAt: string;\n}\n\n// ============================================================================\n// Provider Interface (Core)\n// ============================================================================\n\n/**\n * Core SCM provider interface.\n *\n * All methods return `Result<T, ScmError>` for consistent error handling\n * across GitHub REST API, gh CLI, and future GitLab/Gitea backends.\n */\nexport interface IScmProvider {\n /** Platform identifier. */\n readonly platform: ScmPlatform;\n\n /** Repository in owner/repo format. */\n readonly repo: string;\n\n // Issues\n getIssue(number: number): Promise<Result<ScmIssue, ScmError>>;\n listIssues(filters?: IssueFilters): Promise<Result<readonly ScmIssue[], ScmError>>;\n createIssue(\n title: string,\n body: string,\n labels?: readonly string[]\n ): Promise<Result<ScmIssue, ScmError>>;\n addLabels(issueNumber: number, labels: readonly string[]): Promise<Result<void, ScmError>>;\n\n // Pull Requests\n createPR(options: CreatePROptions): Promise<Result<ScmPullRequest, ScmError>>;\n mergePR(prNumber: number, options?: MergePROptions): Promise<Result<void, ScmError>>;\n getPRStatus(prNumber: number): Promise<Result<PRStatus, ScmError>>;\n\n // Comments\n addComment(issueNumber: number, body: string): Promise<Result<void, ScmError>>;\n listComments(issueNumber: number): Promise<Result<readonly ScmComment[], ScmError>>;\n}\n\n// ============================================================================\n// Trait Interfaces (ISP — Interface Segregation Principle)\n// ============================================================================\n\n/**\n * Review trait — PR review capabilities.\n *\n * Implemented by platforms supporting code review workflows.\n * Consumers declare this trait when they need PR file diffs or review posting.\n */\nexport interface IScmReviewer {\n /** Fetch PR with full file diffs and stats. */\n getPullRequestDetail(prNumber: number): Promise<Result<ScmPullRequestDetail, ScmError>>;\n\n /** Post a review on a pull request. */\n createReview(\n prNumber: number,\n body: string,\n decision: ScmReviewDecision\n ): Promise<Result<void, ScmError>>;\n\n /** Fetch issue with author association and state. */\n getIssueDetail(issueNumber: number): Promise<Result<ScmIssueDetail, ScmError>>;\n\n /** List comments with author associations. */\n listCommentDetails(issueNumber: number): Promise<Result<readonly ScmCommentDetail[], ScmError>>;\n}\n\n/**\n * User info trait — user metadata for reputation assessment.\n *\n * Implemented by platforms supporting user profile queries.\n * Consumers declare this trait when they need author reputation data.\n */\nexport interface IScmUserInfo {\n /** Fetch user metadata for reputation assessment. */\n fetchUserMetadata(username: string): Promise<Result<ScmUserMetadata, ScmError>>;\n}\n\n/**\n * Convenience type: provider with review capabilities.\n * Used by PR review workflows.\n */\nexport type ReviewCapableProvider = IScmProvider & IScmReviewer;\n\n/**\n * Convenience type: provider with all capabilities.\n * Used by full triage workflows that need review + user info.\n */\nexport type FullCapableProvider = IScmProvider & IScmReviewer & IScmUserInfo;\n","/**\n * nexus-agents/scm - GitHub Provider\n *\n * Unified GitHub provider using gh CLI. Implements IScmProvider with\n * Result-based error handling. Replaced the prior dual-path GitHub\n * clients (dogfooding/github-client.ts deleted in #2553;\n * workflows/self-development/github-client.ts deleted in #2402).\n *\n * @module scm/github-provider\n * (Source: Issue #1136 — Centralized SCM Provider Module)\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { z } from 'zod';\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger, getErrorMessage } from '../core/index.js';\nimport type {\n IScmProvider,\n ScmIssue,\n ScmPullRequest,\n ScmComment,\n CreatePROptions,\n MergePROptions,\n PRStatus,\n IssueFilters,\n} from './types.js';\nimport { ScmError } from './types.js';\nimport { CLI_SUBPROCESS_TIMEOUTS } from '../config/timeouts.js';\n\nconst execFileAsync = promisify(execFile);\nconst logger = createLogger({ component: 'GitHubProvider' });\n\n/** Max buffer for gh CLI output (10MB). */\nconst MAX_BUFFER = 10 * 1024 * 1024;\n\n/** gh CLI subprocess runaway-guard. Centralized to the canonical gh-command\n * timeout in the central authority (#3736); same 30s value. */\nconst GH_TIMEOUT_MS = CLI_SUBPROCESS_TIMEOUTS.ghCommandMs;\n\n// ============================================================================\n// gh CLI JSON schemas (internal — #2962 site 4)\n//\n// Each schema mirrors the `--json <fields>` projection the corresponding\n// gh call asks for. They're applied via `safeParseGhJson` so a gh-schema\n// drift (a renamed field, a missing nullable, a removed nested object)\n// surfaces as a structured ScmError('schema mismatch') instead of the\n// previous TypeError-rewrapped-as-\"Failed to parse JSON\" — pre-#2962, the\n// JSON parsed fine and the mapper's `raw.labels.map` / `raw.author.login`\n// deref blew up, so debuggers chased a parser bug that didn't exist.\n// ============================================================================\n\nconst GhIssueJsonSchema = z.object({\n number: z.number(),\n title: z.string(),\n body: z.string().nullable(),\n labels: z.array(z.object({ name: z.string() })),\n author: z.object({ login: z.string() }),\n createdAt: z.string(),\n});\ntype GhIssueJson = z.infer<typeof GhIssueJsonSchema>;\n\nconst GhCommentJsonSchema = z.object({\n id: z.number(),\n body: z.string(),\n author: z.object({ login: z.string() }),\n createdAt: z.string(),\n});\ntype GhCommentJson = z.infer<typeof GhCommentJsonSchema>;\n\nconst GhPrJsonSchema = z.object({\n number: z.number(),\n title: z.string(),\n body: z.string().nullable(),\n url: z.string(),\n author: z.object({ login: z.string() }),\n baseRefName: z.string(),\n headRefName: z.string(),\n});\n// `createPR` consumes the parsed value inline; no GhPrJson alias needed.\n\nconst GhPrStatusJsonSchema = z.object({\n mergeable: z.string(),\n statusCheckRollup: z.array(z.object({ state: z.string() })).nullable(),\n reviewDecision: z.string().nullable(),\n});\ntype GhPrStatusJson = z.infer<typeof GhPrStatusJsonSchema>;\n\n/**\n * Parse + Zod-validate gh CLI output. Distinguishes:\n * - parse failure (gh returned non-JSON or empty) — \\`label: Failed to parse JSON\\`\n * - schema mismatch (gh returned valid JSON in an unexpected shape) — \\`label: schema mismatch\\`\n * Pre-#2962 both surfaced as the same misleading \"Failed to parse\" error.\n */\nfunction safeParseGhJson<T>(\n rawJson: string,\n schema: z.ZodType<T>,\n label: string\n): Result<T, ScmError> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawJson);\n } catch (error) {\n return err(\n new ScmError(\n `${label}: Failed to parse JSON: ${getErrorMessage(error)} — preview: ${rawJson.slice(0, 120)}`,\n 'github'\n )\n );\n }\n const result = schema.safeParse(parsed);\n if (!result.success) {\n return err(\n new ScmError(\n `${label}: schema mismatch — ${result.error.issues\n .slice(0, 3)\n .map((i) => `${i.path.join('.')}: ${i.message}`)\n .join('; ')} — preview: ${rawJson.slice(0, 120)}`,\n 'github'\n )\n );\n }\n return ok(result.data);\n}\n\n// ============================================================================\n// gh CLI executor\n// ============================================================================\n\nasync function execGh(args: readonly string[], repo: string): Promise<Result<string, ScmError>> {\n const fullArgs = [...args, '--repo', repo];\n\n try {\n const { stdout } = await execFileAsync('gh', fullArgs, {\n maxBuffer: MAX_BUFFER,\n timeout: GH_TIMEOUT_MS,\n });\n return ok(stdout.trim());\n } catch (error) {\n const execError = error as { message: string; stderr?: string };\n return err(\n new ScmError(`gh command failed: ${execError.message}`, 'github', undefined, {\n command: `gh ${fullArgs.join(' ')}`,\n stderr: execError.stderr,\n })\n );\n }\n}\n\n// ============================================================================\n// Mappers\n// ============================================================================\n\nfunction mapIssue(raw: GhIssueJson): ScmIssue {\n return {\n number: raw.number,\n title: raw.title,\n body: raw.body ?? '',\n labels: raw.labels.map((l) => l.name),\n author: raw.author.login,\n createdAt: raw.createdAt,\n };\n}\n\nfunction mapComment(raw: GhCommentJson): ScmComment {\n return {\n id: raw.id,\n body: raw.body,\n author: raw.author.login,\n createdAt: raw.createdAt,\n };\n}\n\nfunction mapPRStatus(raw: GhPrStatusJson): PRStatus {\n const mergeable = raw.mergeable === 'MERGEABLE';\n\n let checksStatus: 'pending' | 'success' | 'failure' = 'pending';\n if (raw.statusCheckRollup !== null && raw.statusCheckRollup.length > 0) {\n const hasFailure = raw.statusCheckRollup.some((c) => c.state === 'FAILURE');\n const allSuccess = raw.statusCheckRollup.every(\n (c) => c.state === 'SUCCESS' || c.state === 'NEUTRAL' || c.state === 'SKIPPED'\n );\n checksStatus = hasFailure ? 'failure' : allSuccess ? 'success' : 'pending';\n }\n\n let reviewStatus: 'approved' | 'pending' | 'changes_requested' = 'pending';\n if (raw.reviewDecision === 'APPROVED') reviewStatus = 'approved';\n else if (raw.reviewDecision === 'CHANGES_REQUESTED') reviewStatus = 'changes_requested';\n\n return { mergeable, checksStatus, reviewStatus };\n}\n\n// ============================================================================\n// Provider Implementation\n// ============================================================================\n\n/**\n * GitHub provider using the gh CLI.\n *\n * Requires: gh CLI installed and authenticated.\n */\nexport class GitHubProvider implements IScmProvider {\n readonly platform = 'github' as const;\n\n constructor(readonly repo: string) {}\n\n async getIssue(number: number): Promise<Result<ScmIssue, ScmError>> {\n const fields = 'number,title,body,labels,author,createdAt';\n const args = ['issue', 'view', String(number), '--json', fields];\n\n logger.debug('Getting issue', { repo: this.repo, number });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, GhIssueJsonSchema, 'getIssue');\n if (!parsed.ok) return parsed;\n return ok(mapIssue(parsed.value));\n }\n\n async listIssues(filters?: IssueFilters): Promise<Result<readonly ScmIssue[], ScmError>> {\n const fields = 'number,title,body,labels,author,createdAt';\n const args = ['issue', 'list', '--json', fields];\n\n if (filters?.labels !== undefined && filters.labels.length > 0) {\n args.push('--label', filters.labels.join(','));\n }\n if (filters?.state !== undefined) {\n args.push('--state', filters.state);\n }\n args.push('--limit', String(filters?.limit ?? 50));\n\n logger.debug('Listing issues', { repo: this.repo, filters });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, z.array(GhIssueJsonSchema), 'listIssues');\n if (!parsed.ok) return parsed;\n return ok(parsed.value.map(mapIssue));\n }\n\n async addLabels(issueNumber: number, labels: readonly string[]): Promise<Result<void, ScmError>> {\n const args = ['issue', 'edit', String(issueNumber), '--add-label', labels.join(',')];\n\n logger.debug('Adding labels', { repo: this.repo, issueNumber, labels });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n async createPR(options: CreatePROptions): Promise<Result<ScmPullRequest, ScmError>> {\n const fields = 'number,title,body,url,author,baseRefName,headRefName';\n const args = [\n 'pr',\n 'create',\n '--title',\n options.title,\n '--body',\n options.body,\n '--head',\n options.head,\n '--base',\n options.base,\n '--json',\n fields,\n ];\n\n logger.info('Creating PR', { repo: this.repo, title: options.title });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, GhPrJsonSchema, 'createPR');\n if (!parsed.ok) return parsed;\n const raw = parsed.value;\n return ok({\n number: raw.number,\n title: raw.title,\n body: raw.body ?? '',\n author: raw.author.login,\n base: raw.baseRefName,\n head: raw.headRefName,\n url: raw.url,\n });\n }\n\n async mergePR(prNumber: number, options?: MergePROptions): Promise<Result<void, ScmError>> {\n const method = options?.method ?? 'squash';\n const args = ['pr', 'merge', String(prNumber), `--${method}`];\n\n if (options?.commitTitle !== undefined) args.push('--subject', options.commitTitle);\n if (options?.commitMessage !== undefined) args.push('--body', options.commitMessage);\n if (options?.deleteBranch === true) args.push('--delete-branch');\n\n logger.info('Merging PR', { repo: this.repo, prNumber, method });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n async getPRStatus(prNumber: number): Promise<Result<PRStatus, ScmError>> {\n const fields = 'mergeable,statusCheckRollup,reviewDecision';\n const args = ['pr', 'view', String(prNumber), '--json', fields];\n\n logger.debug('Getting PR status', { repo: this.repo, prNumber });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, GhPrStatusJsonSchema, 'getPRStatus');\n if (!parsed.ok) return parsed;\n return ok(mapPRStatus(parsed.value));\n }\n\n async createIssue(\n title: string,\n body: string,\n labels?: readonly string[]\n ): Promise<Result<ScmIssue, ScmError>> {\n const args = ['issue', 'create', '--title', title, '--body', body];\n if (labels !== undefined && labels.length > 0) args.push('--label', labels.join(','));\n logger.debug('Creating issue', { repo: this.repo, title });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n const url = result.value.trim();\n const match = /\\/(\\d+)$/.exec(url);\n const number = match?.[1] !== undefined ? parseInt(match[1], 10) : 0;\n return ok({\n number,\n title,\n body,\n labels: labels !== undefined ? [...labels] : [],\n author: 'pipeline',\n createdAt: new Date().toISOString(),\n });\n }\n\n async addComment(issueNumber: number, body: string): Promise<Result<void, ScmError>> {\n const args = ['issue', 'comment', String(issueNumber), '--body', body];\n\n logger.debug('Adding comment', { repo: this.repo, issueNumber });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n async listComments(issueNumber: number): Promise<Result<readonly ScmComment[], ScmError>> {\n const args = ['issue', 'view', String(issueNumber), '--json', 'comments', '--jq', '.comments'];\n\n logger.debug('Listing comments', { repo: this.repo, issueNumber });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, z.array(GhCommentJsonSchema), 'listComments');\n if (!parsed.ok) return parsed;\n return ok(parsed.value.map(mapComment));\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/scm/types.ts","../src/scm/github-provider.ts"],"sourcesContent":["/**\n * nexus-agents/scm - SCM Provider Types\n *\n * Shared types for the centralized SCM (Source Control Management) module.\n * Supports GitHub (REST API + gh CLI) with extensibility for GitLab/Gitea.\n *\n * @module scm/types\n * (Source: Issue #1136 — Centralized SCM Provider Module)\n */\n\nimport type { Result } from '../core/index.js';\n\n// ============================================================================\n// Token Types\n// ============================================================================\n\n/** Supported SCM platforms. */\nexport type ScmPlatform = 'github' | 'gitlab' | 'gitea';\n\n/** Token resolution strategy. */\nexport type TokenStrategy = 'env' | 'cli' | 'config';\n\n/** Resolved SCM token with metadata. */\nexport interface ScmToken {\n /** The raw token value */\n readonly value: string;\n /** How the token was resolved */\n readonly strategy: TokenStrategy;\n /** SCM platform this token is for */\n readonly platform: ScmPlatform;\n}\n\n/** Token resolution configuration. */\nexport interface TokenResolverConfig {\n /** Explicit token (highest priority) */\n readonly token?: string;\n /** SCM platform to resolve for */\n readonly platform?: ScmPlatform;\n /** Custom env var name override */\n readonly envVar?: string;\n}\n\n// ============================================================================\n// SCM Entity Types\n// ============================================================================\n\n/** SCM issue representation. */\nexport interface ScmIssue {\n readonly number: number;\n readonly title: string;\n readonly body: string;\n readonly labels: readonly string[];\n readonly author: string;\n readonly createdAt: string;\n}\n\n/** SCM pull/merge request representation. */\nexport interface ScmPullRequest {\n readonly number: number;\n readonly title: string;\n readonly body: string;\n readonly author: string;\n readonly base: string;\n readonly head: string;\n readonly url: string;\n}\n\n/** SCM comment representation. */\nexport interface ScmComment {\n readonly id: number;\n readonly body: string;\n readonly author: string;\n readonly createdAt: string;\n}\n\n/** PR creation options. */\nexport interface CreatePROptions {\n readonly title: string;\n readonly body: string;\n readonly head: string;\n readonly base: string;\n}\n\n/** PR merge options. */\nexport interface MergePROptions {\n readonly method?: 'merge' | 'squash' | 'rebase';\n readonly commitTitle?: string;\n readonly commitMessage?: string;\n readonly deleteBranch?: boolean;\n}\n\n/** PR status for merge eligibility. */\nexport interface PRStatus {\n readonly mergeable: boolean;\n readonly checksStatus: 'pending' | 'success' | 'failure';\n readonly reviewStatus: 'approved' | 'pending' | 'changes_requested';\n}\n\n/** Issue filter options. */\nexport interface IssueFilters {\n readonly labels?: readonly string[];\n readonly state?: 'open' | 'closed' | 'all';\n readonly limit?: number;\n}\n\n// ============================================================================\n// SCM Error\n// ============================================================================\n\n/** Unified SCM error with platform-aware context. */\nexport class ScmError extends Error {\n constructor(\n message: string,\n readonly platform: ScmPlatform,\n readonly statusCode?: number,\n readonly context?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'ScmError';\n }\n}\n\n// ============================================================================\n// Extended Entity Types (Trait support)\n// ============================================================================\n\n/** File change in a pull request. */\nexport interface ScmFileChange {\n readonly filename: string;\n readonly status: 'added' | 'removed' | 'modified' | 'renamed' | 'copied';\n readonly additions: number;\n readonly deletions: number;\n readonly patch?: string;\n readonly previousFilename?: string;\n}\n\n/** Extended PR with file diffs and stats. Used by IScmReviewer. */\nexport interface ScmPullRequestDetail extends ScmPullRequest {\n readonly draft: boolean;\n readonly authorAssociation: string;\n readonly labels: readonly string[];\n readonly files: readonly ScmFileChange[];\n readonly additions: number;\n readonly deletions: number;\n readonly headSha: string;\n}\n\n/** Extended issue with association and state. Used by IScmReviewer. */\nexport interface ScmIssueDetail extends ScmIssue {\n readonly authorAssociation: string;\n readonly state: string;\n readonly url: string;\n}\n\n/** Extended comment with author association. */\nexport interface ScmCommentDetail extends ScmComment {\n readonly authorAssociation: string;\n}\n\n/** Review decision for a pull request. */\nexport type ScmReviewDecision = 'approve' | 'request_changes' | 'comment';\n\n/** User metadata for reputation assessment. */\nexport interface ScmUserMetadata {\n readonly login: string;\n readonly name: string | null;\n readonly company: string | null;\n readonly followers: number;\n readonly following: number;\n readonly publicRepos: number;\n readonly createdAt: string;\n}\n\n// ============================================================================\n// Provider Interface (Core)\n// ============================================================================\n\n/**\n * Core SCM provider interface.\n *\n * All methods return `Result<T, ScmError>` for consistent error handling\n * across GitHub REST API, gh CLI, and future GitLab/Gitea backends.\n */\nexport interface IScmProvider {\n /** Platform identifier. */\n readonly platform: ScmPlatform;\n\n /** Repository in owner/repo format. */\n readonly repo: string;\n\n // Issues\n getIssue(number: number): Promise<Result<ScmIssue, ScmError>>;\n listIssues(filters?: IssueFilters): Promise<Result<readonly ScmIssue[], ScmError>>;\n createIssue(\n title: string,\n body: string,\n labels?: readonly string[]\n ): Promise<Result<ScmIssue, ScmError>>;\n addLabels(issueNumber: number, labels: readonly string[]): Promise<Result<void, ScmError>>;\n\n // Pull Requests\n createPR(options: CreatePROptions): Promise<Result<ScmPullRequest, ScmError>>;\n mergePR(prNumber: number, options?: MergePROptions): Promise<Result<void, ScmError>>;\n getPRStatus(prNumber: number): Promise<Result<PRStatus, ScmError>>;\n\n // Comments\n addComment(issueNumber: number, body: string): Promise<Result<void, ScmError>>;\n listComments(issueNumber: number): Promise<Result<readonly ScmComment[], ScmError>>;\n}\n\n// ============================================================================\n// Trait Interfaces (ISP — Interface Segregation Principle)\n// ============================================================================\n\n/**\n * Review trait — PR review capabilities.\n *\n * Implemented by platforms supporting code review workflows.\n * Consumers declare this trait when they need PR file diffs or review posting.\n */\nexport interface IScmReviewer {\n /** Fetch PR with full file diffs and stats. */\n getPullRequestDetail(prNumber: number): Promise<Result<ScmPullRequestDetail, ScmError>>;\n\n /** Post a review on a pull request. */\n createReview(\n prNumber: number,\n body: string,\n decision: ScmReviewDecision\n ): Promise<Result<void, ScmError>>;\n\n /** Fetch issue with author association and state. */\n getIssueDetail(issueNumber: number): Promise<Result<ScmIssueDetail, ScmError>>;\n\n /** List comments with author associations. */\n listCommentDetails(issueNumber: number): Promise<Result<readonly ScmCommentDetail[], ScmError>>;\n}\n\n/**\n * User info trait — user metadata for reputation assessment.\n *\n * Implemented by platforms supporting user profile queries.\n * Consumers declare this trait when they need author reputation data.\n */\nexport interface IScmUserInfo {\n /** Fetch user metadata for reputation assessment. */\n fetchUserMetadata(username: string): Promise<Result<ScmUserMetadata, ScmError>>;\n}\n\n/**\n * Convenience type: provider with review capabilities.\n * Used by PR review workflows.\n */\nexport type ReviewCapableProvider = IScmProvider & IScmReviewer;\n\n/**\n * Convenience type: provider with all capabilities.\n * Used by full triage workflows that need review + user info.\n */\nexport type FullCapableProvider = IScmProvider & IScmReviewer & IScmUserInfo;\n","/**\n * nexus-agents/scm - GitHub Provider\n *\n * Unified GitHub provider using gh CLI. Implements IScmProvider with\n * Result-based error handling. Replaced the prior dual-path GitHub\n * clients (dogfooding/github-client.ts deleted in #2553;\n * workflows/self-development/github-client.ts deleted in #2402).\n *\n * @module scm/github-provider\n * (Source: Issue #1136 — Centralized SCM Provider Module)\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { z } from 'zod';\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger, getErrorMessage } from '../core/index.js';\nimport type {\n IScmProvider,\n ScmIssue,\n ScmPullRequest,\n ScmComment,\n CreatePROptions,\n MergePROptions,\n PRStatus,\n IssueFilters,\n} from './types.js';\nimport { ScmError } from './types.js';\nimport { CLI_SUBPROCESS_TIMEOUTS } from '../config/timeouts.js';\n\nconst execFileAsync = promisify(execFile);\nconst logger = createLogger({ component: 'GitHubProvider' });\n\n/** Max buffer for gh CLI output (10MB). */\nconst MAX_BUFFER = 10 * 1024 * 1024;\n\n/** gh CLI subprocess runaway-guard. Centralized to the canonical gh-command\n * timeout in the central authority (#3736); same 30s value. */\nconst GH_TIMEOUT_MS = CLI_SUBPROCESS_TIMEOUTS.ghCommandMs;\n\n// ============================================================================\n// gh CLI JSON schemas (internal — #2962 site 4)\n//\n// Each schema mirrors the `--json <fields>` projection the corresponding\n// gh call asks for. They're applied via `safeParseGhJson` so a gh-schema\n// drift (a renamed field, a missing nullable, a removed nested object)\n// surfaces as a structured ScmError('schema mismatch') instead of the\n// previous TypeError-rewrapped-as-\"Failed to parse JSON\" — pre-#2962, the\n// JSON parsed fine and the mapper's `raw.labels.map` / `raw.author.login`\n// deref blew up, so debuggers chased a parser bug that didn't exist.\n// ============================================================================\n\nconst GhIssueJsonSchema = z.object({\n number: z.number(),\n title: z.string(),\n body: z.string().nullable(),\n labels: z.array(z.object({ name: z.string() })),\n author: z.object({ login: z.string() }),\n createdAt: z.string(),\n});\ntype GhIssueJson = z.infer<typeof GhIssueJsonSchema>;\n\nconst GhCommentJsonSchema = z.object({\n id: z.number(),\n body: z.string(),\n author: z.object({ login: z.string() }),\n createdAt: z.string(),\n});\ntype GhCommentJson = z.infer<typeof GhCommentJsonSchema>;\n\nconst GhPrJsonSchema = z.object({\n number: z.number(),\n title: z.string(),\n body: z.string().nullable(),\n url: z.string(),\n author: z.object({ login: z.string() }),\n baseRefName: z.string(),\n headRefName: z.string(),\n});\n// `createPR` consumes the parsed value inline; no GhPrJson alias needed.\n\nconst GhPrStatusJsonSchema = z.object({\n mergeable: z.string(),\n statusCheckRollup: z.array(z.object({ state: z.string() })).nullable(),\n reviewDecision: z.string().nullable(),\n});\ntype GhPrStatusJson = z.infer<typeof GhPrStatusJsonSchema>;\n\n/**\n * Parse + Zod-validate gh CLI output. Distinguishes:\n * - parse failure (gh returned non-JSON or empty) — \\`label: Failed to parse JSON\\`\n * - schema mismatch (gh returned valid JSON in an unexpected shape) — \\`label: schema mismatch\\`\n * Pre-#2962 both surfaced as the same misleading \"Failed to parse\" error.\n */\nfunction safeParseGhJson<T>(\n rawJson: string,\n schema: z.ZodType<T>,\n label: string\n): Result<T, ScmError> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawJson);\n } catch (error) {\n return err(\n new ScmError(\n `${label}: Failed to parse JSON: ${getErrorMessage(error)} — preview: ${rawJson.slice(0, 120)}`,\n 'github'\n )\n );\n }\n const result = schema.safeParse(parsed);\n if (!result.success) {\n return err(\n new ScmError(\n `${label}: schema mismatch — ${result.error.issues\n .slice(0, 3)\n .map((i) => `${i.path.join('.')}: ${i.message}`)\n .join('; ')} — preview: ${rawJson.slice(0, 120)}`,\n 'github'\n )\n );\n }\n return ok(result.data);\n}\n\n// ============================================================================\n// gh CLI executor\n// ============================================================================\n\nasync function execGh(args: readonly string[], repo: string): Promise<Result<string, ScmError>> {\n const fullArgs = [...args, '--repo', repo];\n\n try {\n const { stdout } = await execFileAsync('gh', fullArgs, {\n maxBuffer: MAX_BUFFER,\n timeout: GH_TIMEOUT_MS,\n });\n return ok(stdout.trim());\n } catch (error) {\n const execError = error as { message: string; stderr?: string };\n return err(\n new ScmError(`gh command failed: ${execError.message}`, 'github', undefined, {\n command: `gh ${fullArgs.join(' ')}`,\n stderr: execError.stderr,\n })\n );\n }\n}\n\n// ============================================================================\n// Mappers\n// ============================================================================\n\nfunction mapIssue(raw: GhIssueJson): ScmIssue {\n return {\n number: raw.number,\n title: raw.title,\n body: raw.body ?? '',\n labels: raw.labels.map((l) => l.name),\n author: raw.author.login,\n createdAt: raw.createdAt,\n };\n}\n\nfunction mapComment(raw: GhCommentJson): ScmComment {\n return {\n id: raw.id,\n body: raw.body,\n author: raw.author.login,\n createdAt: raw.createdAt,\n };\n}\n\nfunction mapPRStatus(raw: GhPrStatusJson): PRStatus {\n const mergeable = raw.mergeable === 'MERGEABLE';\n\n let checksStatus: 'pending' | 'success' | 'failure' = 'pending';\n if (raw.statusCheckRollup !== null && raw.statusCheckRollup.length > 0) {\n const hasFailure = raw.statusCheckRollup.some((c) => c.state === 'FAILURE');\n const allSuccess = raw.statusCheckRollup.every(\n (c) => c.state === 'SUCCESS' || c.state === 'NEUTRAL' || c.state === 'SKIPPED'\n );\n checksStatus = hasFailure ? 'failure' : allSuccess ? 'success' : 'pending';\n }\n\n let reviewStatus: 'approved' | 'pending' | 'changes_requested' = 'pending';\n if (raw.reviewDecision === 'APPROVED') reviewStatus = 'approved';\n else if (raw.reviewDecision === 'CHANGES_REQUESTED') reviewStatus = 'changes_requested';\n\n return { mergeable, checksStatus, reviewStatus };\n}\n\n// ============================================================================\n// Provider Implementation\n// ============================================================================\n\n/**\n * GitHub provider using the gh CLI.\n *\n * Requires: gh CLI installed and authenticated.\n */\nexport class GitHubProvider implements IScmProvider {\n readonly platform = 'github' as const;\n\n constructor(readonly repo: string) {}\n\n async getIssue(number: number): Promise<Result<ScmIssue, ScmError>> {\n const fields = 'number,title,body,labels,author,createdAt';\n const args = ['issue', 'view', String(number), '--json', fields];\n\n logger.debug('Getting issue', { repo: this.repo, number });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, GhIssueJsonSchema, 'getIssue');\n if (!parsed.ok) return parsed;\n return ok(mapIssue(parsed.value));\n }\n\n async listIssues(filters?: IssueFilters): Promise<Result<readonly ScmIssue[], ScmError>> {\n const fields = 'number,title,body,labels,author,createdAt';\n const args = ['issue', 'list', '--json', fields];\n\n if (filters?.labels !== undefined && filters.labels.length > 0) {\n args.push('--label', filters.labels.join(','));\n }\n if (filters?.state !== undefined) {\n args.push('--state', filters.state);\n }\n args.push('--limit', String(filters?.limit ?? 50));\n\n logger.debug('Listing issues', { repo: this.repo, filters });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, z.array(GhIssueJsonSchema), 'listIssues');\n if (!parsed.ok) return parsed;\n return ok(parsed.value.map(mapIssue));\n }\n\n async addLabels(issueNumber: number, labels: readonly string[]): Promise<Result<void, ScmError>> {\n const args = ['issue', 'edit', String(issueNumber), '--add-label', labels.join(',')];\n\n logger.debug('Adding labels', { repo: this.repo, issueNumber, labels });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n async createPR(options: CreatePROptions): Promise<Result<ScmPullRequest, ScmError>> {\n const fields = 'number,title,body,url,author,baseRefName,headRefName';\n const args = [\n 'pr',\n 'create',\n '--title',\n options.title,\n '--body',\n options.body,\n '--head',\n options.head,\n '--base',\n options.base,\n '--json',\n fields,\n ];\n\n logger.info('Creating PR', { repo: this.repo, title: options.title });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, GhPrJsonSchema, 'createPR');\n if (!parsed.ok) return parsed;\n const raw = parsed.value;\n return ok({\n number: raw.number,\n title: raw.title,\n body: raw.body ?? '',\n author: raw.author.login,\n base: raw.baseRefName,\n head: raw.headRefName,\n url: raw.url,\n });\n }\n\n async mergePR(prNumber: number, options?: MergePROptions): Promise<Result<void, ScmError>> {\n const method = options?.method ?? 'squash';\n const args = ['pr', 'merge', String(prNumber), `--${method}`];\n\n if (options?.commitTitle !== undefined) args.push('--subject', options.commitTitle);\n if (options?.commitMessage !== undefined) args.push('--body', options.commitMessage);\n if (options?.deleteBranch === true) args.push('--delete-branch');\n\n logger.info('Merging PR', { repo: this.repo, prNumber, method });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n async getPRStatus(prNumber: number): Promise<Result<PRStatus, ScmError>> {\n const fields = 'mergeable,statusCheckRollup,reviewDecision';\n const args = ['pr', 'view', String(prNumber), '--json', fields];\n\n logger.debug('Getting PR status', { repo: this.repo, prNumber });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, GhPrStatusJsonSchema, 'getPRStatus');\n if (!parsed.ok) return parsed;\n return ok(mapPRStatus(parsed.value));\n }\n\n async createIssue(\n title: string,\n body: string,\n labels?: readonly string[]\n ): Promise<Result<ScmIssue, ScmError>> {\n const args = ['issue', 'create', '--title', title, '--body', body];\n if (labels !== undefined && labels.length > 0) args.push('--label', labels.join(','));\n logger.debug('Creating issue', { repo: this.repo, title });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n const url = result.value.trim();\n const match = /\\/(\\d+)$/.exec(url);\n const number = match?.[1] !== undefined ? parseInt(match[1], 10) : 0;\n return ok({\n number,\n title,\n body,\n labels: labels !== undefined ? [...labels] : [],\n author: 'pipeline',\n createdAt: new Date().toISOString(),\n });\n }\n\n async addComment(issueNumber: number, body: string): Promise<Result<void, ScmError>> {\n const args = ['issue', 'comment', String(issueNumber), '--body', body];\n\n logger.debug('Adding comment', { repo: this.repo, issueNumber });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n async listComments(issueNumber: number): Promise<Result<readonly ScmComment[], ScmError>> {\n const args = ['issue', 'view', String(issueNumber), '--json', 'comments', '--jq', '.comments'];\n\n logger.debug('Listing comments', { repo: this.repo, issueNumber });\n const result = await execGh(args, this.repo);\n if (!result.ok) return result;\n\n const parsed = safeParseGhJson(result.value, z.array(GhCommentJsonSchema), 'listComments');\n if (!parsed.ok) return parsed;\n return ok(parsed.value.map(mapComment));\n }\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  MODEL_IDS,
4
4
  getTimeProvider,
5
5
  resolveModelIdentitySync
6
- } from "./chunk-4KBG4ICZ.js";
6
+ } from "./chunk-QJIHSQZX.js";
7
7
 
8
8
  // src/config/model-availability.ts
9
9
  var DEFAULT_TTL_MS = 6e4;
@@ -150,4 +150,4 @@ export {
150
150
  resetAvailabilityCache,
151
151
  filterAvailableModels
152
152
  };
153
- //# sourceMappingURL=chunk-JPBPQN4U.js.map
153
+ //# sourceMappingURL=chunk-7BONPRR2.js.map
@@ -2,14 +2,14 @@ import {
2
2
  CircuitBreakerRegistry,
3
3
  CircuitError,
4
4
  mapCliErrorToCategory
5
- } from "./chunk-VZ3TTUWP.js";
5
+ } from "./chunk-ZTRBMKXK.js";
6
6
  import {
7
7
  createLogger,
8
8
  err,
9
9
  getFallbackChainForCategory,
10
10
  getTimeProvider,
11
11
  ok
12
- } from "./chunk-4KBG4ICZ.js";
12
+ } from "./chunk-QJIHSQZX.js";
13
13
 
14
14
  // src/cli-adapters/cli-circuit-breaker.ts
15
15
  var CATEGORY_TO_FALLBACK = {
@@ -152,4 +152,4 @@ export {
152
152
  CliCircuitBreakerIntegration,
153
153
  createCliCircuitBreakerIntegration
154
154
  };
155
- //# sourceMappingURL=chunk-O563YLBH.js.map
155
+ //# sourceMappingURL=chunk-CI5KGCX5.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getRandomProvider,
3
3
  getTimeProvider
4
- } from "./chunk-4KBG4ICZ.js";
4
+ } from "./chunk-QJIHSQZX.js";
5
5
 
6
6
  // src/utils/id-utils.ts
7
7
  import { randomUUID } from "crypto";
@@ -24,4 +24,4 @@ export {
24
24
  generateHyphenId,
25
25
  generateUUID
26
26
  };
27
- //# sourceMappingURL=chunk-PARI43LB.js.map
27
+ //# sourceMappingURL=chunk-E4IYX7LD.js.map
@@ -10,7 +10,7 @@ import {
10
10
  getTokenEstimator,
11
11
  isRateLimitLikeError,
12
12
  ok
13
- } from "./chunk-4KBG4ICZ.js";
13
+ } from "./chunk-QJIHSQZX.js";
14
14
 
15
15
  // src/adapters/base-adapter.ts
16
16
  var AdapterModelError = class extends ModelError {
@@ -707,4 +707,4 @@ export {
707
707
  DEFAULT_COLLECT_STREAM_MAX_CHUNKS,
708
708
  collectStream
709
709
  };
710
- //# sourceMappingURL=chunk-JSYFPXID.js.map
710
+ //# sourceMappingURL=chunk-ECDJM6UB.js.map