@yuaone/core 0.1.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 (235) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +15 -0
  3. package/dist/__tests__/context-manager.test.d.ts +6 -0
  4. package/dist/__tests__/context-manager.test.d.ts.map +1 -0
  5. package/dist/__tests__/context-manager.test.js +220 -0
  6. package/dist/__tests__/context-manager.test.js.map +1 -0
  7. package/dist/__tests__/governor.test.d.ts +6 -0
  8. package/dist/__tests__/governor.test.d.ts.map +1 -0
  9. package/dist/__tests__/governor.test.js +210 -0
  10. package/dist/__tests__/governor.test.js.map +1 -0
  11. package/dist/__tests__/model-router.test.d.ts +6 -0
  12. package/dist/__tests__/model-router.test.d.ts.map +1 -0
  13. package/dist/__tests__/model-router.test.js +329 -0
  14. package/dist/__tests__/model-router.test.js.map +1 -0
  15. package/dist/agent-logger.d.ts +384 -0
  16. package/dist/agent-logger.d.ts.map +1 -0
  17. package/dist/agent-logger.js +820 -0
  18. package/dist/agent-logger.js.map +1 -0
  19. package/dist/agent-loop.d.ts +163 -0
  20. package/dist/agent-loop.d.ts.map +1 -0
  21. package/dist/agent-loop.js +609 -0
  22. package/dist/agent-loop.js.map +1 -0
  23. package/dist/agent-modes.d.ts +85 -0
  24. package/dist/agent-modes.d.ts.map +1 -0
  25. package/dist/agent-modes.js +418 -0
  26. package/dist/agent-modes.js.map +1 -0
  27. package/dist/approval.d.ts +137 -0
  28. package/dist/approval.d.ts.map +1 -0
  29. package/dist/approval.js +299 -0
  30. package/dist/approval.js.map +1 -0
  31. package/dist/async-completion-queue.d.ts +56 -0
  32. package/dist/async-completion-queue.d.ts.map +1 -0
  33. package/dist/async-completion-queue.js +77 -0
  34. package/dist/async-completion-queue.js.map +1 -0
  35. package/dist/auto-fix.d.ts +174 -0
  36. package/dist/auto-fix.d.ts.map +1 -0
  37. package/dist/auto-fix.js +319 -0
  38. package/dist/auto-fix.js.map +1 -0
  39. package/dist/codebase-context.d.ts +396 -0
  40. package/dist/codebase-context.d.ts.map +1 -0
  41. package/dist/codebase-context.js +1260 -0
  42. package/dist/codebase-context.js.map +1 -0
  43. package/dist/conflict-resolver.d.ts +191 -0
  44. package/dist/conflict-resolver.d.ts.map +1 -0
  45. package/dist/conflict-resolver.js +524 -0
  46. package/dist/conflict-resolver.js.map +1 -0
  47. package/dist/constants.d.ts +52 -0
  48. package/dist/constants.d.ts.map +1 -0
  49. package/dist/constants.js +141 -0
  50. package/dist/constants.js.map +1 -0
  51. package/dist/context-budget.d.ts +435 -0
  52. package/dist/context-budget.d.ts.map +1 -0
  53. package/dist/context-budget.js +903 -0
  54. package/dist/context-budget.js.map +1 -0
  55. package/dist/context-compressor.d.ts +143 -0
  56. package/dist/context-compressor.d.ts.map +1 -0
  57. package/dist/context-compressor.js +511 -0
  58. package/dist/context-compressor.js.map +1 -0
  59. package/dist/context-manager.d.ts +112 -0
  60. package/dist/context-manager.d.ts.map +1 -0
  61. package/dist/context-manager.js +247 -0
  62. package/dist/context-manager.js.map +1 -0
  63. package/dist/continuous-reflection.d.ts +267 -0
  64. package/dist/continuous-reflection.d.ts.map +1 -0
  65. package/dist/continuous-reflection.js +338 -0
  66. package/dist/continuous-reflection.js.map +1 -0
  67. package/dist/cross-file-refactor.d.ts +352 -0
  68. package/dist/cross-file-refactor.d.ts.map +1 -0
  69. package/dist/cross-file-refactor.js +1544 -0
  70. package/dist/cross-file-refactor.js.map +1 -0
  71. package/dist/dag-orchestrator.d.ts +138 -0
  72. package/dist/dag-orchestrator.d.ts.map +1 -0
  73. package/dist/dag-orchestrator.js +379 -0
  74. package/dist/dag-orchestrator.js.map +1 -0
  75. package/dist/debate-orchestrator.d.ts +301 -0
  76. package/dist/debate-orchestrator.d.ts.map +1 -0
  77. package/dist/debate-orchestrator.js +719 -0
  78. package/dist/debate-orchestrator.js.map +1 -0
  79. package/dist/dependency-analyzer.d.ts +113 -0
  80. package/dist/dependency-analyzer.d.ts.map +1 -0
  81. package/dist/dependency-analyzer.js +444 -0
  82. package/dist/dependency-analyzer.js.map +1 -0
  83. package/dist/design-loop.d.ts +59 -0
  84. package/dist/design-loop.d.ts.map +1 -0
  85. package/dist/design-loop.js +344 -0
  86. package/dist/design-loop.js.map +1 -0
  87. package/dist/doc-intelligence.d.ts +383 -0
  88. package/dist/doc-intelligence.d.ts.map +1 -0
  89. package/dist/doc-intelligence.js +1307 -0
  90. package/dist/doc-intelligence.js.map +1 -0
  91. package/dist/dynamic-role-generator.d.ts +76 -0
  92. package/dist/dynamic-role-generator.d.ts.map +1 -0
  93. package/dist/dynamic-role-generator.js +194 -0
  94. package/dist/dynamic-role-generator.js.map +1 -0
  95. package/dist/errors.d.ts +69 -0
  96. package/dist/errors.d.ts.map +1 -0
  97. package/dist/errors.js +102 -0
  98. package/dist/errors.js.map +1 -0
  99. package/dist/event-bus.d.ts +159 -0
  100. package/dist/event-bus.d.ts.map +1 -0
  101. package/dist/event-bus.js +305 -0
  102. package/dist/event-bus.js.map +1 -0
  103. package/dist/execution-engine.d.ts +425 -0
  104. package/dist/execution-engine.d.ts.map +1 -0
  105. package/dist/execution-engine.js +1555 -0
  106. package/dist/execution-engine.js.map +1 -0
  107. package/dist/git-intelligence.d.ts +306 -0
  108. package/dist/git-intelligence.d.ts.map +1 -0
  109. package/dist/git-intelligence.js +1099 -0
  110. package/dist/git-intelligence.js.map +1 -0
  111. package/dist/governor.d.ts +77 -0
  112. package/dist/governor.d.ts.map +1 -0
  113. package/dist/governor.js +161 -0
  114. package/dist/governor.js.map +1 -0
  115. package/dist/hierarchical-planner.d.ts +313 -0
  116. package/dist/hierarchical-planner.d.ts.map +1 -0
  117. package/dist/hierarchical-planner.js +981 -0
  118. package/dist/hierarchical-planner.js.map +1 -0
  119. package/dist/index.d.ts +121 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +123 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/intent-inference.d.ts +103 -0
  124. package/dist/intent-inference.d.ts.map +1 -0
  125. package/dist/intent-inference.js +605 -0
  126. package/dist/intent-inference.js.map +1 -0
  127. package/dist/interrupt-manager.d.ts +143 -0
  128. package/dist/interrupt-manager.d.ts.map +1 -0
  129. package/dist/interrupt-manager.js +196 -0
  130. package/dist/interrupt-manager.js.map +1 -0
  131. package/dist/kernel.d.ts +564 -0
  132. package/dist/kernel.d.ts.map +1 -0
  133. package/dist/kernel.js +1419 -0
  134. package/dist/kernel.js.map +1 -0
  135. package/dist/language-support.d.ts +232 -0
  136. package/dist/language-support.d.ts.map +1 -0
  137. package/dist/language-support.js +1134 -0
  138. package/dist/language-support.js.map +1 -0
  139. package/dist/llm-client.d.ts +82 -0
  140. package/dist/llm-client.d.ts.map +1 -0
  141. package/dist/llm-client.js +475 -0
  142. package/dist/llm-client.js.map +1 -0
  143. package/dist/mcp-client.d.ts +232 -0
  144. package/dist/mcp-client.d.ts.map +1 -0
  145. package/dist/mcp-client.js +718 -0
  146. package/dist/mcp-client.js.map +1 -0
  147. package/dist/memory-manager.d.ts +200 -0
  148. package/dist/memory-manager.d.ts.map +1 -0
  149. package/dist/memory-manager.js +568 -0
  150. package/dist/memory-manager.js.map +1 -0
  151. package/dist/memory.d.ts +87 -0
  152. package/dist/memory.d.ts.map +1 -0
  153. package/dist/memory.js +341 -0
  154. package/dist/memory.js.map +1 -0
  155. package/dist/model-router.d.ts +245 -0
  156. package/dist/model-router.d.ts.map +1 -0
  157. package/dist/model-router.js +632 -0
  158. package/dist/model-router.js.map +1 -0
  159. package/dist/parallel-executor.d.ts +125 -0
  160. package/dist/parallel-executor.d.ts.map +1 -0
  161. package/dist/parallel-executor.js +201 -0
  162. package/dist/parallel-executor.js.map +1 -0
  163. package/dist/perf-optimizer.d.ts +212 -0
  164. package/dist/perf-optimizer.d.ts.map +1 -0
  165. package/dist/perf-optimizer.js +721 -0
  166. package/dist/perf-optimizer.js.map +1 -0
  167. package/dist/persona.d.ts +305 -0
  168. package/dist/persona.d.ts.map +1 -0
  169. package/dist/persona.js +887 -0
  170. package/dist/persona.js.map +1 -0
  171. package/dist/planner.d.ts +70 -0
  172. package/dist/planner.d.ts.map +1 -0
  173. package/dist/planner.js +264 -0
  174. package/dist/planner.js.map +1 -0
  175. package/dist/qa-pipeline.d.ts +365 -0
  176. package/dist/qa-pipeline.d.ts.map +1 -0
  177. package/dist/qa-pipeline.js +1352 -0
  178. package/dist/qa-pipeline.js.map +1 -0
  179. package/dist/reasoning-adapter.d.ts +116 -0
  180. package/dist/reasoning-adapter.d.ts.map +1 -0
  181. package/dist/reasoning-adapter.js +187 -0
  182. package/dist/reasoning-adapter.js.map +1 -0
  183. package/dist/role-registry.d.ts +55 -0
  184. package/dist/role-registry.d.ts.map +1 -0
  185. package/dist/role-registry.js +192 -0
  186. package/dist/role-registry.js.map +1 -0
  187. package/dist/sandbox-tiers.d.ts +327 -0
  188. package/dist/sandbox-tiers.d.ts.map +1 -0
  189. package/dist/sandbox-tiers.js +928 -0
  190. package/dist/sandbox-tiers.js.map +1 -0
  191. package/dist/security-scanner.d.ts +222 -0
  192. package/dist/security-scanner.d.ts.map +1 -0
  193. package/dist/security-scanner.js +1129 -0
  194. package/dist/security-scanner.js.map +1 -0
  195. package/dist/security.d.ts +93 -0
  196. package/dist/security.d.ts.map +1 -0
  197. package/dist/security.js +393 -0
  198. package/dist/security.js.map +1 -0
  199. package/dist/self-reflection.d.ts +397 -0
  200. package/dist/self-reflection.d.ts.map +1 -0
  201. package/dist/self-reflection.js +908 -0
  202. package/dist/self-reflection.js.map +1 -0
  203. package/dist/session-persistence.d.ts +191 -0
  204. package/dist/session-persistence.d.ts.map +1 -0
  205. package/dist/session-persistence.js +395 -0
  206. package/dist/session-persistence.js.map +1 -0
  207. package/dist/speculative-executor.d.ts +210 -0
  208. package/dist/speculative-executor.d.ts.map +1 -0
  209. package/dist/speculative-executor.js +618 -0
  210. package/dist/speculative-executor.js.map +1 -0
  211. package/dist/state-machine.d.ts +289 -0
  212. package/dist/state-machine.d.ts.map +1 -0
  213. package/dist/state-machine.js +695 -0
  214. package/dist/state-machine.js.map +1 -0
  215. package/dist/sub-agent.d.ts +177 -0
  216. package/dist/sub-agent.d.ts.map +1 -0
  217. package/dist/sub-agent.js +303 -0
  218. package/dist/sub-agent.js.map +1 -0
  219. package/dist/system-prompt.d.ts +26 -0
  220. package/dist/system-prompt.d.ts.map +1 -0
  221. package/dist/system-prompt.js +84 -0
  222. package/dist/system-prompt.js.map +1 -0
  223. package/dist/test-intelligence.d.ts +439 -0
  224. package/dist/test-intelligence.d.ts.map +1 -0
  225. package/dist/test-intelligence.js +1165 -0
  226. package/dist/test-intelligence.js.map +1 -0
  227. package/dist/types.d.ts +632 -0
  228. package/dist/types.d.ts.map +1 -0
  229. package/dist/types.js +6 -0
  230. package/dist/types.js.map +1 -0
  231. package/dist/vector-index.d.ts +314 -0
  232. package/dist/vector-index.d.ts.map +1 -0
  233. package/dist/vector-index.js +618 -0
  234. package/dist/vector-index.js.map +1 -0
  235. package/package.json +41 -0
