@yuaone/core 0.4.2 → 0.4.4
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/dist/agent-logger.d.ts +1 -1
- package/dist/agent-logger.d.ts.map +1 -1
- package/dist/agent-logger.js +17 -15
- package/dist/agent-logger.js.map +1 -1
- package/dist/agent-loop.d.ts +31 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +514 -98
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent-modes.d.ts.map +1 -1
- package/dist/agent-modes.js +5 -0
- package/dist/agent-modes.js.map +1 -1
- package/dist/async-completion-queue.d.ts +2 -0
- package/dist/async-completion-queue.d.ts.map +1 -1
- package/dist/async-completion-queue.js +14 -0
- package/dist/async-completion-queue.js.map +1 -1
- package/dist/auto-fix.d.ts.map +1 -1
- package/dist/auto-fix.js +12 -1
- package/dist/auto-fix.js.map +1 -1
- package/dist/benchmark-runner.d.ts.map +1 -1
- package/dist/benchmark-runner.js +5 -1
- package/dist/benchmark-runner.js.map +1 -1
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -1
- package/dist/context-manager.d.ts +25 -0
- package/dist/context-manager.d.ts.map +1 -1
- package/dist/context-manager.js +132 -5
- package/dist/context-manager.js.map +1 -1
- package/dist/continuation-engine.d.ts.map +1 -1
- package/dist/continuation-engine.js +8 -7
- package/dist/continuation-engine.js.map +1 -1
- package/dist/continuous-reflection.d.ts.map +1 -1
- package/dist/continuous-reflection.js +22 -12
- package/dist/continuous-reflection.js.map +1 -1
- package/dist/cost-optimizer.js +1 -1
- package/dist/cost-optimizer.js.map +1 -1
- package/dist/cross-file-refactor.d.ts.map +1 -1
- package/dist/cross-file-refactor.js +7 -2
- package/dist/cross-file-refactor.js.map +1 -1
- package/dist/dag-orchestrator.d.ts +10 -1
- package/dist/dag-orchestrator.d.ts.map +1 -1
- package/dist/dag-orchestrator.js +101 -6
- package/dist/dag-orchestrator.js.map +1 -1
- package/dist/debate-orchestrator.d.ts +1 -0
- package/dist/debate-orchestrator.d.ts.map +1 -1
- package/dist/debate-orchestrator.js +27 -15
- package/dist/debate-orchestrator.js.map +1 -1
- package/dist/dependency-analyzer.d.ts.map +1 -1
- package/dist/dependency-analyzer.js +19 -1
- package/dist/dependency-analyzer.js.map +1 -1
- package/dist/dynamic-role-generator.d.ts.map +1 -1
- package/dist/dynamic-role-generator.js +6 -3
- package/dist/dynamic-role-generator.js.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/event-bus.d.ts.map +1 -1
- package/dist/event-bus.js +4 -3
- package/dist/event-bus.js.map +1 -1
- package/dist/execution-engine.d.ts +39 -1
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/execution-engine.js +453 -83
- package/dist/execution-engine.js.map +1 -1
- package/dist/failure-recovery.d.ts.map +1 -1
- package/dist/failure-recovery.js +14 -3
- package/dist/failure-recovery.js.map +1 -1
- package/dist/git-intelligence.d.ts.map +1 -1
- package/dist/git-intelligence.js +16 -11
- package/dist/git-intelligence.js.map +1 -1
- package/dist/governor.d.ts +8 -0
- package/dist/governor.d.ts.map +1 -1
- package/dist/governor.js +19 -1
- package/dist/governor.js.map +1 -1
- package/dist/hierarchical-planner.d.ts +3 -0
- package/dist/hierarchical-planner.d.ts.map +1 -1
- package/dist/hierarchical-planner.js +32 -2
- package/dist/hierarchical-planner.js.map +1 -1
- package/dist/impact-analyzer.d.ts +27 -0
- package/dist/impact-analyzer.d.ts.map +1 -1
- package/dist/impact-analyzer.js +415 -53
- package/dist/impact-analyzer.js.map +1 -1
- package/dist/intent-inference.d.ts.map +1 -1
- package/dist/intent-inference.js +20 -24
- package/dist/intent-inference.js.map +1 -1
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +5 -3
- package/dist/kernel.js.map +1 -1
- package/dist/language-detector.d.ts +19 -0
- package/dist/language-detector.d.ts.map +1 -0
- package/dist/language-detector.js +482 -0
- package/dist/language-detector.js.map +1 -0
- package/dist/language-support.d.ts.map +1 -1
- package/dist/language-support.js +5 -9
- package/dist/language-support.js.map +1 -1
- package/dist/llm-client.d.ts +21 -8
- package/dist/llm-client.d.ts.map +1 -1
- package/dist/llm-client.js +125 -21
- package/dist/llm-client.js.map +1 -1
- package/dist/mcp-client.d.ts.map +1 -1
- package/dist/mcp-client.js +9 -1
- package/dist/mcp-client.js.map +1 -1
- package/dist/memory-manager.d.ts +13 -8
- package/dist/memory-manager.d.ts.map +1 -1
- package/dist/memory-manager.js +125 -32
- package/dist/memory-manager.js.map +1 -1
- package/dist/memory-updater.d.ts.map +1 -1
- package/dist/memory-updater.js +5 -4
- package/dist/memory-updater.js.map +1 -1
- package/dist/memory.d.ts +6 -2
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +32 -4
- package/dist/memory.js.map +1 -1
- package/dist/parallel-executor.d.ts +7 -0
- package/dist/parallel-executor.d.ts.map +1 -1
- package/dist/parallel-executor.js +28 -0
- package/dist/parallel-executor.js.map +1 -1
- package/dist/perf-optimizer.d.ts.map +1 -1
- package/dist/perf-optimizer.js +18 -3
- package/dist/perf-optimizer.js.map +1 -1
- package/dist/persona.d.ts.map +1 -1
- package/dist/persona.js +8 -3
- package/dist/persona.js.map +1 -1
- package/dist/planner.d.ts.map +1 -1
- package/dist/planner.js +5 -3
- package/dist/planner.js.map +1 -1
- package/dist/plugin-auto-loader.d.ts.map +1 -1
- package/dist/plugin-auto-loader.js +4 -1
- package/dist/plugin-auto-loader.js.map +1 -1
- package/dist/plugin-registry.d.ts +4 -0
- package/dist/plugin-registry.d.ts.map +1 -1
- package/dist/plugin-registry.js +6 -0
- package/dist/plugin-registry.js.map +1 -1
- package/dist/plugin-validator.d.ts.map +1 -1
- package/dist/plugin-validator.js +10 -1
- package/dist/plugin-validator.js.map +1 -1
- package/dist/reasoning-aggregator.d.ts +35 -0
- package/dist/reasoning-aggregator.d.ts.map +1 -0
- package/dist/reasoning-aggregator.js +102 -0
- package/dist/reasoning-aggregator.js.map +1 -0
- package/dist/reasoning-tree.d.ts +23 -0
- package/dist/reasoning-tree.d.ts.map +1 -0
- package/dist/reasoning-tree.js +44 -0
- package/dist/reasoning-tree.js.map +1 -0
- package/dist/session-persistence.d.ts +8 -4
- package/dist/session-persistence.d.ts.map +1 -1
- package/dist/session-persistence.js +22 -7
- package/dist/session-persistence.js.map +1 -1
- package/dist/skill-learner.d.ts.map +1 -1
- package/dist/skill-learner.js +4 -2
- package/dist/skill-learner.js.map +1 -1
- package/dist/skill-loader.d.ts +4 -0
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/skill-loader.js +6 -0
- package/dist/skill-loader.js.map +1 -1
- package/dist/speculative-executor.d.ts +22 -0
- package/dist/speculative-executor.d.ts.map +1 -1
- package/dist/speculative-executor.js +90 -45
- package/dist/speculative-executor.js.map +1 -1
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +4 -2
- package/dist/state-machine.js.map +1 -1
- package/dist/sub-agent-prompts.d.ts +5 -29
- package/dist/sub-agent-prompts.d.ts.map +1 -1
- package/dist/sub-agent-prompts.js +231 -134
- package/dist/sub-agent-prompts.js.map +1 -1
- package/dist/sub-agent.d.ts +19 -0
- package/dist/sub-agent.d.ts.map +1 -1
- package/dist/sub-agent.js +135 -11
- package/dist/sub-agent.js.map +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +45 -0
- package/dist/system-prompt.js.map +1 -1
- package/dist/task-classifier.js +1 -1
- package/dist/task-classifier.js.map +1 -1
- package/dist/types.d.ts +67 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vector-index.d.ts +14 -0
- package/dist/vector-index.d.ts.map +1 -1
- package/dist/vector-index.js +84 -16
- package/dist/vector-index.js.map +1 -1
- package/dist/workspace-lock.d.ts +5 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +16 -0
- package/dist/workspace-lock.js.map +1 -0
- package/package.json +2 -1
package/dist/execution-engine.js
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import { EventEmitter } from "node:events";
|
|
30
30
|
import { readFile } from "node:fs/promises";
|
|
31
|
+
import path from "node:path";
|
|
31
32
|
import { BYOKClient } from "./llm-client.js";
|
|
32
33
|
import { AgentLoop } from "./agent-loop.js";
|
|
33
34
|
import { AgentLogger } from "./agent-logger.js";
|
|
@@ -46,6 +47,12 @@ import { SecurityScanner } from "./security-scanner.js";
|
|
|
46
47
|
import { DocIntelligence } from "./doc-intelligence.js";
|
|
47
48
|
import { IntentInferenceEngine } from "./intent-inference.js";
|
|
48
49
|
import { SpeculativeExecutor } from "./speculative-executor.js";
|
|
50
|
+
import { SubAgent } from "./sub-agent.js";
|
|
51
|
+
import { routeSubAgent } from "./sub-agent-router.js";
|
|
52
|
+
import { DAGOrchestrator } from "./dag-orchestrator.js";
|
|
53
|
+
import { WorkspaceLock } from "./workspace-lock.js";
|
|
54
|
+
import { SkillLoader, } from "./skill-loader.js";
|
|
55
|
+
import { SkillLearner, } from "./skill-learner.js";
|
|
49
56
|
// ─── Defaults ────────────────────────────────────────────────────
|
|
50
57
|
const ENGINE_DEFAULTS = {
|
|
51
58
|
maxIterations: 100,
|
|
@@ -82,6 +89,7 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
82
89
|
reflection;
|
|
83
90
|
codebaseContext;
|
|
84
91
|
vectorIndex;
|
|
92
|
+
embeddingCache;
|
|
85
93
|
stateMachine;
|
|
86
94
|
abortController;
|
|
87
95
|
changedFiles;
|
|
@@ -97,6 +105,8 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
97
105
|
parallelStepResults;
|
|
98
106
|
/** DAG 기반 병렬 실행이 완료되었는지 */
|
|
99
107
|
parallelExecutionDone;
|
|
108
|
+
dagOrchestrator = null;
|
|
109
|
+
workspaceLock;
|
|
100
110
|
/** MCP server configurations (stored from constructor config) */
|
|
101
111
|
mcpServerConfigs;
|
|
102
112
|
/** MCP Client — external tool bridge (null if disabled) */
|
|
@@ -105,10 +115,32 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
105
115
|
mcpToolDefinitions = [];
|
|
106
116
|
/** Performance optimizer (null if disabled) */
|
|
107
117
|
perfOptimizer = null;
|
|
118
|
+
/**
|
|
119
|
+
* step / sub-agent 단위 토큰 예산 SSOT.
|
|
120
|
+
* - 최소 5k 보장
|
|
121
|
+
* - 병렬도 고려해서 전체 예산을 안전하게 분할
|
|
122
|
+
*/
|
|
123
|
+
getSubAgentTokenBudget() {
|
|
124
|
+
const divisor = Math.max(1, this.config.maxParallelAgents + 1);
|
|
125
|
+
return Math.max(5000, Math.floor(this.config.totalTokenBudget / divisor));
|
|
126
|
+
}
|
|
108
127
|
/** Sandbox manager for tool call validation (null if disabled) */
|
|
109
128
|
sandboxManager = null;
|
|
129
|
+
/** Skill loader */
|
|
130
|
+
skillLoader;
|
|
131
|
+
/** Skill learner */
|
|
132
|
+
skillLearner;
|
|
133
|
+
/** Static + learned skill definitions */
|
|
134
|
+
skills;
|
|
135
|
+
/** Run-level skill context */
|
|
136
|
+
activeSkillContext = null;
|
|
137
|
+
/** Step resolved skills */
|
|
138
|
+
resolvedSkillsByStepId = new Map();
|
|
139
|
+
/** Global complexity inferred during analyze phase */
|
|
140
|
+
globalComplexity = "moderate";
|
|
110
141
|
constructor(config) {
|
|
111
142
|
super();
|
|
143
|
+
this.setMaxListeners(200);
|
|
112
144
|
this.config = {
|
|
113
145
|
byokConfig: config.byokConfig,
|
|
114
146
|
projectPath: config.projectPath,
|
|
@@ -155,6 +187,15 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
155
187
|
});
|
|
156
188
|
this.codebaseContext = null;
|
|
157
189
|
this.vectorIndex = null;
|
|
190
|
+
const memoryCache = new Map();
|
|
191
|
+
this.embeddingCache = {
|
|
192
|
+
async get(key) {
|
|
193
|
+
return memoryCache.get(key) ?? null;
|
|
194
|
+
},
|
|
195
|
+
async set(key, embedding) {
|
|
196
|
+
memoryCache.set(key, embedding);
|
|
197
|
+
},
|
|
198
|
+
};
|
|
158
199
|
this.stateMachine = null;
|
|
159
200
|
this.planGraph = null;
|
|
160
201
|
this.abortController = new AbortController();
|
|
@@ -163,6 +204,28 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
163
204
|
this.lastTermination = { reason: "USER_CANCELLED" };
|
|
164
205
|
this.parallelStepResults = new Map();
|
|
165
206
|
this.parallelExecutionDone = false;
|
|
207
|
+
this.skillLoader = new SkillLoader();
|
|
208
|
+
this.skillLearner = new SkillLearner(this.config.projectPath);
|
|
209
|
+
this.skills = config.skills ?? [];
|
|
210
|
+
this.activeSkillContext = null;
|
|
211
|
+
this.resolvedSkillsByStepId = new Map();
|
|
212
|
+
this.workspaceLock = new WorkspaceLock();
|
|
213
|
+
// Agent DAG orchestrator
|
|
214
|
+
this.dagOrchestrator = new DAGOrchestrator({
|
|
215
|
+
maxParallelAgents: this.config.maxParallelAgents,
|
|
216
|
+
maxRetries: 2,
|
|
217
|
+
tokenBudget: this.config.totalTokenBudget,
|
|
218
|
+
wallTimeLimit: 600000,
|
|
219
|
+
spawnAgent: this.spawnAgent.bind(this),
|
|
220
|
+
});
|
|
221
|
+
this.dagOrchestrator.on("dag:agent_reasoning", (e) => {
|
|
222
|
+
this.emit("agent:event", {
|
|
223
|
+
kind: "agent:reasoning_timeline",
|
|
224
|
+
source: "dag",
|
|
225
|
+
taskId: e.taskId,
|
|
226
|
+
text: e.text,
|
|
227
|
+
});
|
|
228
|
+
});
|
|
166
229
|
// Store MCP server configs for use in execute()
|
|
167
230
|
this.mcpServerConfigs = config.mcpServerConfigs ?? [];
|
|
168
231
|
// Initialize PerfOptimizer if enabled
|
|
@@ -229,10 +292,21 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
229
292
|
this.planGraph = null;
|
|
230
293
|
this.parallelStepResults = new Map();
|
|
231
294
|
this.parallelExecutionDone = false;
|
|
295
|
+
this.resolvedSkillsByStepId.clear();
|
|
296
|
+
this.activeSkillContext = {
|
|
297
|
+
taskDescription: goal,
|
|
298
|
+
};
|
|
232
299
|
this.emit("engine:start", goal);
|
|
233
300
|
this._logger.logInput(goal);
|
|
234
301
|
this.reflection.think("start", `Goal received: "${goal}"`);
|
|
235
302
|
try {
|
|
303
|
+
// 0.5 Load learned skills
|
|
304
|
+
try {
|
|
305
|
+
await this.skillLearner.init();
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
this._logger.warn("system", `SkillLearner init failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
309
|
+
}
|
|
236
310
|
// 0. Intent inference pre-processing (refine ambiguous goals)
|
|
237
311
|
if (this.config.enableIntentInference) {
|
|
238
312
|
try {
|
|
@@ -247,6 +321,10 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
247
321
|
this._logger.info("system", `Intent inference: ambiguous input refined — "${goal}" → "${intentResult.refinedGoal}" (category: ${intentResult.category}, confidence: ${intentResult.confidence.toFixed(2)})`);
|
|
248
322
|
this.reflection.think("analyze", `Intent inference refined ambiguous goal: "${goal}" → "${intentResult.refinedGoal}" [${intentResult.category}, confidence=${intentResult.confidence.toFixed(2)}]`);
|
|
249
323
|
goal = intentResult.refinedGoal;
|
|
324
|
+
this.activeSkillContext = {
|
|
325
|
+
...(this.activeSkillContext ?? {}),
|
|
326
|
+
taskDescription: goal,
|
|
327
|
+
};
|
|
250
328
|
}
|
|
251
329
|
else {
|
|
252
330
|
this._logger.info("system", `Intent inference: input is clear (category: ${intentResult.category}, confidence: ${intentResult.confidence.toFixed(2)})`);
|
|
@@ -267,24 +345,39 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
267
345
|
this._logger.info("system", `Codebase indexed: ${stats.totalFiles} files, ${stats.totalSymbols} symbols`);
|
|
268
346
|
this.reflection.think("analyze", `Index built: ${stats.totalFiles} files, ${stats.totalSymbols} symbols`);
|
|
269
347
|
}
|
|
270
|
-
// 1b.
|
|
348
|
+
// 1b. VectorIndex initialization (pgvector semantic search)
|
|
271
349
|
if (this.config.enableVectorSearch && !this.vectorIndex) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
350
|
+
try {
|
|
351
|
+
const sqlExecutor = {
|
|
352
|
+
query: async (sql, params) => {
|
|
353
|
+
return this.config.toolExecutor.executeSQL
|
|
354
|
+
? await this.config.toolExecutor.executeSQL(sql, params)
|
|
355
|
+
: { rows: [] };
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
const embeddingProvider = {
|
|
359
|
+
dimension: 1536,
|
|
360
|
+
embed: async (texts) => {
|
|
361
|
+
const results = [];
|
|
362
|
+
for (const text of texts) {
|
|
363
|
+
const resp = await this.llmClient.embed(text);
|
|
364
|
+
results.push(resp.embedding);
|
|
365
|
+
}
|
|
366
|
+
return results;
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
this.vectorIndex = new VectorIndex({
|
|
370
|
+
projectId: path.basename(this.config.projectPath),
|
|
371
|
+
sqlExecutor,
|
|
372
|
+
embeddingProvider,
|
|
373
|
+
embeddingCache: this.embeddingCache
|
|
374
|
+
});
|
|
375
|
+
await this.vectorIndex.initialize();
|
|
376
|
+
this._logger.info("system", "VectorIndex initialized (pgvector semantic search enabled)");
|
|
377
|
+
}
|
|
378
|
+
catch (err) {
|
|
379
|
+
this._logger.warn("system", `VectorIndex initialization failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
380
|
+
}
|
|
288
381
|
}
|
|
289
382
|
// 1c. MCP Client initialization (optional — connect to external MCP servers)
|
|
290
383
|
if (this.mcpServerConfigs.length > 0) {
|
|
@@ -463,9 +556,19 @@ export class ExecutionEngine extends EventEmitter {
|
|
|
463
556
|
if (complexityMatch) {
|
|
464
557
|
complexity = complexityMatch[1].toLowerCase();
|
|
465
558
|
}
|
|
559
|
+
this.globalComplexity =
|
|
560
|
+
complexity;
|
|
466
561
|
this._logger.logDecision("Goal complexity assessment", ["trivial", "simple", "moderate", "complex", "massive"], complexity, `LLM analysis determined complexity based on goal and codebase context`);
|
|
467
562
|
this.reflection.think("analyze", `Analysis complete — complexity: ${complexity}`);
|
|
563
|
+
this.activeSkillContext = {
|
|
564
|
+
...(this.activeSkillContext ?? {}),
|
|
565
|
+
taskDescription: goal,
|
|
566
|
+
};
|
|
468
567
|
exitAnalyze();
|
|
568
|
+
this.activeSkillContext = {
|
|
569
|
+
...(this.activeSkillContext ?? {}),
|
|
570
|
+
taskDescription: goal,
|
|
571
|
+
};
|
|
469
572
|
return { complexity, context: content };
|
|
470
573
|
}
|
|
471
574
|
/**
|
|
@@ -557,6 +660,28 @@ Exactly one approach should have recommended=true.`;
|
|
|
557
660
|
`${hPlan.totalEstimatedIterations} estimated iterations, ` +
|
|
558
661
|
`${hPlan.parallelizableGroups.length} parallel groups`);
|
|
559
662
|
const executionPlan = planner.toExecutionPlan(hPlan);
|
|
663
|
+
for (const step of executionPlan.steps) {
|
|
664
|
+
const stepSkillContext = {
|
|
665
|
+
...(this.activeSkillContext ?? {}),
|
|
666
|
+
taskDescription: step.goal,
|
|
667
|
+
filePath: step.targetFiles[0],
|
|
668
|
+
};
|
|
669
|
+
const matched = this.skillLoader.matchTriggers(this.skills, stepSkillContext);
|
|
670
|
+
const learned = this.skillLearner
|
|
671
|
+
.getRelevantSkills({
|
|
672
|
+
filePath: step.targetFiles[0],
|
|
673
|
+
})
|
|
674
|
+
.map((s) => this.skillLearner.toSkillDefinition(s));
|
|
675
|
+
const combined = [...matched, ...learned];
|
|
676
|
+
const deduped = new Map();
|
|
677
|
+
for (const skill of combined) {
|
|
678
|
+
deduped.set(skill.id, skill);
|
|
679
|
+
}
|
|
680
|
+
const resolvedSkills = [...deduped.values()].map((skill) => this.skillLoader.loadTemplate(skill));
|
|
681
|
+
step.skillIds = resolvedSkills.map((s) => s.definition.id);
|
|
682
|
+
step.resolvedSkills = resolvedSkills;
|
|
683
|
+
this.resolvedSkillsByStepId.set(step.id, resolvedSkills);
|
|
684
|
+
}
|
|
560
685
|
// Initialize PlanGraphManager for step progress tracking
|
|
561
686
|
this.planGraph = PlanGraphManager.fromExecutionPlan(this.sessionId, executionPlan);
|
|
562
687
|
this._logger.info("system", `PlanGraph initialized: ${executionPlan.steps.length} nodes, ${this.planGraph.getState().parallelGroups.length} parallel groups`);
|
|
@@ -574,7 +699,10 @@ Exactly one approach should have recommended=true.`;
|
|
|
574
699
|
id: "step-1",
|
|
575
700
|
goal: `${approach.description}: ${goal}`,
|
|
576
701
|
targetFiles: [],
|
|
702
|
+
role: "coder",
|
|
577
703
|
readFiles: [],
|
|
704
|
+
skillIds: [],
|
|
705
|
+
resolvedSkills: [],
|
|
578
706
|
tools: ["file_read", "file_write", "file_edit", "grep", "glob"],
|
|
579
707
|
estimatedIterations: 10,
|
|
580
708
|
dependsOn: [],
|
|
@@ -582,6 +710,25 @@ Exactly one approach should have recommended=true.`;
|
|
|
582
710
|
],
|
|
583
711
|
estimatedTokens: 50_000,
|
|
584
712
|
};
|
|
713
|
+
{
|
|
714
|
+
const step = fallbackPlan.steps[0];
|
|
715
|
+
const stepSkillContext = {
|
|
716
|
+
...(this.activeSkillContext ?? {}),
|
|
717
|
+
taskDescription: step.goal,
|
|
718
|
+
};
|
|
719
|
+
const matched = this.skillLoader.matchTriggers(this.skills, stepSkillContext);
|
|
720
|
+
const learned = this.skillLearner
|
|
721
|
+
.getRelevantSkills({})
|
|
722
|
+
.map((s) => this.skillLearner.toSkillDefinition(s));
|
|
723
|
+
const combined = [...matched, ...learned];
|
|
724
|
+
const deduped = new Map();
|
|
725
|
+
for (const skill of combined)
|
|
726
|
+
deduped.set(skill.id, skill);
|
|
727
|
+
const resolvedSkills = [...deduped.values()].map((skill) => this.skillLoader.loadTemplate(skill));
|
|
728
|
+
step.skillIds = resolvedSkills.map((s) => s.definition.id);
|
|
729
|
+
step.resolvedSkills = resolvedSkills;
|
|
730
|
+
this.resolvedSkillsByStepId.set(step.id, resolvedSkills);
|
|
731
|
+
}
|
|
585
732
|
// Initialize PlanGraphManager for fallback plan too
|
|
586
733
|
this.planGraph = PlanGraphManager.fromExecutionPlan(this.sessionId, fallbackPlan);
|
|
587
734
|
this._logger.info("system", `PlanGraph initialized (fallback): 1 node`);
|
|
@@ -629,7 +776,47 @@ Exactly one approach should have recommended=true.`;
|
|
|
629
776
|
};
|
|
630
777
|
}
|
|
631
778
|
// First call triggers full DAG execution for ALL steps
|
|
632
|
-
|
|
779
|
+
if (!this.dagOrchestrator) {
|
|
780
|
+
throw new Error("DAGOrchestrator not initialized");
|
|
781
|
+
}
|
|
782
|
+
const agentPlan = {
|
|
783
|
+
tasks: plan.steps.map((s, i) => ({
|
|
784
|
+
id: s.id,
|
|
785
|
+
goal: s.goal,
|
|
786
|
+
targetFiles: s.targetFiles,
|
|
787
|
+
readFiles: s.readFiles,
|
|
788
|
+
tools: s.tools,
|
|
789
|
+
estimatedIterations: s.estimatedIterations,
|
|
790
|
+
priority: plan.steps.length - i,
|
|
791
|
+
complexity: state.workingMemory.get("complexity") ?? "moderate",
|
|
792
|
+
role: s.role ?? "coder",
|
|
793
|
+
skillIds: s.skillIds ?? [],
|
|
794
|
+
resolvedSkills: s.resolvedSkills ?? this.resolvedSkillsByStepId.get(s.id) ?? [],
|
|
795
|
+
})),
|
|
796
|
+
estimatedTokens: plan.estimatedTokens,
|
|
797
|
+
estimatedDurationMs: plan.steps.reduce((sum, s) => sum + s.estimatedIterations * 1000, 0),
|
|
798
|
+
dependencies: plan.steps.flatMap((s) => (s.dependsOn ?? []).map((dep) => [dep, s.id])),
|
|
799
|
+
maxParallelAgents: this.config.maxParallelAgents,
|
|
800
|
+
};
|
|
801
|
+
const dagResult = await this.dagOrchestrator.execute(agentPlan, {
|
|
802
|
+
overallGoal: plan.goal,
|
|
803
|
+
projectPath: this.config.projectPath,
|
|
804
|
+
projectStructure: this.config.projectPath,
|
|
805
|
+
});
|
|
806
|
+
for (const task of dagResult.completedTasks) {
|
|
807
|
+
const stepIndex = plan.steps.findIndex(s => s.id === task.taskId);
|
|
808
|
+
if (stepIndex >= 0) {
|
|
809
|
+
this.parallelStepResults.set(stepIndex, {
|
|
810
|
+
stepIndex,
|
|
811
|
+
phase: "implement",
|
|
812
|
+
success: true,
|
|
813
|
+
output: task.summary,
|
|
814
|
+
changedFiles: task.changedFiles.map((f) => f.path),
|
|
815
|
+
tokensUsed: task.tokensUsed,
|
|
816
|
+
durationMs: 0
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
633
820
|
this.parallelExecutionDone = true;
|
|
634
821
|
// Return this step's result from cache
|
|
635
822
|
const result = this.parallelStepResults.get(stepIndex);
|
|
@@ -758,6 +945,11 @@ Exactly one approach should have recommended=true.`;
|
|
|
758
945
|
}
|
|
759
946
|
const exitImplement = this._logger.enterLayer("implement", `Step ${stepIndex + 1}/${plan.steps.length}: "${step.goal}"`);
|
|
760
947
|
this.reflection.think("implement", `Executing step ${stepIndex + 1}/${plan.steps.length}: "${step.goal}"`);
|
|
948
|
+
const stepSkillContext = {
|
|
949
|
+
...(this.activeSkillContext ?? {}),
|
|
950
|
+
taskDescription: step.goal,
|
|
951
|
+
filePath: step.targetFiles[0],
|
|
952
|
+
};
|
|
761
953
|
// Mark step as running in PlanGraph
|
|
762
954
|
const stepId = step.id;
|
|
763
955
|
if (this.planGraph) {
|
|
@@ -793,12 +985,21 @@ Exactly one approach should have recommended=true.`;
|
|
|
793
985
|
"speculative:approach:complete",
|
|
794
986
|
"speculative:evaluation",
|
|
795
987
|
"speculative:complete",
|
|
988
|
+
"speculative:timeline",
|
|
796
989
|
];
|
|
797
990
|
for (const eventName of specEvents) {
|
|
798
991
|
specExecutor.on(eventName, (...args) => {
|
|
799
992
|
this.emit(eventName, ...args);
|
|
800
993
|
});
|
|
801
994
|
}
|
|
995
|
+
specExecutor.on("speculative:timeline", (payload) => {
|
|
996
|
+
this.emit("agent:event", {
|
|
997
|
+
kind: "agent:reasoning_timeline",
|
|
998
|
+
source: "speculative",
|
|
999
|
+
taskId: payload.taskId,
|
|
1000
|
+
text: payload.text,
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
802
1003
|
// Build codebase context summary for speculative executor
|
|
803
1004
|
let codebaseContextSummary = "";
|
|
804
1005
|
if (this.codebaseContext) {
|
|
@@ -848,40 +1049,88 @@ Exactly one approach should have recommended=true.`;
|
|
|
848
1049
|
}
|
|
849
1050
|
}
|
|
850
1051
|
// ─── Normal AgentLoop Execution ─────────────────────────────
|
|
851
|
-
//
|
|
852
|
-
|
|
853
|
-
//
|
|
854
|
-
const loop = this.createAgentLoop(systemPrompt);
|
|
855
|
-
this.wireAgentLoopEvents(loop);
|
|
1052
|
+
// ─────────────────────────────────────────
|
|
1053
|
+
// SubAgent execution
|
|
1054
|
+
// ─────────────────────────────────────────
|
|
856
1055
|
const startMs = Date.now();
|
|
857
|
-
const
|
|
1056
|
+
const routing = routeSubAgent({
|
|
1057
|
+
role: step.role ?? "coder",
|
|
1058
|
+
complexity: state.workingMemory.get("complexity") ?? "moderate",
|
|
1059
|
+
fileCount: step.targetFiles.length,
|
|
1060
|
+
hasTests: step.tools.includes("test_run"),
|
|
1061
|
+
isCriticalPath: false,
|
|
1062
|
+
previousFailures: 0,
|
|
1063
|
+
parentModelTier: "NORMAL"
|
|
1064
|
+
});
|
|
1065
|
+
const role = step.role ?? "coder";
|
|
1066
|
+
const tier = routing.tier;
|
|
1067
|
+
const subAgent = new SubAgent({
|
|
1068
|
+
taskId: step.id,
|
|
1069
|
+
goal: step.goal,
|
|
1070
|
+
targetFiles: step.targetFiles,
|
|
1071
|
+
readFiles: step.readFiles,
|
|
1072
|
+
maxIterations: this.config.maxIterations,
|
|
1073
|
+
totalTokenBudget: this.getSubAgentTokenBudget(),
|
|
1074
|
+
projectPath: this.config.projectPath,
|
|
1075
|
+
byokConfig: this.config.byokConfig,
|
|
1076
|
+
tools: step.tools,
|
|
1077
|
+
createToolExecutor: () => this.config.toolExecutor,
|
|
1078
|
+
role,
|
|
1079
|
+
parentModelTier: tier,
|
|
1080
|
+
});
|
|
1081
|
+
const agentRole = subAgent.role;
|
|
1082
|
+
// ─────────────────────────────────────────
|
|
1083
|
+
// SubAgent lifecycle forwarding (CLI stream)
|
|
1084
|
+
// ─────────────────────────────────────────
|
|
1085
|
+
subAgent.on("subagent:phase", (_taskId, phase) => {
|
|
1086
|
+
this.emit("agent:subagent_phase", {
|
|
1087
|
+
role: agentRole,
|
|
1088
|
+
phase,
|
|
1089
|
+
taskId: step.id,
|
|
1090
|
+
goal: step.goal,
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
// ─────────────────────────────────────────
|
|
1094
|
+
// Forward SubAgent events → ExecutionEngine
|
|
1095
|
+
// ─────────────────────────────────────────
|
|
1096
|
+
subAgent.on("event", (event) => {
|
|
1097
|
+
this.emit("agent:event", event);
|
|
1098
|
+
});
|
|
1099
|
+
const subResult = await subAgent.run({
|
|
1100
|
+
overallGoal: plan.goal,
|
|
1101
|
+
taskGoal: step.goal,
|
|
1102
|
+
targetFiles: step.targetFiles,
|
|
1103
|
+
readFiles: step.readFiles,
|
|
1104
|
+
projectStructure: this.config.projectPath,
|
|
1105
|
+
skillContext: stepSkillContext,
|
|
1106
|
+
resolvedSkills: step.resolvedSkills ?? this.resolvedSkillsByStepId.get(step.id) ?? [],
|
|
1107
|
+
totalTasks: plan.steps.length,
|
|
1108
|
+
completedTasks: [],
|
|
1109
|
+
runningTasks: [],
|
|
1110
|
+
});
|
|
858
1111
|
const durationMs = Date.now() - startMs;
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
// The changedFiles set is updated via wireAgentLoopEvents
|
|
1112
|
+
const stepChangedFiles = new Set(subResult.changedFiles.map((f) => typeof f === "string" ? f : f.path));
|
|
1113
|
+
const loopUsage = {
|
|
1114
|
+
input: subResult.tokensUsed?.input ?? 0,
|
|
1115
|
+
output: subResult.tokensUsed?.output ?? 0,
|
|
1116
|
+
};
|
|
1117
|
+
const success = subResult.success;
|
|
866
1118
|
for (const file of this.changedFiles) {
|
|
867
|
-
if (step.targetFiles.
|
|
868
|
-
|
|
1119
|
+
if (step.targetFiles.length === 0)
|
|
1120
|
+
continue;
|
|
1121
|
+
if (step.targetFiles.some(f => file.endsWith(f))) {
|
|
1122
|
+
stepChangedFiles.add(file);
|
|
869
1123
|
}
|
|
870
1124
|
}
|
|
871
|
-
const success = termination.reason === "GOAL_ACHIEVED";
|
|
872
1125
|
// Update PlanGraph with step outcome
|
|
873
1126
|
if (this.planGraph) {
|
|
874
1127
|
try {
|
|
875
1128
|
if (success) {
|
|
876
|
-
this.planGraph.markCompleted(stepId,
|
|
877
|
-
? termination.summary
|
|
878
|
-
: "completed", stepChangedFiles, { input: loopUsage.input, output: loopUsage.output });
|
|
1129
|
+
this.planGraph.markCompleted(stepId, subResult.summary, [...stepChangedFiles], { input: loopUsage.input, output: loopUsage.output });
|
|
879
1130
|
this._logger.info("system", `PlanGraph: node "${stepId}" → completed`);
|
|
880
1131
|
}
|
|
881
1132
|
else {
|
|
882
|
-
const errorMsg =
|
|
883
|
-
? termination.error
|
|
884
|
-
: `Step failed: ${termination.reason}`;
|
|
1133
|
+
const errorMsg = subResult.error ?? "SubAgent failed";
|
|
885
1134
|
this.planGraph.markFailed(stepId, errorMsg);
|
|
886
1135
|
this._logger.info("system", `PlanGraph: node "${stepId}" → failed`);
|
|
887
1136
|
}
|
|
@@ -893,10 +1142,8 @@ Exactly one approach should have recommended=true.`;
|
|
|
893
1142
|
const progress = this.planGraph.getProgress();
|
|
894
1143
|
this._logger.info("system", `PlanGraph progress: ${progress.completed}/${progress.total} (${progress.percent}%)`);
|
|
895
1144
|
}
|
|
896
|
-
this._logger.logDecision(`Step ${stepIndex + 1} outcome`, ["succeeded", "failed"], success ? "succeeded" : "failed", `
|
|
897
|
-
this.reflection.think("implement", `Step ${stepIndex + 1} ${success ? "succeeded" : "failed"}: ${
|
|
898
|
-
? termination.summary
|
|
899
|
-
: termination.reason}`);
|
|
1145
|
+
this._logger.logDecision(`Step ${stepIndex + 1} outcome`, ["succeeded", "failed"], success ? "succeeded" : "failed", `SubAgent execution result`);
|
|
1146
|
+
this.reflection.think("implement", `Step ${stepIndex + 1} ${success ? "succeeded" : "failed"}: ${subResult.summary}`);
|
|
900
1147
|
// Checkpoint after step completes (success or failure)
|
|
901
1148
|
await this.checkpoint(stepIndex);
|
|
902
1149
|
exitImplement();
|
|
@@ -904,12 +1151,8 @@ Exactly one approach should have recommended=true.`;
|
|
|
904
1151
|
stepIndex,
|
|
905
1152
|
phase: "implement",
|
|
906
1153
|
success,
|
|
907
|
-
output:
|
|
908
|
-
|
|
909
|
-
: termination.reason === "ERROR"
|
|
910
|
-
? termination.error
|
|
911
|
-
: `Terminated: ${termination.reason}`,
|
|
912
|
-
changedFiles: stepChangedFiles,
|
|
1154
|
+
output: subResult.summary,
|
|
1155
|
+
changedFiles: [...stepChangedFiles],
|
|
913
1156
|
tokensUsed: loopUsage.input + loopUsage.output,
|
|
914
1157
|
durationMs,
|
|
915
1158
|
};
|
|
@@ -1109,6 +1352,28 @@ Exactly one approach should have recommended=true.`;
|
|
|
1109
1352
|
}
|
|
1110
1353
|
}
|
|
1111
1354
|
exitVerify();
|
|
1355
|
+
// Best-effort learned skill persistence hook
|
|
1356
|
+
try {
|
|
1357
|
+
const syntheticAnalysis = {
|
|
1358
|
+
errorPatterns: deepResult.suggestedFixes
|
|
1359
|
+
.filter((f) => !!f.description)
|
|
1360
|
+
.map((f) => ({
|
|
1361
|
+
message: f.description,
|
|
1362
|
+
resolution: f.description,
|
|
1363
|
+
frequency: 1,
|
|
1364
|
+
type: "VerificationIssue",
|
|
1365
|
+
tool: "verify",
|
|
1366
|
+
})),
|
|
1367
|
+
toolPatterns: [],
|
|
1368
|
+
};
|
|
1369
|
+
const learned = this.skillLearner.extractSkillFromRun(syntheticAnalysis, this.sessionId);
|
|
1370
|
+
if (learned) {
|
|
1371
|
+
await this.skillLearner.save();
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
catch (err) {
|
|
1375
|
+
this._logger.warn("system", `Skill learn/save failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
1376
|
+
}
|
|
1112
1377
|
// Map DeepVerifyResult → VerifyResult
|
|
1113
1378
|
return this.mapVerifyResult(deepResult);
|
|
1114
1379
|
}
|
|
@@ -1277,6 +1542,62 @@ ${[...this.changedFiles].join(", ")}
|
|
|
1277
1542
|
}
|
|
1278
1543
|
}
|
|
1279
1544
|
}
|
|
1545
|
+
/**
|
|
1546
|
+
* DAGOrchestrator가 호출하는 Agent worker
|
|
1547
|
+
*/
|
|
1548
|
+
async spawnAgent(task, context, _signal) {
|
|
1549
|
+
const routing = routeSubAgent({
|
|
1550
|
+
role: task.role ?? "coder",
|
|
1551
|
+
complexity: this.globalComplexity,
|
|
1552
|
+
fileCount: task.targetFiles.length,
|
|
1553
|
+
hasTests: task.tools?.includes("test_run") ?? false,
|
|
1554
|
+
isCriticalPath: false,
|
|
1555
|
+
previousFailures: 0,
|
|
1556
|
+
parentModelTier: "NORMAL"
|
|
1557
|
+
});
|
|
1558
|
+
const role = task.role ?? "coder";
|
|
1559
|
+
const subAgent = new SubAgent({
|
|
1560
|
+
taskId: task.id,
|
|
1561
|
+
goal: task.goal,
|
|
1562
|
+
targetFiles: task.targetFiles,
|
|
1563
|
+
readFiles: task.readFiles,
|
|
1564
|
+
maxIterations: this.config.maxIterations,
|
|
1565
|
+
totalTokenBudget: this.getSubAgentTokenBudget(),
|
|
1566
|
+
projectPath: this.config.projectPath,
|
|
1567
|
+
byokConfig: this.config.byokConfig,
|
|
1568
|
+
tools: task.tools,
|
|
1569
|
+
createToolExecutor: () => this.config.toolExecutor,
|
|
1570
|
+
role,
|
|
1571
|
+
parentModelTier: routing.tier,
|
|
1572
|
+
});
|
|
1573
|
+
const result = await subAgent.run({
|
|
1574
|
+
overallGoal: context.overallGoal,
|
|
1575
|
+
taskGoal: context.taskGoal ?? task.goal,
|
|
1576
|
+
targetFiles: context.targetFiles ?? task.targetFiles,
|
|
1577
|
+
readFiles: context.readFiles ?? task.readFiles,
|
|
1578
|
+
projectStructure: context.projectStructure,
|
|
1579
|
+
skillContext: context.skillContext ?? {
|
|
1580
|
+
taskDescription: task.goal,
|
|
1581
|
+
filePath: task.targetFiles[0],
|
|
1582
|
+
},
|
|
1583
|
+
resolvedSkills: context.resolvedSkills ?? task.resolvedSkills ?? [],
|
|
1584
|
+
totalTasks: 1,
|
|
1585
|
+
completedTasks: [],
|
|
1586
|
+
runningTasks: [],
|
|
1587
|
+
});
|
|
1588
|
+
return {
|
|
1589
|
+
taskId: task.id,
|
|
1590
|
+
summary: result.summary,
|
|
1591
|
+
changedFiles: result.changedFiles.map((file) => ({
|
|
1592
|
+
path: typeof file === "string" ? file : file.path,
|
|
1593
|
+
diff: "",
|
|
1594
|
+
})),
|
|
1595
|
+
tokensUsed: (result.tokensUsed?.input ?? 0) +
|
|
1596
|
+
(result.tokensUsed?.output ?? 0),
|
|
1597
|
+
iterations: this.config.maxIterations,
|
|
1598
|
+
usedSkillIds: task.skillIds ?? [],
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1280
1601
|
/**
|
|
1281
1602
|
* 개별 step 실행을 위한 AgentLoop 인스턴스를 생성한다.
|
|
1282
1603
|
*
|
|
@@ -1285,9 +1606,12 @@ ${[...this.changedFiles].join(", ")}
|
|
|
1285
1606
|
*/
|
|
1286
1607
|
createAgentLoop(systemPrompt) {
|
|
1287
1608
|
// Merge base tool definitions with MCP tool definitions
|
|
1609
|
+
// Tool filtering to avoid token explosion
|
|
1610
|
+
const baseTools = this.config.toolExecutor.definitions;
|
|
1611
|
+
const mcpToolsFiltered = this.mcpToolDefinitions.filter((t) => true);
|
|
1288
1612
|
const tools = [
|
|
1289
|
-
...
|
|
1290
|
-
...
|
|
1613
|
+
...baseTools,
|
|
1614
|
+
...mcpToolsFiltered,
|
|
1291
1615
|
];
|
|
1292
1616
|
// Wrap tool executor to add sandbox validation and MCP tool routing
|
|
1293
1617
|
const baseExecutor = this.config.toolExecutor;
|
|
@@ -1297,44 +1621,77 @@ ${[...this.changedFiles].join(", ")}
|
|
|
1297
1621
|
const wrappedExecutor = {
|
|
1298
1622
|
definitions: tools,
|
|
1299
1623
|
execute: async (call, abortSignal) => {
|
|
1300
|
-
|
|
1301
|
-
let
|
|
1624
|
+
let args = {};
|
|
1625
|
+
let release = null;
|
|
1302
1626
|
try {
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
name: call.name,
|
|
1311
|
-
output: `[Error] Invalid JSON in tool arguments: ${String(call.arguments).slice(0, 200)}`,
|
|
1312
|
-
success: false,
|
|
1313
|
-
durationMs: 0,
|
|
1314
|
-
};
|
|
1315
|
-
}
|
|
1316
|
-
// Sandbox pre-validation
|
|
1317
|
-
if (sandboxMgr) {
|
|
1318
|
-
const validation = sandboxMgr.validateToolCall(call.name, args);
|
|
1319
|
-
if (!validation.allowed) {
|
|
1627
|
+
// Parse arguments to object if needed
|
|
1628
|
+
try {
|
|
1629
|
+
args = typeof call.arguments === "string"
|
|
1630
|
+
? JSON.parse(call.arguments)
|
|
1631
|
+
: call.arguments;
|
|
1632
|
+
}
|
|
1633
|
+
catch {
|
|
1320
1634
|
return {
|
|
1321
1635
|
tool_call_id: call.id,
|
|
1322
1636
|
name: call.name,
|
|
1323
|
-
output:
|
|
1637
|
+
output: "[Error] invalid tool arguments JSON",
|
|
1324
1638
|
success: false,
|
|
1325
1639
|
durationMs: 0,
|
|
1326
1640
|
};
|
|
1327
1641
|
}
|
|
1642
|
+
const targetFile = typeof args?.path === "string"
|
|
1643
|
+
? args.path
|
|
1644
|
+
: typeof args?.file === "string"
|
|
1645
|
+
? args.file
|
|
1646
|
+
: null;
|
|
1647
|
+
if (targetFile && this.isFileMutationTool(call.name)) {
|
|
1648
|
+
release = await this.workspaceLock.acquire(targetFile);
|
|
1649
|
+
}
|
|
1650
|
+
// Sandbox pre-validation
|
|
1651
|
+
if (sandboxMgr) {
|
|
1652
|
+
const validation = sandboxMgr.validateToolCall(call.name, args);
|
|
1653
|
+
if (!validation.allowed) {
|
|
1654
|
+
return {
|
|
1655
|
+
tool_call_id: call.id,
|
|
1656
|
+
name: call.name,
|
|
1657
|
+
output: `[Sandbox blocked] ${validation.violations.join("; ")}`,
|
|
1658
|
+
success: false,
|
|
1659
|
+
durationMs: 0,
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
// Route MCP tool calls to MCPClient (check MCP registry, not string pattern)
|
|
1664
|
+
if (mcpCli && mcpToolDefs.some((t) => t.name === call.name)) {
|
|
1665
|
+
const MCP_TIMEOUT = 20000;
|
|
1666
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("MCP timeout")), MCP_TIMEOUT));
|
|
1667
|
+
try {
|
|
1668
|
+
return (await Promise.race([
|
|
1669
|
+
mcpCli.callToolAsYuan(call.name, args, call.id),
|
|
1670
|
+
timeout,
|
|
1671
|
+
]));
|
|
1672
|
+
}
|
|
1673
|
+
catch (err) {
|
|
1674
|
+
return {
|
|
1675
|
+
tool_call_id: call.id,
|
|
1676
|
+
name: call.name,
|
|
1677
|
+
output: `[MCP error] ${err instanceof Error ? err.message : String(err)}`,
|
|
1678
|
+
success: false,
|
|
1679
|
+
durationMs: MCP_TIMEOUT,
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
// Default: use base executor
|
|
1684
|
+
return await baseExecutor.execute(call, abortSignal);
|
|
1328
1685
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1686
|
+
finally {
|
|
1687
|
+
if (release) {
|
|
1688
|
+
release();
|
|
1689
|
+
}
|
|
1332
1690
|
}
|
|
1333
|
-
// Default: use base executor
|
|
1334
|
-
return baseExecutor.execute(call, abortSignal);
|
|
1335
1691
|
},
|
|
1336
1692
|
};
|
|
1337
1693
|
return new AgentLoop({
|
|
1694
|
+
abortSignal: this.abortController.signal,
|
|
1338
1695
|
config: {
|
|
1339
1696
|
byok: this.config.byokConfig,
|
|
1340
1697
|
loop: {
|
|
@@ -1491,6 +1848,17 @@ Then provide your analysis.`;
|
|
|
1491
1848
|
confidence: deep.confidence,
|
|
1492
1849
|
};
|
|
1493
1850
|
}
|
|
1851
|
+
isFileMutationTool(toolName) {
|
|
1852
|
+
const mutationTools = new Set([
|
|
1853
|
+
"file_write",
|
|
1854
|
+
"file_edit",
|
|
1855
|
+
"file_delete",
|
|
1856
|
+
"file_move",
|
|
1857
|
+
"file_rename",
|
|
1858
|
+
"apply_patch",
|
|
1859
|
+
]);
|
|
1860
|
+
return mutationTools.has(toolName);
|
|
1861
|
+
}
|
|
1494
1862
|
/**
|
|
1495
1863
|
* StateMachine 최종 상태에서 ExecutionResult를 생성한다.
|
|
1496
1864
|
*
|
|
@@ -1546,13 +1914,15 @@ Then provide your analysis.`;
|
|
|
1546
1914
|
return fenced[1].trim();
|
|
1547
1915
|
}
|
|
1548
1916
|
// Find first JSON array or object
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
|
|
1917
|
+
const start = content.indexOf("[");
|
|
1918
|
+
const end = content.lastIndexOf("]");
|
|
1919
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
1920
|
+
return content.slice(start, end + 1);
|
|
1552
1921
|
}
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
|
|
1922
|
+
const objStart = content.indexOf("{");
|
|
1923
|
+
const objEnd = content.lastIndexOf("}");
|
|
1924
|
+
if (objStart !== -1 && objEnd !== -1 && objEnd > objStart) {
|
|
1925
|
+
return content.slice(objStart, objEnd + 1);
|
|
1556
1926
|
}
|
|
1557
1927
|
return content.trim();
|
|
1558
1928
|
}
|