@yuaone/core 0.4.8 → 0.5.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/dist/agent-loop.d.ts +15 -27
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +319 -28
- package/dist/agent-loop.js.map +1 -1
- package/dist/ast-analyzer.d.ts +147 -0
- package/dist/ast-analyzer.d.ts.map +1 -0
- package/dist/ast-analyzer.js +344 -0
- package/dist/ast-analyzer.js.map +1 -0
- package/dist/codebase-context.d.ts +24 -4
- package/dist/codebase-context.d.ts.map +1 -1
- package/dist/codebase-context.js +100 -10
- package/dist/codebase-context.js.map +1 -1
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +7 -0
- package/dist/constants.js.map +1 -1
- package/dist/cross-file-refactor.d.ts +12 -3
- package/dist/cross-file-refactor.d.ts.map +1 -1
- package/dist/cross-file-refactor.js +35 -3
- package/dist/cross-file-refactor.js.map +1 -1
- package/dist/execution-engine.d.ts +9 -0
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/execution-engine.js +59 -10
- package/dist/execution-engine.js.map +1 -1
- package/dist/index.d.ts +4 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -11
- package/dist/index.js.map +1 -1
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +15 -8
- package/dist/security.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-store.d.ts +148 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +328 -0
- package/dist/vector-store.js.map +1 -0
- package/package.json +1 -1
package/dist/agent-loop.d.ts
CHANGED
|
@@ -75,33 +75,6 @@ export interface AgentLoopOptions {
|
|
|
75
75
|
/** Enable background agents (persistent monitors, default false — opt-in) */
|
|
76
76
|
enableBackgroundAgents?: boolean;
|
|
77
77
|
}
|
|
78
|
-
/**
|
|
79
|
-
* AgentLoop — YUAN 에이전트의 핵심 실행 루프.
|
|
80
|
-
*
|
|
81
|
-
* 동작 흐름:
|
|
82
|
-
* 1. 사용자 메시지 수신
|
|
83
|
-
* 2. 시스템 프롬프트 + 히스토리로 LLM 호출
|
|
84
|
-
* 3. LLM 응답에서 tool_call 파싱
|
|
85
|
-
* 4. Governor가 안전성 검증
|
|
86
|
-
* 5. 도구 실행 → 결과를 히스토리에 추가
|
|
87
|
-
* 6. LLM에 결과 피드백 → 2번으로 반복
|
|
88
|
-
* 7. 종료 조건 충족 시 결과 반환
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* const loop = new AgentLoop({
|
|
93
|
-
* config: agentConfig,
|
|
94
|
-
* toolExecutor: executor,
|
|
95
|
-
* governorConfig: { planTier: "PRO" },
|
|
96
|
-
* });
|
|
97
|
-
*
|
|
98
|
-
* loop.on("event", (event: AgentEvent) => {
|
|
99
|
-
* // SSE 스트리밍
|
|
100
|
-
* });
|
|
101
|
-
*
|
|
102
|
-
* const result = await loop.run("모든 console.log를 제거해줘");
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
78
|
export declare class AgentLoop extends EventEmitter {
|
|
106
79
|
private readonly abortSignal?;
|
|
107
80
|
private readonly llmClient;
|
|
@@ -171,6 +144,16 @@ export declare class AgentLoop extends EventEmitter {
|
|
|
171
144
|
private static readonly MAX_ACTIVE_SKILLS;
|
|
172
145
|
/** Context Budget: track injected system messages per iteration to cap at 5 */
|
|
173
146
|
private iterationSystemMsgCount;
|
|
147
|
+
/** Task 1: ContextBudgetManager for LLM-based summarization at 60-70% context usage */
|
|
148
|
+
private contextBudgetManager;
|
|
149
|
+
/** Task 2: Track whether write tools ran this iteration for QA triggering */
|
|
150
|
+
private iterationWriteToolPaths;
|
|
151
|
+
/** Task 2: Last QA result (surfaced to LLM on issues) */
|
|
152
|
+
private lastQAResult;
|
|
153
|
+
/** Task 3: Track TS files modified this run for auto-tsc */
|
|
154
|
+
private iterationTsFilesModified;
|
|
155
|
+
/** Task 3: Whether tsc was run in the previous iteration (skip cooldown) */
|
|
156
|
+
private tscRanLastIteration;
|
|
174
157
|
private tokenUsage;
|
|
175
158
|
private readonly reasoningAggregator;
|
|
176
159
|
private readonly reasoningTree;
|
|
@@ -409,6 +392,11 @@ export declare class AgentLoop extends EventEmitter {
|
|
|
409
392
|
private handleSoftContextOverflow;
|
|
410
393
|
/** MCP 클라이언트 정리 (세션 종료 시 호출) */
|
|
411
394
|
dispose(): Promise<void>;
|
|
395
|
+
/**
|
|
396
|
+
* Builds a Map<filePath, toolOutput> for all changed files from write/edit tool results.
|
|
397
|
+
* Used by selfReflection deepVerify and quickVerify.
|
|
398
|
+
*/
|
|
399
|
+
private buildChangedFilesMap;
|
|
412
400
|
private emitEvent;
|
|
413
401
|
private emitReasoning;
|
|
414
402
|
private emitSubagent;
|
package/dist/agent-loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EACV,WAAW,EAEX,gBAAgB,EAChB,OAAO,EAGP,YAAY,EACZ,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAEL,KAAK,WAAW,EAGjB,MAAM,0BAA0B,CAAC;AAQlC,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EAEpB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAAE,kBAAkB,EAAmB,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,eAAe,EAAyB,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAqB,MAAM,sBAAsB,CAAC;AAGzE,OAAO,EACL,oBAAoB,EACpB,KAAK,kBAAkB,EACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAqB,MAAM,sBAAsB,CAAC;AAGzE,OAAO,EAAE,kBAAkB,EAAwB,MAAM,0BAA0B,CAAC;AAGpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAA0C,MAAM,2BAA2B,CAAC;AACvG,OAAO,EAAE,sBAAsB,EAAwB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"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"}
|
package/dist/agent-loop.js
CHANGED
|
@@ -48,6 +48,8 @@ import { ReasoningTree } from "./reasoning-tree.js";
|
|
|
48
48
|
import { ContextCompressor } from "./context-compressor.js";
|
|
49
49
|
import { DependencyAnalyzer } from "./dependency-analyzer.js";
|
|
50
50
|
import { CrossFileRefactor } from "./cross-file-refactor.js";
|
|
51
|
+
import { ContextBudgetManager } from "./context-budget.js";
|
|
52
|
+
import { QAPipeline } from "./qa-pipeline.js";
|
|
51
53
|
/**
|
|
52
54
|
* AgentLoop — YUAN 에이전트의 핵심 실행 루프.
|
|
53
55
|
*
|
|
@@ -75,6 +77,8 @@ import { CrossFileRefactor } from "./cross-file-refactor.js";
|
|
|
75
77
|
* const result = await loop.run("모든 console.log를 제거해줘");
|
|
76
78
|
* ```
|
|
77
79
|
*/
|
|
80
|
+
/** Minimum confidence for classification-based hints/routing to activate */
|
|
81
|
+
const CLASSIFICATION_CONFIDENCE_THRESHOLD = 0.6;
|
|
78
82
|
export class AgentLoop extends EventEmitter {
|
|
79
83
|
abortSignal;
|
|
80
84
|
llmClient;
|
|
@@ -144,6 +148,16 @@ export class AgentLoop extends EventEmitter {
|
|
|
144
148
|
static MAX_ACTIVE_SKILLS = 3;
|
|
145
149
|
/** Context Budget: track injected system messages per iteration to cap at 5 */
|
|
146
150
|
iterationSystemMsgCount = 0;
|
|
151
|
+
/** Task 1: ContextBudgetManager for LLM-based summarization at 60-70% context usage */
|
|
152
|
+
contextBudgetManager = null;
|
|
153
|
+
/** Task 2: Track whether write tools ran this iteration for QA triggering */
|
|
154
|
+
iterationWriteToolPaths = [];
|
|
155
|
+
/** Task 2: Last QA result (surfaced to LLM on issues) */
|
|
156
|
+
lastQAResult = null;
|
|
157
|
+
/** Task 3: Track TS files modified this run for auto-tsc */
|
|
158
|
+
iterationTsFilesModified = [];
|
|
159
|
+
/** Task 3: Whether tsc was run in the previous iteration (skip cooldown) */
|
|
160
|
+
tscRanLastIteration = false;
|
|
147
161
|
tokenUsage = {
|
|
148
162
|
input: 0,
|
|
149
163
|
output: 0,
|
|
@@ -278,6 +292,12 @@ export class AgentLoop extends EventEmitter {
|
|
|
278
292
|
if (this.initialized)
|
|
279
293
|
return;
|
|
280
294
|
this.initialized = true;
|
|
295
|
+
// Task 1: Initialize ContextBudgetManager with the total token budget
|
|
296
|
+
this.contextBudgetManager = new ContextBudgetManager({
|
|
297
|
+
totalBudget: this.config.loop.totalTokenBudget,
|
|
298
|
+
enableSummarization: true,
|
|
299
|
+
summarizationThreshold: 0.60, // trigger summarize() at 60% (before ContextCompressor at 70%)
|
|
300
|
+
});
|
|
281
301
|
const projectPath = this.config.loop.projectPath;
|
|
282
302
|
if (!projectPath)
|
|
283
303
|
return;
|
|
@@ -463,7 +483,7 @@ export class AgentLoop extends EventEmitter {
|
|
|
463
483
|
const learnedSkills = this.skillLearner.getAllSkills();
|
|
464
484
|
if (learnedSkills.length > 0) {
|
|
465
485
|
const skillNames = learnedSkills
|
|
466
|
-
.filter((s) => s.confidence >=
|
|
486
|
+
.filter((s) => s.confidence >= CLASSIFICATION_CONFIDENCE_THRESHOLD)
|
|
467
487
|
.map((s) => s.id);
|
|
468
488
|
if (skillNames.length > 0) {
|
|
469
489
|
this.contextManager.addMessage({
|
|
@@ -597,7 +617,7 @@ export class AgentLoop extends EventEmitter {
|
|
|
597
617
|
buildMemoryContext(memory) {
|
|
598
618
|
const parts = [];
|
|
599
619
|
// 높은 confidence 학습만 포함
|
|
600
|
-
const highConfLearnings = memory.learnings.filter((l) => l.confidence >=
|
|
620
|
+
const highConfLearnings = memory.learnings.filter((l) => l.confidence >= CLASSIFICATION_CONFIDENCE_THRESHOLD);
|
|
601
621
|
if (highConfLearnings.length > 0) {
|
|
602
622
|
parts.push("## Things I've Learned About This Project");
|
|
603
623
|
for (const l of highConfLearnings.slice(0, 20)) {
|
|
@@ -630,6 +650,8 @@ export class AgentLoop extends EventEmitter {
|
|
|
630
650
|
this.aborted = false;
|
|
631
651
|
this.reasoningAggregator.reset();
|
|
632
652
|
this.reasoningTree.reset();
|
|
653
|
+
// Capture before reset so session snapshot gets accurate file list
|
|
654
|
+
const prevChangedFiles = [...this.changedFiles];
|
|
633
655
|
if (!this.resumedFromSession) {
|
|
634
656
|
this.changedFiles = [];
|
|
635
657
|
this.allToolResults = [];
|
|
@@ -645,6 +667,9 @@ export class AgentLoop extends EventEmitter {
|
|
|
645
667
|
total: 0,
|
|
646
668
|
};
|
|
647
669
|
this.impactHintInjected = false;
|
|
670
|
+
// Task 3: reset tsc tracking per run
|
|
671
|
+
this.iterationTsFilesModified = [];
|
|
672
|
+
this.tscRanLastIteration = false;
|
|
648
673
|
}
|
|
649
674
|
this.checkpointSaved = false;
|
|
650
675
|
this.failureRecovery.reset();
|
|
@@ -677,7 +702,7 @@ export class AgentLoop extends EventEmitter {
|
|
|
677
702
|
snapshot,
|
|
678
703
|
messages: this.contextManager.getMessages(),
|
|
679
704
|
plan: this.activePlan,
|
|
680
|
-
changedFiles:
|
|
705
|
+
changedFiles: prevChangedFiles,
|
|
681
706
|
});
|
|
682
707
|
}
|
|
683
708
|
// 사용자 입력 검증 (prompt injection 방어)
|
|
@@ -717,7 +742,7 @@ export class AgentLoop extends EventEmitter {
|
|
|
717
742
|
}
|
|
718
743
|
// Task 분류 → 시스템 프롬프트에 tool sequence hint 주입
|
|
719
744
|
const classification = this.taskClassifier.classify(userMessage);
|
|
720
|
-
if (classification.confidence >=
|
|
745
|
+
if (classification.confidence >= CLASSIFICATION_CONFIDENCE_THRESHOLD) {
|
|
721
746
|
const classificationHint = this.taskClassifier.formatForSystemPrompt(classification);
|
|
722
747
|
this.contextManager.addMessage({
|
|
723
748
|
role: "system",
|
|
@@ -727,7 +752,7 @@ export class AgentLoop extends EventEmitter {
|
|
|
727
752
|
// Specialist routing: 태스크 타입에 맞는 전문 에이전트 설정 주입
|
|
728
753
|
if (classification.specialistDomain) {
|
|
729
754
|
const specialistMatch = this.specialistRegistry.findSpecialist(classification.specialistDomain);
|
|
730
|
-
if (specialistMatch && specialistMatch.confidence >=
|
|
755
|
+
if (specialistMatch && specialistMatch.confidence >= CLASSIFICATION_CONFIDENCE_THRESHOLD) {
|
|
731
756
|
this.contextManager.addMessage({
|
|
732
757
|
role: "system",
|
|
733
758
|
content: `[Specialist: ${specialistMatch.specialist.name}] ${specialistMatch.specialist.systemPrompt.slice(0, 500)}`,
|
|
@@ -735,13 +760,13 @@ export class AgentLoop extends EventEmitter {
|
|
|
735
760
|
}
|
|
736
761
|
}
|
|
737
762
|
// Tool Planning: 태스크 타입에 맞는 도구 실행 계획 힌트 주입
|
|
738
|
-
if (this.enableToolPlanning && classification.confidence >=
|
|
763
|
+
if (this.enableToolPlanning && classification.confidence >= CLASSIFICATION_CONFIDENCE_THRESHOLD) {
|
|
739
764
|
const planContext = {
|
|
740
765
|
userMessage,
|
|
741
766
|
};
|
|
742
767
|
this.currentToolPlan = this.toolPlanner.planForTask(classification.type, planContext);
|
|
743
768
|
this.executedToolNames = [];
|
|
744
|
-
if (this.currentToolPlan.confidence >=
|
|
769
|
+
if (this.currentToolPlan.confidence >= CLASSIFICATION_CONFIDENCE_THRESHOLD) {
|
|
745
770
|
const planHint = this.toolPlanner.formatPlanHint(this.currentToolPlan);
|
|
746
771
|
this.contextManager.addMessage({
|
|
747
772
|
role: "system",
|
|
@@ -882,6 +907,18 @@ export class AgentLoop extends EventEmitter {
|
|
|
882
907
|
return result;
|
|
883
908
|
}
|
|
884
909
|
catch (err) {
|
|
910
|
+
// Attempt recovery from last checkpoint via ContinuationEngine
|
|
911
|
+
if (this.continuationEngine) {
|
|
912
|
+
try {
|
|
913
|
+
const recovered = await this.continuationEngine.findLatestCheckpoint();
|
|
914
|
+
if (recovered) {
|
|
915
|
+
this.emitReasoning(`recovered from checkpoint at iteration ${recovered.iterationsCompleted ?? "unknown"}`);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
catch {
|
|
919
|
+
// Recovery failure is non-fatal
|
|
920
|
+
}
|
|
921
|
+
}
|
|
885
922
|
return this.handleFatalError(err);
|
|
886
923
|
}
|
|
887
924
|
}
|
|
@@ -1388,10 +1425,48 @@ export class AgentLoop extends EventEmitter {
|
|
|
1388
1425
|
this.iterationCount = iteration;
|
|
1389
1426
|
const iterationStart = Date.now();
|
|
1390
1427
|
this.emitReasoning(`iteration ${iteration}: preparing context`);
|
|
1391
|
-
this.iterationSystemMsgCount = 0; // Reset per-iteration
|
|
1428
|
+
this.iterationSystemMsgCount = 0; // Reset per-iteration (prevents accumulation across iterations)
|
|
1429
|
+
// Policy validation — check cost limits from ExecutionPolicyEngine
|
|
1430
|
+
if (this.policyEngine) {
|
|
1431
|
+
try {
|
|
1432
|
+
const costPolicy = this.policyEngine.get("cost");
|
|
1433
|
+
const iterationTokensUsed = this.tokenUsage.input + this.tokenUsage.output;
|
|
1434
|
+
if (costPolicy.maxTokensPerIteration > 0 && iterationTokensUsed > costPolicy.maxTokensPerIteration) {
|
|
1435
|
+
this.emitReasoning(`policy blocked iteration ${iteration}: token usage ${iterationTokensUsed} exceeds maxTokensPerIteration ${costPolicy.maxTokensPerIteration}`);
|
|
1436
|
+
return { reason: "BUDGET_EXHAUSTED", tokensUsed: iterationTokensUsed };
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
catch {
|
|
1440
|
+
// Policy engine failure is non-fatal
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1392
1443
|
// Soft context rollover:
|
|
1393
1444
|
// checkpoint first, then let ContextManager compact instead of aborting/throwing.
|
|
1394
1445
|
const contextUsageRatio = this.contextManager.getUsageRatio();
|
|
1446
|
+
// Task 1: ContextBudgetManager LLM summarization at 60-70% — runs BEFORE ContextCompressor
|
|
1447
|
+
// Summarizes old "medium" priority conversation turns into a compact summary message,
|
|
1448
|
+
// freeing tokens before the heavier ContextCompressor kicks in at 70%.
|
|
1449
|
+
if (contextUsageRatio >= 0.60 && contextUsageRatio < 0.70 && this.contextBudgetManager) {
|
|
1450
|
+
try {
|
|
1451
|
+
// Sync current messages into the budget manager so it knows what to summarize
|
|
1452
|
+
this.contextBudgetManager.importMessages(this.contextManager.getMessages());
|
|
1453
|
+
if (this.contextBudgetManager.needsSummarization()) {
|
|
1454
|
+
const summary = await this.contextBudgetManager.summarize(async (prompt) => {
|
|
1455
|
+
const resp = await this.llmClient.chat([{ role: "user", content: prompt }], []);
|
|
1456
|
+
return typeof resp.content === "string" ? resp.content : "";
|
|
1457
|
+
});
|
|
1458
|
+
if (summary) {
|
|
1459
|
+
this.emitEvent({
|
|
1460
|
+
kind: "agent:thinking",
|
|
1461
|
+
content: `Context at ${Math.round(contextUsageRatio * 100)}%: summarized ${summary.originalIds.length} old messages (${summary.originalTokens} → ${summary.summarizedTokens} tokens, ${Math.round(summary.compressionRatio * 100)}% ratio).`,
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
catch {
|
|
1467
|
+
// ContextBudgetManager summarization failure is non-fatal
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1395
1470
|
// Bug 5 fix: use ContextCompressor as an alternative when context pressure is high (>70%)
|
|
1396
1471
|
// At 70-84% we apply intelligent priority-based compression before falling back to truncation.
|
|
1397
1472
|
if (contextUsageRatio >= 0.70 && contextUsageRatio < 0.85) {
|
|
@@ -1531,15 +1606,7 @@ export class AgentLoop extends EventEmitter {
|
|
|
1531
1606
|
// Level 2: Deep verification before declaring completion
|
|
1532
1607
|
if (this.selfReflection && this.changedFiles.length > 0) {
|
|
1533
1608
|
try {
|
|
1534
|
-
const changedFilesMap =
|
|
1535
|
-
for (const filePath of this.changedFiles) {
|
|
1536
|
-
const lastWrite = this.allToolResults
|
|
1537
|
-
.filter((r) => r.name === "file_write" || r.name === "file_edit")
|
|
1538
|
-
.find((r) => r.output.includes(filePath));
|
|
1539
|
-
if (lastWrite) {
|
|
1540
|
-
changedFilesMap.set(filePath, lastWrite.output);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1609
|
+
const changedFilesMap = this.buildChangedFilesMap();
|
|
1543
1610
|
const verifyFn = async (prompt) => {
|
|
1544
1611
|
const verifyResponse = await this.llmClient.chat([
|
|
1545
1612
|
{ role: "system", content: "You are a meticulous code reviewer." },
|
|
@@ -1625,6 +1692,50 @@ export class AgentLoop extends EventEmitter {
|
|
|
1625
1692
|
content: finalSummary,
|
|
1626
1693
|
});
|
|
1627
1694
|
}
|
|
1695
|
+
// Task 2: QAPipeline "thorough" mode at final task completion (LLM review included)
|
|
1696
|
+
if (this.changedFiles.length > 0 && this.config.loop.projectPath) {
|
|
1697
|
+
try {
|
|
1698
|
+
const thoroughQA = new QAPipeline({
|
|
1699
|
+
projectPath: this.config.loop.projectPath,
|
|
1700
|
+
level: "thorough",
|
|
1701
|
+
enableStructural: true,
|
|
1702
|
+
enableSemantic: false, // skip tests for speed — structural + quality + review
|
|
1703
|
+
enableQuality: true,
|
|
1704
|
+
enableReview: true,
|
|
1705
|
+
enableDecision: true,
|
|
1706
|
+
autoFix: false,
|
|
1707
|
+
});
|
|
1708
|
+
const thoroughResult = await thoroughQA.run(this.changedFiles, async (prompt) => {
|
|
1709
|
+
try {
|
|
1710
|
+
const reviewResp = await this.llmClient.chat([
|
|
1711
|
+
{ role: "system", content: "You are a code reviewer. Review the code changes concisely." },
|
|
1712
|
+
{ role: "user", content: prompt },
|
|
1713
|
+
], []);
|
|
1714
|
+
return typeof reviewResp.content === "string" ? reviewResp.content : "";
|
|
1715
|
+
}
|
|
1716
|
+
catch {
|
|
1717
|
+
return "";
|
|
1718
|
+
}
|
|
1719
|
+
});
|
|
1720
|
+
const thoroughFailures = thoroughResult.stages
|
|
1721
|
+
.flatMap((s) => s.checks)
|
|
1722
|
+
.filter((c) => c.status === "fail");
|
|
1723
|
+
const thoroughIssues = thoroughFailures
|
|
1724
|
+
.slice(0, 10)
|
|
1725
|
+
.map((c) => `[${c.severity}] ${c.name}: ${c.message}`);
|
|
1726
|
+
// Emit structured qa_result event for TUI display
|
|
1727
|
+
this.emitEvent({
|
|
1728
|
+
kind: "agent:qa_result",
|
|
1729
|
+
stage: "thorough",
|
|
1730
|
+
passed: thoroughFailures.length === 0,
|
|
1731
|
+
issues: thoroughIssues,
|
|
1732
|
+
});
|
|
1733
|
+
this.lastQAResult = thoroughResult;
|
|
1734
|
+
}
|
|
1735
|
+
catch {
|
|
1736
|
+
// Thorough QA failure is non-fatal — proceed with completion
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1628
1739
|
this.emitEvent({
|
|
1629
1740
|
kind: "agent:completed",
|
|
1630
1741
|
summary: finalSummary,
|
|
@@ -1688,6 +1799,105 @@ export class AgentLoop extends EventEmitter {
|
|
|
1688
1799
|
content: fixPrompt,
|
|
1689
1800
|
});
|
|
1690
1801
|
}
|
|
1802
|
+
// Task 2: QAPipeline — run "quick" (structural only) after any WRITE tool call this iteration
|
|
1803
|
+
const projectPath = this.config.loop.projectPath;
|
|
1804
|
+
if (this.iterationWriteToolPaths.length > 0 && projectPath) {
|
|
1805
|
+
try {
|
|
1806
|
+
const qaPipeline = new QAPipeline({
|
|
1807
|
+
projectPath,
|
|
1808
|
+
level: "quick",
|
|
1809
|
+
enableStructural: true,
|
|
1810
|
+
enableSemantic: false,
|
|
1811
|
+
enableQuality: false,
|
|
1812
|
+
enableReview: false,
|
|
1813
|
+
enableDecision: true,
|
|
1814
|
+
autoFix: false,
|
|
1815
|
+
});
|
|
1816
|
+
const qaResult = await qaPipeline.run(this.iterationWriteToolPaths);
|
|
1817
|
+
this.lastQAResult = qaResult;
|
|
1818
|
+
// Surface QA issues as a system message so LLM sees them next iteration
|
|
1819
|
+
const failedChecks = qaResult.stages
|
|
1820
|
+
.flatMap((s) => s.checks)
|
|
1821
|
+
.filter((c) => c.status === "fail" || c.status === "warn");
|
|
1822
|
+
const qaIssues = failedChecks
|
|
1823
|
+
.slice(0, 10)
|
|
1824
|
+
.map((c) => `[${c.severity}] ${c.name}: ${c.message}`);
|
|
1825
|
+
// Emit structured qa_result event for TUI display
|
|
1826
|
+
this.emitEvent({
|
|
1827
|
+
kind: "agent:qa_result",
|
|
1828
|
+
stage: "quick",
|
|
1829
|
+
passed: failedChecks.length === 0,
|
|
1830
|
+
issues: qaIssues,
|
|
1831
|
+
});
|
|
1832
|
+
if (failedChecks.length > 0 && this.iterationSystemMsgCount < 5) {
|
|
1833
|
+
const checkSummary = failedChecks
|
|
1834
|
+
.slice(0, 5)
|
|
1835
|
+
.map((c) => ` - [${c.severity}] ${c.name}: ${c.message}`)
|
|
1836
|
+
.join("\n");
|
|
1837
|
+
this.contextManager.addMessage({
|
|
1838
|
+
role: "system",
|
|
1839
|
+
content: `[QA Quick Check] ${failedChecks.length} issue(s) detected in modified files:\n${checkSummary}`,
|
|
1840
|
+
});
|
|
1841
|
+
this.iterationSystemMsgCount++;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
catch {
|
|
1845
|
+
// QAPipeline failure is non-fatal
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
// Reset per-iteration write tool tracking
|
|
1849
|
+
this.iterationWriteToolPaths = [];
|
|
1850
|
+
// Task 3: Auto-run tsc --noEmit after 2+ TS files modified in this iteration
|
|
1851
|
+
// Skip if tsc was already run in the previous iteration (cooldown)
|
|
1852
|
+
const tscFilesThisIteration = [...this.iterationTsFilesModified];
|
|
1853
|
+
this.iterationTsFilesModified = []; // reset for next iteration
|
|
1854
|
+
const tscRanPrev = this.tscRanLastIteration;
|
|
1855
|
+
this.tscRanLastIteration = false; // will set to true below if we run it
|
|
1856
|
+
if (tscFilesThisIteration.length >= 2 && projectPath && !tscRanPrev) {
|
|
1857
|
+
try {
|
|
1858
|
+
const tscResult = await this.toolExecutor.execute({
|
|
1859
|
+
id: `auto-tsc-${Date.now()}`,
|
|
1860
|
+
name: "shell_exec",
|
|
1861
|
+
arguments: JSON.stringify({
|
|
1862
|
+
command: "npx tsc --noEmit 2>&1 || true",
|
|
1863
|
+
cwd: projectPath,
|
|
1864
|
+
timeout: 60000,
|
|
1865
|
+
}),
|
|
1866
|
+
});
|
|
1867
|
+
this.tscRanLastIteration = true;
|
|
1868
|
+
// Inject TypeScript errors into context so LLM sees them next iteration
|
|
1869
|
+
if (tscResult.success && tscResult.output && tscResult.output.trim().length > 0) {
|
|
1870
|
+
const tscOutput = tscResult.output.trim();
|
|
1871
|
+
// Only inject if there are actual TS errors (output is non-empty)
|
|
1872
|
+
const hasErrors = tscOutput.includes(": error TS") || tscOutput.includes("error TS");
|
|
1873
|
+
if (hasErrors && this.iterationSystemMsgCount < 5) {
|
|
1874
|
+
// Truncate long tsc output to avoid context bloat
|
|
1875
|
+
const truncated = tscOutput.length > 2000
|
|
1876
|
+
? tscOutput.slice(0, 2000) + "\n[...tsc output truncated]"
|
|
1877
|
+
: tscOutput;
|
|
1878
|
+
this.contextManager.addMessage({
|
|
1879
|
+
role: "system",
|
|
1880
|
+
content: `[Auto-TSC] TypeScript errors detected after modifying ${tscFilesThisIteration.length} files:\n\`\`\`\n${truncated}\n\`\`\`\nPlease fix these type errors.`,
|
|
1881
|
+
});
|
|
1882
|
+
this.iterationSystemMsgCount++;
|
|
1883
|
+
this.emitEvent({
|
|
1884
|
+
kind: "agent:thinking",
|
|
1885
|
+
content: `Auto-TSC: TypeScript errors found after editing ${tscFilesThisIteration.join(", ")}.`,
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
else if (!hasErrors) {
|
|
1889
|
+
this.emitEvent({
|
|
1890
|
+
kind: "agent:thinking",
|
|
1891
|
+
content: `Auto-TSC: No type errors after editing ${tscFilesThisIteration.length} file(s).`,
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
catch {
|
|
1897
|
+
// Auto-tsc failure is non-fatal
|
|
1898
|
+
this.tscRanLastIteration = false;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1691
1901
|
// iteration 이벤트
|
|
1692
1902
|
this.emitEvent({
|
|
1693
1903
|
kind: "agent:iteration",
|
|
@@ -1708,20 +1918,41 @@ export class AgentLoop extends EventEmitter {
|
|
|
1708
1918
|
};
|
|
1709
1919
|
await this.sessionPersistence.checkpoint(this.sessionId, checkpoint);
|
|
1710
1920
|
}
|
|
1921
|
+
// ReflexionEngine: reflect on this iteration's tool results
|
|
1922
|
+
if (this.reflexionEngine && toolResults.length > 0) {
|
|
1923
|
+
try {
|
|
1924
|
+
const iterReflection = this.reflexionEngine.reflect({
|
|
1925
|
+
goal: "",
|
|
1926
|
+
runId: `iter-${iteration}-${randomUUID()}`,
|
|
1927
|
+
termination: { reason: "USER_CANCELLED" },
|
|
1928
|
+
toolResults,
|
|
1929
|
+
messages: this.contextManager.getMessages(),
|
|
1930
|
+
tokensUsed: this.tokenUsage.total,
|
|
1931
|
+
durationMs: Date.now() - iterationStart,
|
|
1932
|
+
changedFiles: this.changedFiles,
|
|
1933
|
+
});
|
|
1934
|
+
// Build insight from available reflection fields
|
|
1935
|
+
const insight = iterReflection.reflection.alternativeApproach ??
|
|
1936
|
+
(iterReflection.reflection.whatFailed.length > 0
|
|
1937
|
+
? iterReflection.reflection.whatFailed.slice(0, 2).join("; ")
|
|
1938
|
+
: null);
|
|
1939
|
+
if (insight && insight.length > 10 && this.iterationSystemMsgCount < 5) {
|
|
1940
|
+
this.contextManager.addMessage({
|
|
1941
|
+
role: "system",
|
|
1942
|
+
content: `[Reflection] ${insight}`,
|
|
1943
|
+
});
|
|
1944
|
+
this.iterationSystemMsgCount++;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
catch {
|
|
1948
|
+
// Reflection failure is non-fatal
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1711
1951
|
// Level 1: Quick verification after every 3rd iteration
|
|
1712
1952
|
if (this.selfReflection && iteration % 3 === 0) {
|
|
1713
1953
|
try {
|
|
1714
1954
|
this.emitSubagent("verifier", "start", "running quick verification");
|
|
1715
|
-
const changedFilesMap =
|
|
1716
|
-
for (const filePath of this.changedFiles) {
|
|
1717
|
-
// Collect changed file contents from tool results
|
|
1718
|
-
const lastWrite = this.allToolResults
|
|
1719
|
-
.filter((r) => r.name === "file_write" || r.name === "file_edit")
|
|
1720
|
-
.find((r) => r.output.includes(filePath));
|
|
1721
|
-
if (lastWrite) {
|
|
1722
|
-
changedFilesMap.set(filePath, lastWrite.output);
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1955
|
+
const changedFilesMap = this.buildChangedFilesMap();
|
|
1725
1956
|
const quickResult = await this.selfReflection.quickVerify(changedFilesMap, async (prompt) => {
|
|
1726
1957
|
const verifyResponse = await this.llmClient.chat([
|
|
1727
1958
|
{ role: "system", content: "You are a code verification assistant." },
|
|
@@ -1923,6 +2154,28 @@ export class AgentLoop extends EventEmitter {
|
|
|
1923
2154
|
await this.saveAutoCheckpoint(iteration);
|
|
1924
2155
|
this.checkpointSaved = true;
|
|
1925
2156
|
}
|
|
2157
|
+
// ContinuationEngine: checkpoint current state after each iteration (every 3 iterations, non-fatal)
|
|
2158
|
+
if (this.continuationEngine && iteration > 0 && iteration % 3 === 0 && !this.checkpointSaved) {
|
|
2159
|
+
try {
|
|
2160
|
+
const progress = this.extractProgress();
|
|
2161
|
+
await this.continuationEngine.saveCheckpoint({
|
|
2162
|
+
sessionId: this.sessionId ?? `session-${Date.now()}`,
|
|
2163
|
+
goal: this.contextManager.getMessages().find((m) => m.role === "user")?.content ?? "",
|
|
2164
|
+
progress,
|
|
2165
|
+
changedFiles: [...this.changedFiles].map((p) => ({ path: p, diff: "" })),
|
|
2166
|
+
workingMemory: this.buildWorkingMemorySummary(),
|
|
2167
|
+
yuanMdUpdates: [],
|
|
2168
|
+
errors: [],
|
|
2169
|
+
contextUsageAtSave: this.config.loop.totalTokenBudget > 0 ? this.tokenUsage.total / this.config.loop.totalTokenBudget : 0,
|
|
2170
|
+
totalTokensUsed: this.tokenUsage.total,
|
|
2171
|
+
iterationsCompleted: iteration,
|
|
2172
|
+
createdAt: new Date(),
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
catch {
|
|
2176
|
+
// Checkpoint failure is non-fatal
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
1926
2179
|
// ContinuousReflection: 매 5 iteration마다 비상 체크포인트 트리거
|
|
1927
2180
|
// (정기 타이머 외에 iteration 기반 추가 안전망)
|
|
1928
2181
|
if (this.continuousReflection?.isRunning() &&
|
|
@@ -2241,9 +2494,31 @@ export class AgentLoop extends EventEmitter {
|
|
|
2241
2494
|
if (!this.changedFiles.includes(filePathStr)) {
|
|
2242
2495
|
this.changedFiles.push(filePathStr);
|
|
2243
2496
|
}
|
|
2497
|
+
// Task 2: track write tool paths per-iteration for QA triggering
|
|
2498
|
+
if (!this.iterationWriteToolPaths.includes(filePathStr)) {
|
|
2499
|
+
this.iterationWriteToolPaths.push(filePathStr);
|
|
2500
|
+
}
|
|
2501
|
+
// Task 3: track TS/TSX files modified this iteration for auto-tsc
|
|
2502
|
+
if (filePathStr.match(/\.[cm]?tsx?$/) && !this.iterationTsFilesModified.includes(filePathStr)) {
|
|
2503
|
+
this.iterationTsFilesModified.push(filePathStr);
|
|
2504
|
+
}
|
|
2244
2505
|
this.emitEvent({ kind: "agent:file_change", path: filePathStr, diff: result.output });
|
|
2506
|
+
// Update world state after file modification
|
|
2507
|
+
if (this.config.loop.projectPath) {
|
|
2508
|
+
const wsProjectPath = this.config.loop.projectPath;
|
|
2509
|
+
new WorldStateCollector({ projectPath: wsProjectPath, skipTest: true })
|
|
2510
|
+
.collect()
|
|
2511
|
+
.then((snapshot) => { this.worldState = snapshot; })
|
|
2512
|
+
.catch(() => {
|
|
2513
|
+
// Non-fatal: world state update failure should not interrupt tool execution
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2245
2516
|
if (this.impactAnalyzer) {
|
|
2246
|
-
this.analyzeFileImpact(filePathStr).catch(() => {
|
|
2517
|
+
this.analyzeFileImpact(filePathStr).catch((err) => {
|
|
2518
|
+
// Non-fatal: impact analysis failure should not interrupt tool execution
|
|
2519
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2520
|
+
console.warn(`[AgentLoop] impact analysis skipped for ${filePathStr}: ${msg}`);
|
|
2521
|
+
});
|
|
2247
2522
|
}
|
|
2248
2523
|
}
|
|
2249
2524
|
const fixPrompt = await this.validateAndFeedback(toolCall.name, result);
|
|
@@ -2832,6 +3107,22 @@ export class AgentLoop extends EventEmitter {
|
|
|
2832
3107
|
}
|
|
2833
3108
|
}
|
|
2834
3109
|
// ─── Helpers ───
|
|
3110
|
+
/**
|
|
3111
|
+
* Builds a Map<filePath, toolOutput> for all changed files from write/edit tool results.
|
|
3112
|
+
* Used by selfReflection deepVerify and quickVerify.
|
|
3113
|
+
*/
|
|
3114
|
+
buildChangedFilesMap() {
|
|
3115
|
+
const map = new Map();
|
|
3116
|
+
for (const filePath of this.changedFiles) {
|
|
3117
|
+
const lastWrite = this.allToolResults
|
|
3118
|
+
.filter((r) => r.name === "file_write" || r.name === "file_edit")
|
|
3119
|
+
.find((r) => r.output.includes(filePath));
|
|
3120
|
+
if (lastWrite) {
|
|
3121
|
+
map.set(filePath, lastWrite.output);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
return map;
|
|
3125
|
+
}
|
|
2835
3126
|
emitEvent(event) {
|
|
2836
3127
|
this.emit("event", event);
|
|
2837
3128
|
}
|