@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.
- package/LICENSE +663 -0
- package/README.md +15 -0
- package/dist/__tests__/context-manager.test.d.ts +6 -0
- package/dist/__tests__/context-manager.test.d.ts.map +1 -0
- package/dist/__tests__/context-manager.test.js +220 -0
- package/dist/__tests__/context-manager.test.js.map +1 -0
- package/dist/__tests__/governor.test.d.ts +6 -0
- package/dist/__tests__/governor.test.d.ts.map +1 -0
- package/dist/__tests__/governor.test.js +210 -0
- package/dist/__tests__/governor.test.js.map +1 -0
- package/dist/__tests__/model-router.test.d.ts +6 -0
- package/dist/__tests__/model-router.test.d.ts.map +1 -0
- package/dist/__tests__/model-router.test.js +329 -0
- package/dist/__tests__/model-router.test.js.map +1 -0
- package/dist/agent-logger.d.ts +384 -0
- package/dist/agent-logger.d.ts.map +1 -0
- package/dist/agent-logger.js +820 -0
- package/dist/agent-logger.js.map +1 -0
- package/dist/agent-loop.d.ts +163 -0
- package/dist/agent-loop.d.ts.map +1 -0
- package/dist/agent-loop.js +609 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/agent-modes.d.ts +85 -0
- package/dist/agent-modes.d.ts.map +1 -0
- package/dist/agent-modes.js +418 -0
- package/dist/agent-modes.js.map +1 -0
- package/dist/approval.d.ts +137 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +299 -0
- package/dist/approval.js.map +1 -0
- package/dist/async-completion-queue.d.ts +56 -0
- package/dist/async-completion-queue.d.ts.map +1 -0
- package/dist/async-completion-queue.js +77 -0
- package/dist/async-completion-queue.js.map +1 -0
- package/dist/auto-fix.d.ts +174 -0
- package/dist/auto-fix.d.ts.map +1 -0
- package/dist/auto-fix.js +319 -0
- package/dist/auto-fix.js.map +1 -0
- package/dist/codebase-context.d.ts +396 -0
- package/dist/codebase-context.d.ts.map +1 -0
- package/dist/codebase-context.js +1260 -0
- package/dist/codebase-context.js.map +1 -0
- package/dist/conflict-resolver.d.ts +191 -0
- package/dist/conflict-resolver.d.ts.map +1 -0
- package/dist/conflict-resolver.js +524 -0
- package/dist/conflict-resolver.js.map +1 -0
- package/dist/constants.d.ts +52 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +141 -0
- package/dist/constants.js.map +1 -0
- package/dist/context-budget.d.ts +435 -0
- package/dist/context-budget.d.ts.map +1 -0
- package/dist/context-budget.js +903 -0
- package/dist/context-budget.js.map +1 -0
- package/dist/context-compressor.d.ts +143 -0
- package/dist/context-compressor.d.ts.map +1 -0
- package/dist/context-compressor.js +511 -0
- package/dist/context-compressor.js.map +1 -0
- package/dist/context-manager.d.ts +112 -0
- package/dist/context-manager.d.ts.map +1 -0
- package/dist/context-manager.js +247 -0
- package/dist/context-manager.js.map +1 -0
- package/dist/continuous-reflection.d.ts +267 -0
- package/dist/continuous-reflection.d.ts.map +1 -0
- package/dist/continuous-reflection.js +338 -0
- package/dist/continuous-reflection.js.map +1 -0
- package/dist/cross-file-refactor.d.ts +352 -0
- package/dist/cross-file-refactor.d.ts.map +1 -0
- package/dist/cross-file-refactor.js +1544 -0
- package/dist/cross-file-refactor.js.map +1 -0
- package/dist/dag-orchestrator.d.ts +138 -0
- package/dist/dag-orchestrator.d.ts.map +1 -0
- package/dist/dag-orchestrator.js +379 -0
- package/dist/dag-orchestrator.js.map +1 -0
- package/dist/debate-orchestrator.d.ts +301 -0
- package/dist/debate-orchestrator.d.ts.map +1 -0
- package/dist/debate-orchestrator.js +719 -0
- package/dist/debate-orchestrator.js.map +1 -0
- package/dist/dependency-analyzer.d.ts +113 -0
- package/dist/dependency-analyzer.d.ts.map +1 -0
- package/dist/dependency-analyzer.js +444 -0
- package/dist/dependency-analyzer.js.map +1 -0
- package/dist/design-loop.d.ts +59 -0
- package/dist/design-loop.d.ts.map +1 -0
- package/dist/design-loop.js +344 -0
- package/dist/design-loop.js.map +1 -0
- package/dist/doc-intelligence.d.ts +383 -0
- package/dist/doc-intelligence.d.ts.map +1 -0
- package/dist/doc-intelligence.js +1307 -0
- package/dist/doc-intelligence.js.map +1 -0
- package/dist/dynamic-role-generator.d.ts +76 -0
- package/dist/dynamic-role-generator.d.ts.map +1 -0
- package/dist/dynamic-role-generator.js +194 -0
- package/dist/dynamic-role-generator.js.map +1 -0
- package/dist/errors.d.ts +69 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +102 -0
- package/dist/errors.js.map +1 -0
- package/dist/event-bus.d.ts +159 -0
- package/dist/event-bus.d.ts.map +1 -0
- package/dist/event-bus.js +305 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/execution-engine.d.ts +425 -0
- package/dist/execution-engine.d.ts.map +1 -0
- package/dist/execution-engine.js +1555 -0
- package/dist/execution-engine.js.map +1 -0
- package/dist/git-intelligence.d.ts +306 -0
- package/dist/git-intelligence.d.ts.map +1 -0
- package/dist/git-intelligence.js +1099 -0
- package/dist/git-intelligence.js.map +1 -0
- package/dist/governor.d.ts +77 -0
- package/dist/governor.d.ts.map +1 -0
- package/dist/governor.js +161 -0
- package/dist/governor.js.map +1 -0
- package/dist/hierarchical-planner.d.ts +313 -0
- package/dist/hierarchical-planner.d.ts.map +1 -0
- package/dist/hierarchical-planner.js +981 -0
- package/dist/hierarchical-planner.js.map +1 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +123 -0
- package/dist/index.js.map +1 -0
- package/dist/intent-inference.d.ts +103 -0
- package/dist/intent-inference.d.ts.map +1 -0
- package/dist/intent-inference.js +605 -0
- package/dist/intent-inference.js.map +1 -0
- package/dist/interrupt-manager.d.ts +143 -0
- package/dist/interrupt-manager.d.ts.map +1 -0
- package/dist/interrupt-manager.js +196 -0
- package/dist/interrupt-manager.js.map +1 -0
- package/dist/kernel.d.ts +564 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +1419 -0
- package/dist/kernel.js.map +1 -0
- package/dist/language-support.d.ts +232 -0
- package/dist/language-support.d.ts.map +1 -0
- package/dist/language-support.js +1134 -0
- package/dist/language-support.js.map +1 -0
- package/dist/llm-client.d.ts +82 -0
- package/dist/llm-client.d.ts.map +1 -0
- package/dist/llm-client.js +475 -0
- package/dist/llm-client.js.map +1 -0
- package/dist/mcp-client.d.ts +232 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +718 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/memory-manager.d.ts +200 -0
- package/dist/memory-manager.d.ts.map +1 -0
- package/dist/memory-manager.js +568 -0
- package/dist/memory-manager.js.map +1 -0
- package/dist/memory.d.ts +87 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +341 -0
- package/dist/memory.js.map +1 -0
- package/dist/model-router.d.ts +245 -0
- package/dist/model-router.d.ts.map +1 -0
- package/dist/model-router.js +632 -0
- package/dist/model-router.js.map +1 -0
- package/dist/parallel-executor.d.ts +125 -0
- package/dist/parallel-executor.d.ts.map +1 -0
- package/dist/parallel-executor.js +201 -0
- package/dist/parallel-executor.js.map +1 -0
- package/dist/perf-optimizer.d.ts +212 -0
- package/dist/perf-optimizer.d.ts.map +1 -0
- package/dist/perf-optimizer.js +721 -0
- package/dist/perf-optimizer.js.map +1 -0
- package/dist/persona.d.ts +305 -0
- package/dist/persona.d.ts.map +1 -0
- package/dist/persona.js +887 -0
- package/dist/persona.js.map +1 -0
- package/dist/planner.d.ts +70 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +264 -0
- package/dist/planner.js.map +1 -0
- package/dist/qa-pipeline.d.ts +365 -0
- package/dist/qa-pipeline.d.ts.map +1 -0
- package/dist/qa-pipeline.js +1352 -0
- package/dist/qa-pipeline.js.map +1 -0
- package/dist/reasoning-adapter.d.ts +116 -0
- package/dist/reasoning-adapter.d.ts.map +1 -0
- package/dist/reasoning-adapter.js +187 -0
- package/dist/reasoning-adapter.js.map +1 -0
- package/dist/role-registry.d.ts +55 -0
- package/dist/role-registry.d.ts.map +1 -0
- package/dist/role-registry.js +192 -0
- package/dist/role-registry.js.map +1 -0
- package/dist/sandbox-tiers.d.ts +327 -0
- package/dist/sandbox-tiers.d.ts.map +1 -0
- package/dist/sandbox-tiers.js +928 -0
- package/dist/sandbox-tiers.js.map +1 -0
- package/dist/security-scanner.d.ts +222 -0
- package/dist/security-scanner.d.ts.map +1 -0
- package/dist/security-scanner.js +1129 -0
- package/dist/security-scanner.js.map +1 -0
- package/dist/security.d.ts +93 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +393 -0
- package/dist/security.js.map +1 -0
- package/dist/self-reflection.d.ts +397 -0
- package/dist/self-reflection.d.ts.map +1 -0
- package/dist/self-reflection.js +908 -0
- package/dist/self-reflection.js.map +1 -0
- package/dist/session-persistence.d.ts +191 -0
- package/dist/session-persistence.d.ts.map +1 -0
- package/dist/session-persistence.js +395 -0
- package/dist/session-persistence.js.map +1 -0
- package/dist/speculative-executor.d.ts +210 -0
- package/dist/speculative-executor.d.ts.map +1 -0
- package/dist/speculative-executor.js +618 -0
- package/dist/speculative-executor.js.map +1 -0
- package/dist/state-machine.d.ts +289 -0
- package/dist/state-machine.d.ts.map +1 -0
- package/dist/state-machine.js +695 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/sub-agent.d.ts +177 -0
- package/dist/sub-agent.d.ts.map +1 -0
- package/dist/sub-agent.js +303 -0
- package/dist/sub-agent.js.map +1 -0
- package/dist/system-prompt.d.ts +26 -0
- package/dist/system-prompt.d.ts.map +1 -0
- package/dist/system-prompt.js +84 -0
- package/dist/system-prompt.js.map +1 -0
- package/dist/test-intelligence.d.ts +439 -0
- package/dist/test-intelligence.d.ts.map +1 -0
- package/dist/test-intelligence.js +1165 -0
- package/dist/test-intelligence.js.map +1 -0
- package/dist/types.d.ts +632 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/vector-index.d.ts +314 -0
- package/dist/vector-index.d.ts.map +1 -0
- package/dist/vector-index.js +618 -0
- package/dist/vector-index.js.map +1 -0
- 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
|