@@ -0,0 +1,719 @@
1
+ /**
2
+ * @module debate-orchestrator
3
+ * @description Multi-Agent Debate system for YUAN.
4
+ *
5
+ * Implements a Coder -> Reviewer -> Coder -> Verifier loop:
6
+ * 1. Coder Agent generates code to solve the task
7
+ * 2. Reviewer Agent reviews the code, finds issues
8
+ * 3. Coder Agent addresses the review feedback
9
+ * 4. Verifier Agent runs tests/builds and makes final judgment
10
+ *
11
+ * This significantly reduces hallucination and improves code quality
12
+ * by having adversarial agents check each other's work.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const orchestrator = DebateOrchestrator.create({
17
+ * projectPath: "/home/user/project",
18
+ * maxRounds: 3,
19
+ * qualityThreshold: 80,
20
+ * byokConfig: { provider: "openai", apiKey: "sk-..." },
21
+ * });
22
+ *
23
+ * orchestrator.on("debate:round:start", ({ round }) => {
24
+ * console.log(`Round ${round} starting...`);
25
+ * });
26
+ *
27
+ * const result = await orchestrator.debate(
28
+ * "Implement a rate limiter middleware",
29
+ * "Express.js project with TypeScript",
30
+ * );
31
+ *
32
+ * if (result.success) {
33
+ * console.log(`Passed with score ${result.finalScore}`);
34
+ * }
35
+ * ```
36
+ */
37
+ import { EventEmitter } from "node:events";
38
+ import { BYOKClient } from "./llm-client.js";
39
+ // ─── Constants ───────────────────────────────────────────────────
40
+ const DEFAULT_CONFIG = {
41
+ maxRounds: 3,
42
+ qualityThreshold: 80,
43
+ verifyBetweenRounds: true,
44
+ maxTokensPerCall: 16384,
45
+ totalTokenBudget: 200_000,
46
+ };
47
+ const CODER_SYSTEM_PROMPT = `You are an expert software engineer working on a coding task.
48
+
49
+ Your responsibilities:
50
+ - Write clean, correct, complete code
51
+ - Follow best practices and established patterns
52
+ - Consider edge cases and error handling
53
+ - Write tests when appropriate
54
+ - Use proper types and documentation
55
+
56
+ When generating code, output the COMPLETE code (not just snippets).
57
+ Structure your response as:
58
+
59
+ ## Plan
60
+ Brief plan of what you will implement.
61
+
62
+ ## Code
63
+ \`\`\`[language]
64
+ [complete code]
65
+ \`\`\`
66
+
67
+ ## Files Changed
68
+ List each file you created or modified.
69
+
70
+ ## Notes
71
+ Any important notes about your implementation.`;
72
+ const CODER_REVISION_SYSTEM_PROMPT = `You are an expert software engineer addressing code review feedback.
73
+
74
+ Your responsibilities:
75
+ - Carefully read each review issue
76
+ - Fix ALL critical and major issues
77
+ - Address minor issues where reasonable
78
+ - Explain what you changed and why
79
+ - Ensure the fixes don't introduce new problems
80
+
81
+ Structure your response as:
82
+
83
+ ## Issues Addressed
84
+ For each issue, explain what you changed.
85
+
86
+ ## Updated Code
87
+ \`\`\`[language]
88
+ [complete updated code]
89
+ \`\`\`
90
+
91
+ ## Files Changed
92
+ List each file you modified.
93
+
94
+ ## Remaining Issues
95
+ Any issues you intentionally did not fix, with reasoning.`;
96
+ const REVIEWER_SYSTEM_PROMPT = `You are a senior code reviewer conducting a thorough review.
97
+
98
+ Your responsibilities:
99
+ - Find ALL issues including: bugs, security vulnerabilities, performance problems, missing edge cases, style violations, incomplete implementations
100
+ - Be specific and constructive — point to exact locations
101
+ - Rate each issue: critical, major, minor, or suggestion
102
+ - Consider the project context and coding conventions
103
+
104
+ You MUST respond with ONLY a JSON object (no markdown fencing, no extra text):
105
+
106
+ {
107
+ "feedback": "Overall assessment text here",
108
+ "issues": [
109
+ {
110
+ "severity": "critical|major|minor|suggestion",
111
+ "file": "path/to/file.ts",
112
+ "line": 42,
113
+ "description": "What is wrong",
114
+ "suggestion": "How to fix it"
115
+ }
116
+ ]
117
+ }
118
+
119
+ Severity guidelines:
120
+ - **critical**: Will cause crashes, data loss, or security breaches. Must fix.
121
+ - **major**: Significant bugs, performance issues, or missing functionality. Should fix.
122
+ - **minor**: Code style, naming, minor inefficiencies. Nice to fix.
123
+ - **suggestion**: Improvements that could make the code better but aren't necessary.
124
+
125
+ Be thorough but fair. Do NOT inflate severity — only use "critical" for genuinely dangerous issues.`;
126
+ const VERIFIER_SYSTEM_PROMPT = `You are a quality assurance engineer evaluating code.
127
+
128
+ Evaluate the code against these criteria and provide a score for each:
129
+ 1. **Correctness** (0-100): Does the code work? Are there logic errors?
130
+ 2. **Completeness** (0-100): Does it handle all cases? Missing error handling?
131
+ 3. **Security** (0-100): Any vulnerabilities? Hardcoded secrets? Injection risks?
132
+ 4. **Performance** (0-100): Any bottlenecks? Unnecessary complexity?
133
+ 5. **Maintainability** (0-100): Is it clean, readable, well-documented?
134
+
135
+ You MUST respond with ONLY a JSON object (no markdown fencing, no extra text):
136
+
137
+ {
138
+ "passed": true|false,
139
+ "score": 85,
140
+ "buildPassed": true|false,
141
+ "testsPassed": true|false,
142
+ "securityPassed": true|false,
143
+ "details": "Explanation of the assessment",
144
+ "breakdown": {
145
+ "correctness": 90,
146
+ "completeness": 80,
147
+ "security": 95,
148
+ "performance": 75,
149
+ "maintainability": 85
150
+ }
151
+ }
152
+
153
+ The overall "score" should be the weighted average:
154
+ - Correctness: 30%
155
+ - Completeness: 20%
156
+ - Security: 25%
157
+ - Performance: 10%
158
+ - Maintainability: 15%
159
+
160
+ Set "passed" to true only if score >= the quality threshold AND no critical security issues.`;
161
+ // ─── DebateOrchestrator ──────────────────────────────────────────
162
+ /**
163
+ * DebateOrchestrator — Multi-agent debate loop for code quality improvement.
164
+ *
165
+ * Creates adversarial Coder/Reviewer/Verifier agents that check each other's work,
166
+ * significantly reducing hallucination and improving code quality.
167
+ *
168
+ * Uses EventEmitter for observability so callers can track progress in real-time.
169
+ */
170
+ export class DebateOrchestrator extends EventEmitter {
171
+ config;
172
+ rounds = [];
173
+ totalTokens = 0;
174
+ roleTokens = {
175
+ coder: { input: 0, output: 0 },
176
+ reviewer: { input: 0, output: 0 },
177
+ verifier: { input: 0, output: 0 },
178
+ };
179
+ changedFiles = new Set();
180
+ constructor(config) {
181
+ super();
182
+ this.config = {
183
+ ...DEFAULT_CONFIG,
184
+ ...config,
185
+ };
186
+ }
187
+ /**
188
+ * Run a debate session for a given task.
189
+ *
190
+ * The debate proceeds through rounds of Coder -> Reviewer -> Revision -> Verifier
191
+ * until the quality threshold is met or max rounds are exhausted.
192
+ *
193
+ * @param task - The coding task description
194
+ * @param context - Additional context (file contents, plan, etc.)
195
+ * @returns DebateResult with all rounds and final score
196
+ */
197
+ async debate(task, context) {
198
+ this.rounds = [];
199
+ this.totalTokens = 0;
200
+ this.changedFiles.clear();
201
+ this.resetRoleTokens();
202
+ this.emit("debate:start", { task, maxRounds: this.config.maxRounds });
203
+ for (let round = 1; round <= this.config.maxRounds; round++) {
204
+ // Check abort signal
205
+ if (this.config.abortSignal?.aborted) {
206
+ this.emit("debate:abort", { reason: "AbortSignal triggered" });
207
+ break;
208
+ }
209
+ // Check token budget
210
+ if (this.totalTokens >= this.config.totalTokenBudget) {
211
+ this.emit("debate:abort", { reason: "Token budget exhausted" });
212
+ break;
213
+ }
214
+ this.emit("debate:round:start", { round });
215
+ // Step 1: Coder generates/revises code
216
+ const coderOutput = await this.runCoder(task, context, round);
217
+ if (this.config.abortSignal?.aborted)
218
+ break;
219
+ this.emit("debate:coder", { round, output: this.truncate(coderOutput, 500) });
220
+ // Step 2: Reviewer critiques the code
221
+ const review = await this.runReviewer(coderOutput, task, round);
222
+ if (this.config.abortSignal?.aborted)
223
+ break;
224
+ const criticalOrMajor = review.issues.filter((i) => i.severity === "critical" || i.severity === "major");
225
+ this.emit("debate:reviewer", {
226
+ round,
227
+ issueCount: review.issues.length,
228
+ hasCritical: review.issues.some((i) => i.severity === "critical"),
229
+ });
230
+ // Step 3: If no critical/major issues, try verification early
231
+ if (criticalOrMajor.length === 0 && this.config.verifyBetweenRounds) {
232
+ const verification = await this.runVerifier(coderOutput, task);
233
+ this.emit("debate:verifier", {
234
+ round,
235
+ score: verification.score,
236
+ passed: verification.passed,
237
+ });
238
+ const debateRound = {
239
+ round,
240
+ coderOutput,
241
+ reviewerFeedback: review.feedback,
242
+ issues: review.issues,
243
+ verifierResult: verification,
244
+ };
245
+ this.rounds.push(debateRound);
246
+ if (verification.passed && verification.score >= this.config.qualityThreshold) {
247
+ this.emit("debate:pass", { round, score: verification.score });
248
+ return this.buildResult(verification);
249
+ }
250
+ }
251
+ // Step 4: Coder addresses review feedback (revision)
252
+ const revision = await this.runCoderRevision(coderOutput, review, task, round);
253
+ if (this.config.abortSignal?.aborted)
254
+ break;
255
+ this.emit("debate:revision", { round, output: this.truncate(revision, 500) });
256
+ // If we didn't push a round with verification yet (had critical issues), push now
257
+ if (criticalOrMajor.length > 0 || !this.config.verifyBetweenRounds) {
258
+ // Optionally verify the revision
259
+ let verifierResult;
260
+ if (this.config.verifyBetweenRounds) {
261
+ verifierResult = await this.runVerifier(revision, task);
262
+ this.emit("debate:verifier", {
263
+ round,
264
+ score: verifierResult.score,
265
+ passed: verifierResult.passed,
266
+ });
267
+ }
268
+ const debateRound = {
269
+ round,
270
+ coderOutput,
271
+ reviewerFeedback: review.feedback,
272
+ issues: review.issues,
273
+ coderRevision: revision,
274
+ verifierResult,
275
+ };
276
+ this.rounds.push(debateRound);
277
+ // Check if revision passes
278
+ if (verifierResult?.passed && verifierResult.score >= this.config.qualityThreshold) {
279
+ this.emit("debate:pass", { round, score: verifierResult.score });
280
+ return this.buildResult(verifierResult);
281
+ }
282
+ }
283
+ else {
284
+ // Update the already-pushed round with the revision
285
+ const existingRound = this.rounds[this.rounds.length - 1];
286
+ if (existingRound) {
287
+ existingRound.coderRevision = revision;
288
+ }
289
+ }
290
+ // Update context for next round with the revision and feedback
291
+ context = this.buildNextRoundContext(context, this.rounds[this.rounds.length - 1]);
292
+ this.emit("debate:round:end", {
293
+ round,
294
+ issueCount: review.issues.length,
295
+ });
296
+ }
297
+ // Final verification on the last output
298
+ const lastRound = this.rounds[this.rounds.length - 1];
299
+ const lastOutput = lastRound?.coderRevision ?? lastRound?.coderOutput ?? "";
300
+ let finalVerification;
301
+ if (lastRound?.verifierResult) {
302
+ finalVerification = lastRound.verifierResult;
303
+ }
304
+ else {
305
+ finalVerification = await this.runVerifier(lastOutput, task);
306
+ }
307
+ const result = this.buildResult(finalVerification);
308
+ if (!result.success) {
309
+ this.emit("debate:fail", {
310
+ finalScore: result.finalScore,
311
+ reason: result.summary,
312
+ });
313
+ }
314
+ return result;
315
+ }
316
+ // ─── Role Implementations ─────────────────────────────────────
317
+ /**
318
+ * Coder Agent: Generates code to solve the task.
319
+ * Uses system prompt that emphasizes correctness, completeness, and clean code.
320
+ */
321
+ async runCoder(task, context, round) {
322
+ const sections = [];
323
+ sections.push(`## Task\n${task}`);
324
+ if (context) {
325
+ sections.push(`## Context\n${context}`);
326
+ }
327
+ sections.push(`## Project Path\n${this.config.projectPath}`);
328
+ if (round > 1) {
329
+ sections.push(`## Note\nThis is round ${round} of the debate. Previous rounds had issues that need to be addressed. See the context above for details.`);
330
+ }
331
+ const userMessage = sections.join("\n\n");
332
+ return this.callLLM(CODER_SYSTEM_PROMPT, userMessage, "coder", this.config.coderModel);
333
+ }
334
+ /**
335
+ * Reviewer Agent: Critically reviews the coder's output.
336
+ * Uses system prompt that emphasizes finding bugs, security issues, edge cases.
337
+ * Returns structured feedback with severity levels.
338
+ */
339
+ async runReviewer(coderOutput, task, round) {
340
+ const sections = [];
341
+ sections.push(`## Original Task\n${task}`);
342
+ sections.push(`## Code to Review (Round ${round})\n${coderOutput}`);
343
+ sections.push(`## Project Path\n${this.config.projectPath}`);
344
+ const userMessage = sections.join("\n\n");
345
+ const response = await this.callLLM(REVIEWER_SYSTEM_PROMPT, userMessage, "reviewer", this.config.reviewerModel);
346
+ return this.parseReviewerResponse(response);
347
+ }
348
+ /**
349
+ * Coder Agent (revision): Addresses reviewer feedback.
350
+ * Given the original code and reviewer issues, produces improved code.
351
+ */
352
+ async runCoderRevision(originalCode, review, task, round) {
353
+ const sections = [];
354
+ sections.push(`## Original Task\n${task}`);
355
+ sections.push(`## Your Previous Code (Round ${round})\n${originalCode}`);
356
+ sections.push(`## Reviewer Feedback\n${review.feedback}`);
357
+ if (review.issues.length > 0) {
358
+ const issuesList = review.issues
359
+ .map((issue, idx) => {
360
+ const parts = [`${idx + 1}. [${issue.severity.toUpperCase()}] ${issue.description}`];
361
+ if (issue.file)
362
+ parts.push(` File: ${issue.file}${issue.line ? `:${issue.line}` : ""}`);
363
+ if (issue.suggestion)
364
+ parts.push(` Suggestion: ${issue.suggestion}`);
365
+ return parts.join("\n");
366
+ })
367
+ .join("\n\n");
368
+ sections.push(`## Issues to Fix\n${issuesList}`);
369
+ }
370
+ sections.push(`## Project Path\n${this.config.projectPath}`);
371
+ const userMessage = sections.join("\n\n");
372
+ return this.callLLM(CODER_REVISION_SYSTEM_PROMPT, userMessage, "coder", this.config.coderModel);
373
+ }
374
+ /**
375
+ * Verifier Agent: Runs objective checks (build, test, security scan)
376
+ * and provides a holistic quality assessment via LLM.
377
+ */
378
+ async runVerifier(code, task) {
379
+ // Step 1: Run build/test checks if tool executor is available
380
+ let buildPassed = true;
381
+ let testsPassed = true;
382
+ let buildOutput = "";
383
+ let testOutput = "";
384
+ if (this.config.toolExecutor) {
385
+ const buildResult = await this.runToolSafe("shell_exec", {
386
+ command: "pnpm run build --dry-run 2>&1 || echo 'BUILD_CHECK_SKIPPED'",
387
+ cwd: this.config.projectPath,
388
+ });
389
+ buildPassed = buildResult.success;
390
+ buildOutput = buildResult.output;
391
+ const testResult = await this.runToolSafe("shell_exec", {
392
+ command: "pnpm run test --passWithNoTests 2>&1 || echo 'TEST_CHECK_SKIPPED'",
393
+ cwd: this.config.projectPath,
394
+ });
395
+ testsPassed = testResult.success;
396
+ testOutput = testResult.output;
397
+ }
398
+ // Step 2: LLM quality assessment
399
+ const sections = [];
400
+ sections.push(`## Original Task\n${task}`);
401
+ sections.push(`## Code to Verify\n${code}`);
402
+ sections.push(`## Quality Threshold\n${this.config.qualityThreshold}/100`);
403
+ if (buildOutput) {
404
+ sections.push(`## Build Result\n${buildPassed ? "PASSED" : "FAILED"}\n\`\`\`\n${this.truncate(buildOutput, 2000)}\n\`\`\``);
405
+ }
406
+ if (testOutput) {
407
+ sections.push(`## Test Result\n${testsPassed ? "PASSED" : "FAILED"}\n\`\`\`\n${this.truncate(testOutput, 2000)}\n\`\`\``);
408
+ }
409
+ const userMessage = sections.join("\n\n");
410
+ const response = await this.callLLM(VERIFIER_SYSTEM_PROMPT, userMessage, "verifier", this.config.verifierModel);
411
+ const parsed = this.parseVerifierResponse(response);
412
+ // Override with actual build/test results if we ran them
413
+ if (this.config.toolExecutor) {
414
+ parsed.buildPassed = buildPassed;
415
+ parsed.testsPassed = testsPassed;
416
+ // If build or tests failed, cap the score and mark as not passed
417
+ if (!buildPassed || !testsPassed) {
418
+ parsed.score = Math.min(parsed.score, 50);
419
+ parsed.passed = false;
420
+ }
421
+ }
422
+ return parsed;
423
+ }
424
+ // ─── LLM Calling ──────────────────────────────────────────────
425
+ /**
426
+ * Call the LLM with a system prompt and user message.
427
+ * Tracks token usage per role and total.
428
+ */
429
+ async callLLM(systemPrompt, userMessage, role, model) {
430
+ if (!this.config.byokConfig) {
431
+ // No BYOK config — return a placeholder indicating LLM is not configured
432
+ return `[LLM not configured — ${role} agent would process: ${this.truncate(userMessage, 200)}]`;
433
+ }
434
+ const byokConfig = {
435
+ ...this.config.byokConfig,
436
+ };
437
+ if (model) {
438
+ byokConfig.model = model;
439
+ }
440
+ const client = new BYOKClient(byokConfig);
441
+ try {
442
+ const response = await client.chat([
443
+ { role: "system", content: systemPrompt },
444
+ { role: "user", content: userMessage },
445
+ ]);
446
+ // Track token usage
447
+ const usage = response.usage;
448
+ this.roleTokens[role].input += usage.input;
449
+ this.roleTokens[role].output += usage.output;
450
+ this.totalTokens += usage.input + usage.output;
451
+ this.emit("debate:token_usage", {
452
+ role,
453
+ input: usage.input,
454
+ output: usage.output,
455
+ });
456
+ return response.content ?? "";
457
+ }
458
+ catch (error) {
459
+ const message = error instanceof Error ? error.message : String(error);
460
+ throw new Error(`[DebateOrchestrator] ${role} LLM call failed: ${message}`);
461
+ }
462
+ }
463
+ // ─── Tool Execution ───────────────────────────────────────────
464
+ /**
465
+ * Safely execute a tool via the tool executor.
466
+ * Returns a success/failure result, never throws.
467
+ */
468
+ async runToolSafe(toolName, args) {
469
+ if (!this.config.toolExecutor) {
470
+ return { success: true, output: "[No tool executor — skipped]" };
471
+ }
472
+ try {
473
+ const call = {
474
+ id: `debate-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
475
+ name: toolName,
476
+ arguments: args,
477
+ };
478
+ const result = await this.config.toolExecutor.execute(call, this.config.abortSignal);
479
+ return { success: result.success, output: result.output };
480
+ }
481
+ catch (error) {
482
+ const message = error instanceof Error ? error.message : String(error);
483
+ return { success: false, output: `Tool error: ${message}` };
484
+ }
485
+ }
486
+ // ─── Response Parsers ─────────────────────────────────────────
487
+ /**
488
+ * Parse the reviewer's JSON response into structured feedback + issues.
489
+ * Handles malformed JSON gracefully.
490
+ */
491
+ parseReviewerResponse(response) {
492
+ try {
493
+ const jsonStr = this.extractJson(response);
494
+ const parsed = JSON.parse(jsonStr);
495
+ const feedback = typeof parsed.feedback === "string"
496
+ ? parsed.feedback
497
+ : "No feedback provided";
498
+ const rawIssues = Array.isArray(parsed.issues) ? parsed.issues : [];
499
+ const issues = this.parseIssues(rawIssues);
500
+ return { feedback, issues };
501
+ }
502
+ catch {
503
+ // If JSON parsing fails, treat entire response as feedback with no structured issues
504
+ return {
505
+ feedback: response,
506
+ issues: [],
507
+ };
508
+ }
509
+ }
510
+ /**
511
+ * Parse raw issue objects into typed ReviewIssue[].
512
+ */
513
+ parseIssues(raw) {
514
+ const validSeverities = new Set(["critical", "major", "minor", "suggestion"]);
515
+ return raw
516
+ .filter((item) => typeof item === "object" && item !== null)
517
+ .map((item) => {
518
+ const issue = {
519
+ severity: validSeverities.has(item.severity)
520
+ ? item.severity
521
+ : "minor",
522
+ description: typeof item.description === "string"
523
+ ? item.description
524
+ : "No description",
525
+ };
526
+ if (typeof item.file === "string") {
527
+ issue.file = item.file;
528
+ // Track changed files mentioned in reviews
529
+ this.changedFiles.add(item.file);
530
+ }
531
+ if (typeof item.line === "number") {
532
+ issue.line = item.line;
533
+ }
534
+ if (typeof item.suggestion === "string") {
535
+ issue.suggestion = item.suggestion;
536
+ }
537
+ return issue;
538
+ });
539
+ }
540
+ /**
541
+ * Parse the verifier's JSON response into a VerifierResult.
542
+ * Handles malformed JSON gracefully.
543
+ */
544
+ parseVerifierResponse(response) {
545
+ try {
546
+ const jsonStr = this.extractJson(response);
547
+ const parsed = JSON.parse(jsonStr);
548
+ const score = typeof parsed.score === "number"
549
+ ? Math.max(0, Math.min(100, Math.round(parsed.score)))
550
+ : 50;
551
+ return {
552
+ passed: typeof parsed.passed === "boolean" ? parsed.passed : score >= this.config.qualityThreshold,
553
+ score,
554
+ buildPassed: typeof parsed.buildPassed === "boolean" ? parsed.buildPassed : true,
555
+ testsPassed: typeof parsed.testsPassed === "boolean" ? parsed.testsPassed : true,
556
+ securityPassed: typeof parsed.securityPassed === "boolean" ? parsed.securityPassed : true,
557
+ details: typeof parsed.details === "string" ? parsed.details : "No details provided",
558
+ };
559
+ }
560
+ catch {
561
+ // If JSON parsing fails, return a conservative default
562
+ return {
563
+ passed: false,
564
+ score: 50,
565
+ buildPassed: true,
566
+ testsPassed: true,
567
+ securityPassed: true,
568
+ details: `Verifier response could not be parsed: ${this.truncate(response, 200)}`,
569
+ };
570
+ }
571
+ }
572
+ // ─── Helpers ──────────────────────────────────────────────────
573
+ /**
574
+ * Extract JSON from a response that might contain markdown fencing or extra text.
575
+ */
576
+ extractJson(text) {
577
+ // Try ```json ... ``` pattern first
578
+ const fenced = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
579
+ if (fenced)
580
+ return fenced[1].trim();
581
+ // Try to find raw JSON object
582
+ const firstBrace = text.indexOf("{");
583
+ const lastBrace = text.lastIndexOf("}");
584
+ if (firstBrace !== -1 && lastBrace > firstBrace) {
585
+ return text.slice(firstBrace, lastBrace + 1);
586
+ }
587
+ return text.trim();
588
+ }
589
+ /**
590
+ * Build context for the next round, incorporating previous round's results.
591
+ */
592
+ buildNextRoundContext(previousContext, round) {
593
+ const sections = [];
594
+ if (previousContext) {
595
+ // Keep only essential previous context to stay within token limits
596
+ sections.push(`## Previous Context\n${this.truncate(previousContext, 2000)}`);
597
+ }
598
+ sections.push(`## Round ${round.round} Summary`);
599
+ sections.push(`### Reviewer Found ${round.issues.length} Issue(s)`);
600
+ if (round.issues.length > 0) {
601
+ const criticalCount = round.issues.filter((i) => i.severity === "critical").length;
602
+ const majorCount = round.issues.filter((i) => i.severity === "major").length;
603
+ const minorCount = round.issues.filter((i) => i.severity === "minor").length;
604
+ sections.push(`- Critical: ${criticalCount}, Major: ${majorCount}, Minor: ${minorCount}`);
605
+ // Include critical and major issues in detail
606
+ const important = round.issues.filter((i) => i.severity === "critical" || i.severity === "major");
607
+ if (important.length > 0) {
608
+ sections.push("\n### Unresolved Important Issues:");
609
+ for (const issue of important) {
610
+ sections.push(`- [${issue.severity.toUpperCase()}] ${issue.description}`);
611
+ if (issue.suggestion) {
612
+ sections.push(` Fix: ${issue.suggestion}`);
613
+ }
614
+ }
615
+ }
616
+ }
617
+ if (round.verifierResult) {
618
+ sections.push(`\n### Verifier Score: ${round.verifierResult.score}/100 (${round.verifierResult.passed ? "PASSED" : "FAILED"})`);
619
+ sections.push(`Details: ${round.verifierResult.details}`);
620
+ }
621
+ if (round.coderRevision) {
622
+ sections.push(`\n### Last Revision\n${this.truncate(round.coderRevision, 4000)}`);
623
+ }
624
+ else {
625
+ sections.push(`\n### Last Code Output\n${this.truncate(round.coderOutput, 4000)}`);
626
+ }
627
+ return sections.join("\n");
628
+ }
629
+ /**
630
+ * Build the final DebateResult from all rounds and the final verification.
631
+ */
632
+ buildResult(finalVerification) {
633
+ const roundCount = this.rounds.length;
634
+ const lastRound = this.rounds[roundCount - 1];
635
+ // Extract changed files from code output (look for file paths)
636
+ if (lastRound) {
637
+ const output = lastRound.coderRevision ?? lastRound.coderOutput;
638
+ this.extractFilePaths(output);
639
+ }
640
+ const success = finalVerification.passed &&
641
+ finalVerification.score >= this.config.qualityThreshold;
642
+ let summary;
643
+ if (success) {
644
+ summary = `Debate completed successfully after ${roundCount} round(s) with a quality score of ${finalVerification.score}/100.`;
645
+ }
646
+ else if (this.config.abortSignal?.aborted) {
647
+ summary = `Debate aborted after ${roundCount} round(s). Last score: ${finalVerification.score}/100.`;
648
+ }
649
+ else if (this.totalTokens >= this.config.totalTokenBudget) {
650
+ summary = `Debate stopped: token budget exhausted after ${roundCount} round(s). Last score: ${finalVerification.score}/100.`;
651
+ }
652
+ else {
653
+ const totalIssues = this.rounds.reduce((sum, r) => sum + r.issues.length, 0);
654
+ summary = `Debate completed ${roundCount} round(s) but did not meet quality threshold (${this.config.qualityThreshold}). Final score: ${finalVerification.score}/100. Total issues found: ${totalIssues}.`;
655
+ }
656
+ return {
657
+ success,
658
+ rounds: this.rounds,
659
+ finalScore: finalVerification.score,
660
+ totalTokensUsed: this.totalTokens,
661
+ changedFiles: [...this.changedFiles],
662
+ summary,
663
+ };
664
+ }
665
+ /**
666
+ * Extract file paths mentioned in code output (e.g., "src/foo.ts").
667
+ */
668
+ extractFilePaths(text) {
669
+ // Match common file path patterns
670
+ const pathPattern = /(?:^|\s|`)((?:[\w.-]+\/)+[\w.-]+\.\w+)/gm;
671
+ let match;
672
+ while ((match = pathPattern.exec(text)) !== null) {
673
+ const filePath = match[1];
674
+ if (filePath && !filePath.startsWith("http") && !filePath.startsWith("//")) {
675
+ this.changedFiles.add(filePath);
676
+ }
677
+ }
678
+ }
679
+ /** Reset per-role token usage counters */
680
+ resetRoleTokens() {
681
+ this.roleTokens.coder = { input: 0, output: 0 };
682
+ this.roleTokens.reviewer = { input: 0, output: 0 };
683
+ this.roleTokens.verifier = { input: 0, output: 0 };
684
+ }
685
+ /** Truncate text to a maximum length */
686
+ truncate(text, maxLength) {
687
+ if (text.length <= maxLength)
688
+ return text;
689
+ return text.slice(0, maxLength) + "...";
690
+ }
691
+ // ─── Public Accessors ─────────────────────────────────────────
692
+ /** Get the current total token usage */
693
+ getTotalTokensUsed() {
694
+ return this.totalTokens;
695
+ }
696
+ /** Get token usage broken down by role */
697
+ getRoleTokenUsage() {
698
+ return { ...this.roleTokens };
699
+ }
700
+ /** Get all rounds from the current/last debate */
701
+ getRounds() {
702
+ return this.rounds;
703
+ }
704
+ /** Get the current config */
705
+ getConfig() {
706
+ return { ...this.config };
707
+ }
708
+ // ─── Static Factory ───────────────────────────────────────────
709
+ /**
710
+ * Create a new DebateOrchestrator with the given config.
711
+ *
712
+ * @param config - Debate configuration (projectPath is required, rest have defaults)
713
+ * @returns A new DebateOrchestrator instance
714
+ */
715
+ static create(config) {
716
+ return new DebateOrchestrator(config);
717
+ }
718
+ }
719
+ //# sourceMappingURL=debate-orchestrator.js.map