@yuaone/core 0.9.43 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-affordance.d.ts +37 -0
- package/dist/agent-affordance.d.ts.map +1 -0
- package/dist/agent-affordance.js +139 -0
- package/dist/agent-affordance.js.map +1 -0
- package/dist/agent-decision-types.d.ts +262 -0
- package/dist/agent-decision-types.d.ts.map +1 -0
- package/dist/agent-decision-types.js +19 -0
- package/dist/agent-decision-types.js.map +1 -0
- package/dist/agent-decision.d.ts +52 -0
- package/dist/agent-decision.d.ts.map +1 -0
- package/dist/agent-decision.js +767 -0
- package/dist/agent-decision.js.map +1 -0
- package/dist/agent-loop.d.ts +37 -79
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +730 -586
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent-reasoning-engine.d.ts +48 -0
- package/dist/agent-reasoning-engine.d.ts.map +1 -0
- package/dist/agent-reasoning-engine.js +544 -0
- package/dist/agent-reasoning-engine.js.map +1 -0
- package/dist/codebase-context.d.ts +3 -0
- package/dist/codebase-context.d.ts.map +1 -1
- package/dist/codebase-context.js +15 -6
- package/dist/codebase-context.js.map +1 -1
- package/dist/command-plan-compiler.d.ts +43 -0
- package/dist/command-plan-compiler.d.ts.map +1 -0
- package/dist/command-plan-compiler.js +164 -0
- package/dist/command-plan-compiler.js.map +1 -0
- package/dist/dependency-guard.d.ts +18 -0
- package/dist/dependency-guard.d.ts.map +1 -0
- package/dist/dependency-guard.js +113 -0
- package/dist/dependency-guard.js.map +1 -0
- package/dist/execution-engine.d.ts +10 -1
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/execution-engine.js +162 -8
- package/dist/execution-engine.js.map +1 -1
- package/dist/execution-receipt.d.ts +62 -0
- package/dist/execution-receipt.d.ts.map +1 -0
- package/dist/execution-receipt.js +67 -0
- package/dist/execution-receipt.js.map +1 -0
- package/dist/failure-surface-writer.d.ts +13 -0
- package/dist/failure-surface-writer.d.ts.map +1 -0
- package/dist/failure-surface-writer.js +33 -0
- package/dist/failure-surface-writer.js.map +1 -0
- package/dist/file-chunker.d.ts +26 -0
- package/dist/file-chunker.d.ts.map +1 -0
- package/dist/file-chunker.js +103 -0
- package/dist/file-chunker.js.map +1 -0
- package/dist/image-observer.d.ts +22 -0
- package/dist/image-observer.d.ts.map +1 -0
- package/dist/image-observer.js +60 -0
- package/dist/image-observer.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -1
- package/dist/judgment-rules.d.ts +44 -0
- package/dist/judgment-rules.d.ts.map +1 -0
- package/dist/judgment-rules.js +185 -0
- package/dist/judgment-rules.js.map +1 -0
- package/dist/memory-decay.d.ts +41 -0
- package/dist/memory-decay.d.ts.map +1 -0
- package/dist/memory-decay.js +62 -0
- package/dist/memory-decay.js.map +1 -0
- package/dist/memory-manager.d.ts.map +1 -1
- package/dist/memory-manager.js +30 -0
- package/dist/memory-manager.js.map +1 -1
- package/dist/model-weakness-tracker.d.ts +42 -0
- package/dist/model-weakness-tracker.d.ts.map +1 -0
- package/dist/model-weakness-tracker.js +107 -0
- package/dist/model-weakness-tracker.js.map +1 -0
- package/dist/overhead-governor.d.ts +3 -1
- package/dist/overhead-governor.d.ts.map +1 -1
- package/dist/overhead-governor.js +5 -0
- package/dist/overhead-governor.js.map +1 -1
- package/dist/patch-scope-controller.d.ts +44 -0
- package/dist/patch-scope-controller.d.ts.map +1 -0
- package/dist/patch-scope-controller.js +107 -0
- package/dist/patch-scope-controller.js.map +1 -0
- package/dist/patch-transaction.d.ts +53 -0
- package/dist/patch-transaction.d.ts.map +1 -0
- package/dist/patch-transaction.js +119 -0
- package/dist/patch-transaction.js.map +1 -0
- package/dist/pre-write-validator.d.ts +29 -0
- package/dist/pre-write-validator.d.ts.map +1 -0
- package/dist/pre-write-validator.js +97 -0
- package/dist/pre-write-validator.js.map +1 -0
- package/dist/prompt-builder.d.ts +25 -0
- package/dist/prompt-builder.d.ts.map +1 -0
- package/dist/prompt-builder.js +93 -0
- package/dist/prompt-builder.js.map +1 -0
- package/dist/prompt-envelope.d.ts +40 -0
- package/dist/prompt-envelope.d.ts.map +1 -0
- package/dist/prompt-envelope.js +16 -0
- package/dist/prompt-envelope.js.map +1 -0
- package/dist/prompt-runtime.d.ts +66 -0
- package/dist/prompt-runtime.d.ts.map +1 -0
- package/dist/prompt-runtime.js +492 -0
- package/dist/prompt-runtime.js.map +1 -0
- package/dist/repo-capability-profile.d.ts +24 -0
- package/dist/repo-capability-profile.d.ts.map +1 -0
- package/dist/repo-capability-profile.js +113 -0
- package/dist/repo-capability-profile.js.map +1 -0
- package/dist/security-gate.d.ts +39 -0
- package/dist/security-gate.d.ts.map +1 -0
- package/dist/security-gate.js +121 -0
- package/dist/security-gate.js.map +1 -0
- package/dist/self-evaluation.d.ts +22 -0
- package/dist/self-evaluation.d.ts.map +1 -0
- package/dist/self-evaluation.js +43 -0
- package/dist/self-evaluation.js.map +1 -0
- package/dist/semantic-diff-reviewer.d.ts +28 -0
- package/dist/semantic-diff-reviewer.d.ts.map +1 -0
- package/dist/semantic-diff-reviewer.js +168 -0
- package/dist/semantic-diff-reviewer.js.map +1 -0
- package/dist/stall-detector.d.ts +26 -2
- package/dist/stall-detector.d.ts.map +1 -1
- package/dist/stall-detector.js +128 -3
- package/dist/stall-detector.js.map +1 -1
- package/dist/system-core.d.ts +27 -0
- package/dist/system-core.d.ts.map +1 -0
- package/dist/system-core.js +269 -0
- package/dist/system-core.js.map +1 -0
- package/dist/system-prompt.d.ts +4 -0
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +12 -218
- package/dist/system-prompt.js.map +1 -1
- package/dist/target-file-ranker.d.ts +38 -0
- package/dist/target-file-ranker.d.ts.map +1 -0
- package/dist/target-file-ranker.js +90 -0
- package/dist/target-file-ranker.js.map +1 -0
- package/dist/task-classifier.d.ts +6 -0
- package/dist/task-classifier.d.ts.map +1 -1
- package/dist/task-classifier.js +6 -0
- package/dist/task-classifier.js.map +1 -1
- package/dist/test-impact-planner.d.ts +16 -0
- package/dist/test-impact-planner.d.ts.map +1 -0
- package/dist/test-impact-planner.js +68 -0
- package/dist/test-impact-planner.js.map +1 -0
- package/dist/tool-outcome-cache.d.ts +41 -0
- package/dist/tool-outcome-cache.d.ts.map +1 -0
- package/dist/tool-outcome-cache.js +88 -0
- package/dist/tool-outcome-cache.js.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/verifier-rules.d.ts +15 -0
- package/dist/verifier-rules.d.ts.map +1 -0
- package/dist/verifier-rules.js +80 -0
- package/dist/verifier-rules.js.map +1 -0
- package/dist/workspace-mutation-policy.d.ts +28 -0
- package/dist/workspace-mutation-policy.d.ts.map +1 -0
- package/dist/workspace-mutation-policy.js +56 -0
- package/dist/workspace-mutation-policy.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module agent-decision
|
|
3
|
+
* @description Main Agent Decision Orchestrator.
|
|
4
|
+
* Single entry point: `agentDecide()` — called once per user message.
|
|
5
|
+
* Produces an immutable, deep-frozen `AgentDecisionContext` (SSOT).
|
|
6
|
+
*
|
|
7
|
+
* Pure function (except `crypto.randomUUID()` for session ID in meta).
|
|
8
|
+
* NO LLM calls, NO async.
|
|
9
|
+
*
|
|
10
|
+
* Design spec: docs/superpowers/specs/2026-03-17-yuan-agent-decision-engine-design.md, section 6
|
|
11
|
+
*/
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
13
|
+
import { agentReason } from "./agent-reasoning-engine.js";
|
|
14
|
+
import { computeAgentAffordance, applyStuckBreaker } from "./agent-affordance.js";
|
|
15
|
+
// ─── Utilities ───
|
|
16
|
+
/** Clamp a number to [0, 1]. */
|
|
17
|
+
function clamp01(x) {
|
|
18
|
+
return Math.max(0, Math.min(1, x));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Deep freeze utility — recursively freezes nested objects/arrays (GPT QA #1).
|
|
22
|
+
* Returns the same object reference, frozen in place.
|
|
23
|
+
*/
|
|
24
|
+
function deepFreeze(obj) {
|
|
25
|
+
Object.freeze(obj);
|
|
26
|
+
for (const val of Object.values(obj)) {
|
|
27
|
+
if (val && typeof val === "object" && !Object.isFrozen(val)) {
|
|
28
|
+
deepFreeze(val);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return obj;
|
|
32
|
+
}
|
|
33
|
+
// ─── ComputePolicy (§6.2) ───
|
|
34
|
+
const POLICY_TABLE = {
|
|
35
|
+
trivial: { maxIterations: 3, maxTokenBudget: 8_000, modelTier: "fast", parallelAgents: 0 },
|
|
36
|
+
simple: { maxIterations: 8, maxTokenBudget: 20_000, modelTier: "fast", parallelAgents: 0 },
|
|
37
|
+
moderate: { maxIterations: 15, maxTokenBudget: 50_000, modelTier: "standard", parallelAgents: 1 },
|
|
38
|
+
complex: { maxIterations: 30, maxTokenBudget: 100_000, modelTier: "standard", parallelAgents: 2 },
|
|
39
|
+
massive: { maxIterations: 50, maxTokenBudget: 200_000, modelTier: "deep", parallelAgents: 3 },
|
|
40
|
+
};
|
|
41
|
+
function deriveComputePolicy(reasoning) {
|
|
42
|
+
const { complexity, depthHint } = reasoning;
|
|
43
|
+
const base = POLICY_TABLE[complexity];
|
|
44
|
+
// Deep hint override: promote fast → standard when deep analysis requested
|
|
45
|
+
if (depthHint === "deep" && base.modelTier === "fast") {
|
|
46
|
+
return { ...base, modelTier: "standard" };
|
|
47
|
+
}
|
|
48
|
+
return base;
|
|
49
|
+
}
|
|
50
|
+
// ─── FailureSurface (§6.4) ───
|
|
51
|
+
/**
|
|
52
|
+
* Risk balance formula from YUA pipeline-lite: stability/risk-balance.ts
|
|
53
|
+
* Adjusts risk threshold based on FP/FN cost asymmetry.
|
|
54
|
+
* For coding: FP cost (unnecessary caution) = 0.3, FN cost (missed bug) = 0.7
|
|
55
|
+
*/
|
|
56
|
+
function computeRiskBalance(fpCost, fnCost, tau) {
|
|
57
|
+
return (tau + fpCost) / fnCost;
|
|
58
|
+
}
|
|
59
|
+
const RISK_BALANCE = computeRiskBalance(0.3, 0.7, 0.5); // ~1.14 — slightly risk-averse
|
|
60
|
+
const COMPLEXITY_RISK = {
|
|
61
|
+
trivial: 0, simple: 0.1, moderate: 0.2, complex: 0.3, massive: 0.5,
|
|
62
|
+
};
|
|
63
|
+
const BLAST_RADIUS = {
|
|
64
|
+
trivial: 0.05, simple: 0.1, moderate: 0.25, complex: 0.5, massive: 0.8,
|
|
65
|
+
};
|
|
66
|
+
function estimateFailureSurface(reasoning, projectContext) {
|
|
67
|
+
const { intent, complexity, taskStage, confidence } = reasoning;
|
|
68
|
+
// Patch risk: edit/refactor/fix intents carry higher base risk
|
|
69
|
+
// Risk balance (YUA FSLE): FP/FN cost-adjusted risk multiplier
|
|
70
|
+
const isModifyIntent = intent === "edit" || intent === "refactor" || intent === "fix";
|
|
71
|
+
let patchRisk = clamp01(((isModifyIntent ? 0.3 : 0.1) + COMPLEXITY_RISK[complexity]) * RISK_BALANCE);
|
|
72
|
+
// High codebase complexity → patch risk bump
|
|
73
|
+
if (projectContext?.avgCyclomaticComplexity && projectContext.avgCyclomaticComplexity > 15) {
|
|
74
|
+
patchRisk = clamp01(patchRisk + 0.1);
|
|
75
|
+
}
|
|
76
|
+
if (projectContext?.maxFileComplexity && projectContext.maxFileComplexity > 50) {
|
|
77
|
+
patchRisk = clamp01(patchRisk + 0.1);
|
|
78
|
+
}
|
|
79
|
+
// Build risk: higher for compiled languages + existing build errors
|
|
80
|
+
const isCompiled = projectContext?.language !== "python" && projectContext?.language !== "shell";
|
|
81
|
+
let buildRisk = clamp01(patchRisk * (isCompiled ? 1.2 : 0.5));
|
|
82
|
+
if (projectContext?.hasBuildErrors)
|
|
83
|
+
buildRisk = clamp01(buildRisk + 0.2);
|
|
84
|
+
// Test risk: higher when project has no tests or has recent failures
|
|
85
|
+
let testRisk = clamp01(patchRisk * (projectContext?.hasTests ? 0.6 : 1.3));
|
|
86
|
+
if (projectContext?.recentFailureCount && projectContext.recentFailureCount > 0) {
|
|
87
|
+
testRisk = clamp01(testRisk + 0.15);
|
|
88
|
+
}
|
|
89
|
+
// Blast radius: complexity-based + dirty tree bump
|
|
90
|
+
let blastRadius = clamp01(BLAST_RADIUS[complexity]);
|
|
91
|
+
if (projectContext?.dirtyWorkingTree)
|
|
92
|
+
blastRadius = clamp01(blastRadius + 0.1);
|
|
93
|
+
// Ambiguity risk: underspecified + low confidence + missing deps + conflicts
|
|
94
|
+
let ambiguityRisk = clamp01((taskStage === "underspecified" ? 0.5 : 0.1) + (1 - confidence) * 0.3);
|
|
95
|
+
if (projectContext?.missingDeps)
|
|
96
|
+
ambiguityRisk = clamp01(ambiguityRisk + 0.15);
|
|
97
|
+
if (projectContext?.hasConflicts)
|
|
98
|
+
ambiguityRisk = clamp01(ambiguityRisk + 0.2);
|
|
99
|
+
return { patchRisk, buildRisk, testRisk, blastRadius, ambiguityRisk };
|
|
100
|
+
}
|
|
101
|
+
// ─── VetoFlags (GPT QA #6) ───
|
|
102
|
+
function computeVetoFlags(surface, planRequired) {
|
|
103
|
+
return {
|
|
104
|
+
editVetoed: surface.patchRisk >= 0.8 && !planRequired,
|
|
105
|
+
verifyRequired: surface.blastRadius >= 0.6,
|
|
106
|
+
clarifyForced: surface.ambiguityRisk >= 0.7,
|
|
107
|
+
finalizeBlocked: surface.buildRisk >= 0.7,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const BASE_BUDGET = {
|
|
111
|
+
trivial: { maxFileReads: 3, maxEdits: 2, maxShellExecs: 1, maxTestRuns: 1, maxSearches: 3, maxWebLookups: 0, shellCostUnits: 1, maxSameFileEdits: 2 },
|
|
112
|
+
simple: { maxFileReads: 8, maxEdits: 4, maxShellExecs: 3, maxTestRuns: 2, maxSearches: 8, maxWebLookups: 1, shellCostUnits: 2, maxSameFileEdits: 3 },
|
|
113
|
+
moderate: { maxFileReads: 15, maxEdits: 8, maxShellExecs: 5, maxTestRuns: 3, maxSearches: 15, maxWebLookups: 2, shellCostUnits: 3, maxSameFileEdits: 4 },
|
|
114
|
+
complex: { maxFileReads: 30, maxEdits: 15, maxShellExecs: 10, maxTestRuns: 5, maxSearches: 30, maxWebLookups: 3, shellCostUnits: 5, maxSameFileEdits: 5 },
|
|
115
|
+
massive: { maxFileReads: 60, maxEdits: 30, maxShellExecs: 20, maxTestRuns: 8, maxSearches: 60, maxWebLookups: 5, shellCostUnits: 8, maxSameFileEdits: 6 },
|
|
116
|
+
};
|
|
117
|
+
function deriveToolBudget(reasoning, affordance) {
|
|
118
|
+
const { complexity } = reasoning;
|
|
119
|
+
const budget = { ...BASE_BUDGET[complexity] };
|
|
120
|
+
// Affordance-based adjustments
|
|
121
|
+
if (affordance.inspect_more > 0.7) {
|
|
122
|
+
budget.maxFileReads = Math.ceil(budget.maxFileReads * 1.5);
|
|
123
|
+
budget.maxSearches = Math.ceil(budget.maxSearches * 1.5);
|
|
124
|
+
}
|
|
125
|
+
if (affordance.run_checks > 0.7) {
|
|
126
|
+
budget.maxTestRuns = Math.ceil(budget.maxTestRuns * 1.5);
|
|
127
|
+
budget.maxShellExecs = Math.ceil(budget.maxShellExecs * 1.3);
|
|
128
|
+
}
|
|
129
|
+
if (affordance.edit_now < 0.3) {
|
|
130
|
+
budget.maxEdits = Math.ceil(budget.maxEdits * 0.5);
|
|
131
|
+
}
|
|
132
|
+
return budget;
|
|
133
|
+
}
|
|
134
|
+
// ─── MemoryIntent (§13.3) ───
|
|
135
|
+
function deriveMemoryIntent(reasoning) {
|
|
136
|
+
const { intent, complexity } = reasoning;
|
|
137
|
+
// Simple lookups don't need memory saves
|
|
138
|
+
if (intent === "inspect" || intent === "read" || intent === "search") {
|
|
139
|
+
return { shouldSave: false, categories: [], priority: "low", target: "local" };
|
|
140
|
+
}
|
|
141
|
+
const categories = [];
|
|
142
|
+
// Modify intents → tool_pattern + file_structure
|
|
143
|
+
if (intent === "edit" || intent === "refactor" || intent === "fix") {
|
|
144
|
+
categories.push("tool_pattern", "file_structure");
|
|
145
|
+
}
|
|
146
|
+
// Fix → error_pattern + failure_avoidance
|
|
147
|
+
if (intent === "fix") {
|
|
148
|
+
categories.push("error_pattern", "failure_avoidance");
|
|
149
|
+
}
|
|
150
|
+
// Test/verify → project_rule
|
|
151
|
+
if (intent === "test" || intent === "verify") {
|
|
152
|
+
categories.push("project_rule");
|
|
153
|
+
}
|
|
154
|
+
const priority = complexity === "massive" || complexity === "complex" ? "high" : "normal";
|
|
155
|
+
return {
|
|
156
|
+
shouldSave: categories.length > 0,
|
|
157
|
+
categories,
|
|
158
|
+
priority,
|
|
159
|
+
target: "local",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// ─── Task Continuation (§14.3) ───
|
|
163
|
+
function detectTaskContinuation(message, prevDecision) {
|
|
164
|
+
if (!prevDecision) {
|
|
165
|
+
return {
|
|
166
|
+
isContinuation: false,
|
|
167
|
+
continuityScore: 0,
|
|
168
|
+
carryover: { modifiedFiles: [], failedAttempts: [] },
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
let score = 0;
|
|
172
|
+
// Reference to previous context
|
|
173
|
+
if (/(아까|이전|위에|방금|earlier|previous|that file|same)/i.test(message)) {
|
|
174
|
+
score += 0.3;
|
|
175
|
+
}
|
|
176
|
+
// Short continuation commands
|
|
177
|
+
if (/^(계속|다음|진행|ㄱㄱ|go|continue|next|이어서)$/i.test(message.trim())) {
|
|
178
|
+
score += 0.5;
|
|
179
|
+
}
|
|
180
|
+
// Retry/correction requests
|
|
181
|
+
if (/(다시|다르게|수정|고쳐|retry|again|differently)/i.test(message)) {
|
|
182
|
+
score += 0.4;
|
|
183
|
+
}
|
|
184
|
+
// Iterating stage from previous decision
|
|
185
|
+
if (prevDecision.core.reasoning.taskStage === "iterating") {
|
|
186
|
+
score += 0.2;
|
|
187
|
+
}
|
|
188
|
+
const isContinuation = score >= 0.4;
|
|
189
|
+
return {
|
|
190
|
+
isContinuation,
|
|
191
|
+
continuityScore: clamp01(score),
|
|
192
|
+
carryover: {
|
|
193
|
+
modifiedFiles: [],
|
|
194
|
+
failedAttempts: [],
|
|
195
|
+
prevIntent: prevDecision.core.reasoning.intent,
|
|
196
|
+
prevStage: prevDecision.core.reasoning.taskStage,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// ─── MicroPlan (GPT QA #17) ───
|
|
201
|
+
function deriveMicroPlan(anchors) {
|
|
202
|
+
return anchors.map((anchor) => {
|
|
203
|
+
switch (anchor) {
|
|
204
|
+
case "SEARCH_REPO": return "Search the codebase for relevant files";
|
|
205
|
+
case "READ_FILES": return "Read and understand the target files";
|
|
206
|
+
case "PREPARE_PATCH": return "Prepare the code changes";
|
|
207
|
+
case "RUN_TESTS": return "Run tests to verify changes";
|
|
208
|
+
case "VERIFY_RESULT": return "Verify build and type-check pass";
|
|
209
|
+
case "SUMMARIZE_CHANGE": return "Summarize what was changed";
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// ─── InteractionMode (Dual-Mode) ───
|
|
214
|
+
/**
|
|
215
|
+
* Derive the interaction mode from reasoning result.
|
|
216
|
+
* Deterministic: same reasoning always yields the same mode.
|
|
217
|
+
*
|
|
218
|
+
* Rules:
|
|
219
|
+
* - CHAT: trivial + explore intents (inspect/read/search), OR underspecified
|
|
220
|
+
* - AGENT: complex/massive, OR plan/refactor intent, OR fix+moderate+
|
|
221
|
+
* - HYBRID: moderate edit/fix/test/verify, OR simple edit/fix
|
|
222
|
+
* - Default: CHAT
|
|
223
|
+
*/
|
|
224
|
+
export function deriveInteractionMode(reasoning) {
|
|
225
|
+
const { intent, complexity, taskStage } = reasoning;
|
|
226
|
+
// CHAT: trivial complexity with explore-type intents
|
|
227
|
+
if (complexity === "trivial" && (intent === "inspect" || intent === "read" || intent === "search")) {
|
|
228
|
+
return "CHAT";
|
|
229
|
+
}
|
|
230
|
+
// CHAT: underspecified → clarification-only, no execution
|
|
231
|
+
if (taskStage === "underspecified") {
|
|
232
|
+
return "CHAT";
|
|
233
|
+
}
|
|
234
|
+
// AGENT: complex/massive complexity
|
|
235
|
+
if (complexity === "complex" || complexity === "massive") {
|
|
236
|
+
return "AGENT";
|
|
237
|
+
}
|
|
238
|
+
// AGENT: plan/refactor intents always need full agent
|
|
239
|
+
if (intent === "plan" || intent === "refactor") {
|
|
240
|
+
return "AGENT";
|
|
241
|
+
}
|
|
242
|
+
// AGENT: fix intent at moderate+ complexity
|
|
243
|
+
if (intent === "fix" && complexity !== "simple") {
|
|
244
|
+
return "AGENT";
|
|
245
|
+
}
|
|
246
|
+
// HYBRID: moderate complexity with any intent
|
|
247
|
+
if (complexity === "moderate") {
|
|
248
|
+
return "HYBRID";
|
|
249
|
+
}
|
|
250
|
+
// HYBRID: simple edit/fix/test/verify (verify is still useful)
|
|
251
|
+
if (intent === "edit" || intent === "fix" || intent === "test" || intent === "verify") {
|
|
252
|
+
return "HYBRID";
|
|
253
|
+
}
|
|
254
|
+
// Default: CHAT
|
|
255
|
+
return "CHAT";
|
|
256
|
+
}
|
|
257
|
+
// ─── SubAgent Plan (Phase I SSOT) ───
|
|
258
|
+
function deriveSubAgentPlan(reasoning) {
|
|
259
|
+
const { intent, complexity } = reasoning;
|
|
260
|
+
// Only enable for complex+ tasks
|
|
261
|
+
if (complexity === "trivial" || complexity === "simple" || complexity === "moderate") {
|
|
262
|
+
return { enabled: false, maxAgents: 0, roles: [], strategy: "sequential" };
|
|
263
|
+
}
|
|
264
|
+
// complex/massive: intent-based role selection
|
|
265
|
+
const roleMap = {
|
|
266
|
+
fix: ["debugger", "coder", "tester"],
|
|
267
|
+
refactor: ["planner", "refactorer", "tester"],
|
|
268
|
+
edit: ["coder", "reviewer"],
|
|
269
|
+
test: ["tester", "coder"],
|
|
270
|
+
plan: ["planner"],
|
|
271
|
+
verify: ["tester", "reviewer"],
|
|
272
|
+
};
|
|
273
|
+
const roles = roleMap[intent] ?? ["coder"];
|
|
274
|
+
const maxAgents = complexity === "massive" ? 3 : 2;
|
|
275
|
+
const strategy = complexity === "massive" ? "parallel" : "sequential";
|
|
276
|
+
return { enabled: true, maxAgents, roles, strategy };
|
|
277
|
+
}
|
|
278
|
+
// ─── Persona Hint (Phase I+ SSOT) ───
|
|
279
|
+
function derivePersonaHint(message) {
|
|
280
|
+
// Detect language
|
|
281
|
+
const hasKorean = /[가-힣]/.test(message);
|
|
282
|
+
const language = hasKorean ? "ko" : "auto";
|
|
283
|
+
// Detect tone from message patterns
|
|
284
|
+
const isCasual = /ㄱㄱ|ㅋㅋ|ㅎㅎ|ㅇㅇ|해줘|고쳐|봐봐|알려줘/i.test(message);
|
|
285
|
+
const isTechnical = /architecture|설계|구조|리팩토링|타입|인터페이스/i.test(message);
|
|
286
|
+
const tone = isCasual ? "casual" : isTechnical ? "technical" : "professional";
|
|
287
|
+
return { tone, language };
|
|
288
|
+
}
|
|
289
|
+
// ─── Memory Load (Phase I+ SSOT) ───
|
|
290
|
+
function deriveMemoryLoad(reasoning) {
|
|
291
|
+
// Trivial tasks don't need memory
|
|
292
|
+
if (reasoning.complexity === "trivial") {
|
|
293
|
+
return { shouldLoad: false, categories: [] };
|
|
294
|
+
}
|
|
295
|
+
// Exploration: load file_structure + project_rule
|
|
296
|
+
if (reasoning.intent === "inspect" || reasoning.intent === "read" || reasoning.intent === "search") {
|
|
297
|
+
return { shouldLoad: true, categories: ["file_structure", "project_rule"] };
|
|
298
|
+
}
|
|
299
|
+
// Modify: load everything relevant
|
|
300
|
+
if (reasoning.intent === "edit" || reasoning.intent === "refactor" || reasoning.intent === "fix") {
|
|
301
|
+
return { shouldLoad: true, categories: ["tool_pattern", "error_pattern", "project_rule", "file_structure"] };
|
|
302
|
+
}
|
|
303
|
+
// Default: load basics
|
|
304
|
+
return { shouldLoad: true, categories: ["project_rule"] };
|
|
305
|
+
}
|
|
306
|
+
// ─── Skill Activation (Phase I SSOT) ───
|
|
307
|
+
function deriveSkillActivation(reasoning) {
|
|
308
|
+
const { intent, complexity } = reasoning;
|
|
309
|
+
const isModify = intent === "edit" || intent === "refactor" || intent === "fix";
|
|
310
|
+
return {
|
|
311
|
+
enableToolPlanning: isModify && complexity !== "trivial",
|
|
312
|
+
enableSkillLearning: complexity !== "trivial",
|
|
313
|
+
enablePlugins: true, // always on, runtime matching
|
|
314
|
+
enableSpecialist: isModify && complexity !== "trivial" && complexity !== "simple",
|
|
315
|
+
specialistDomain: undefined, // could infer from reasoning, MVP = undefined
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// ─── Code Quality Policy ───
|
|
319
|
+
function deriveCodeQualityPolicy(reasoning) {
|
|
320
|
+
const { intent, complexity } = reasoning;
|
|
321
|
+
// Not a code task
|
|
322
|
+
if (intent === "inspect" || intent === "read" || intent === "search") {
|
|
323
|
+
return { isCodeTask: false, codeTaskType: "none", primaryRisk: "none", constraints: [], strictMode: false, preEditVerify: false };
|
|
324
|
+
}
|
|
325
|
+
// Map intent → code task type
|
|
326
|
+
const taskTypeMap = {
|
|
327
|
+
edit: "generation",
|
|
328
|
+
fix: "fix",
|
|
329
|
+
refactor: "refactor",
|
|
330
|
+
test: "test",
|
|
331
|
+
verify: "review",
|
|
332
|
+
plan: "none",
|
|
333
|
+
};
|
|
334
|
+
const codeTaskType = taskTypeMap[intent] ?? "none";
|
|
335
|
+
// Map task type → primary risk
|
|
336
|
+
const riskMap = {
|
|
337
|
+
generation: "extension_pain",
|
|
338
|
+
fix: "async_race",
|
|
339
|
+
refactor: "state_corruption",
|
|
340
|
+
review: "state_corruption",
|
|
341
|
+
test: "type_safety",
|
|
342
|
+
none: "none",
|
|
343
|
+
};
|
|
344
|
+
const primaryRisk = riskMap[codeTaskType] ?? "none";
|
|
345
|
+
// Constraints based on task type
|
|
346
|
+
const constraints = [];
|
|
347
|
+
if (codeTaskType === "generation") {
|
|
348
|
+
constraints.push("Write complete, production-ready code. No TODO, FIXME, placeholder, stub, or empty implementations.", "If requirements are unclear, state assumptions explicitly then implement fully based on those assumptions.", "Do NOT ask clarifying questions mid-implementation. Make reasonable assumptions and proceed.", "Each file must be a complete, working module. No partial implementations.");
|
|
349
|
+
}
|
|
350
|
+
if (codeTaskType === "fix") {
|
|
351
|
+
constraints.push("Reproduce the issue mentally first. Identify root cause before writing any fix.", "Apply the minimal correct fix. Do not refactor unrelated code.", "Verify the fix does not introduce new issues (type-check, edge cases).");
|
|
352
|
+
}
|
|
353
|
+
if (codeTaskType === "refactor") {
|
|
354
|
+
constraints.push("Do NOT change behavior. Only restructure.", "Verify all existing tests still pass after refactoring.", "Prefer smaller, incremental refactors over large rewrites.");
|
|
355
|
+
}
|
|
356
|
+
if (codeTaskType === "test") {
|
|
357
|
+
constraints.push("Test behavior, not implementation details.", "Cover edge cases and failure paths.", "Tests must be deterministic — no random data, no timing dependencies.");
|
|
358
|
+
}
|
|
359
|
+
if (codeTaskType === "review") {
|
|
360
|
+
constraints.push("Do NOT modify code. Only analyze and report.", "Cite specific file and line numbers for each issue found.", "Prioritize: correctness → security → performance → readability.");
|
|
361
|
+
}
|
|
362
|
+
// Complexity-based additions
|
|
363
|
+
if (complexity === "complex" || complexity === "massive") {
|
|
364
|
+
constraints.push("Break the implementation into clearly separated modules/functions.", "Add JSDoc comments for public APIs only (not for internal helpers).");
|
|
365
|
+
}
|
|
366
|
+
// Risk-based additions
|
|
367
|
+
if (primaryRisk === "extension_pain") {
|
|
368
|
+
constraints.push("Design for extension: use interfaces/types at boundaries. Future requirements should not require rewriting core logic.");
|
|
369
|
+
}
|
|
370
|
+
if (primaryRisk === "type_safety") {
|
|
371
|
+
constraints.push("Never use 'any'. Use proper TypeScript types. Validate at system boundaries.");
|
|
372
|
+
}
|
|
373
|
+
if (primaryRisk === "async_race") {
|
|
374
|
+
constraints.push("Check for race conditions in async code. Ensure proper error handling in Promise chains.");
|
|
375
|
+
}
|
|
376
|
+
if (primaryRisk === "state_corruption") {
|
|
377
|
+
constraints.push("Verify state mutations are atomic and predictable. No hidden side effects.");
|
|
378
|
+
}
|
|
379
|
+
const strictMode = codeTaskType !== "none" && codeTaskType !== "review";
|
|
380
|
+
const preEditVerify = complexity !== "trivial" && complexity !== "simple" && codeTaskType !== "none";
|
|
381
|
+
return { isCodeTask: codeTaskType !== "none", codeTaskType, primaryRisk, constraints, strictMode, preEditVerify };
|
|
382
|
+
}
|
|
383
|
+
// ─── LeadHint ───
|
|
384
|
+
function deriveLeadHint(reasoning, mode) {
|
|
385
|
+
if (mode === "CHAT")
|
|
386
|
+
return "NONE";
|
|
387
|
+
if (mode === "AGENT")
|
|
388
|
+
return "HARD";
|
|
389
|
+
// HYBRID: depends on intent
|
|
390
|
+
if (reasoning.intent === "plan" || reasoning.intent === "refactor")
|
|
391
|
+
return "HARD";
|
|
392
|
+
if (reasoning.intent === "fix" || reasoning.intent === "edit")
|
|
393
|
+
return "SOFT";
|
|
394
|
+
return "NONE";
|
|
395
|
+
}
|
|
396
|
+
// ─── ResponseHint ───
|
|
397
|
+
function deriveResponseHint(reasoning, mode) {
|
|
398
|
+
const { intent, complexity } = reasoning;
|
|
399
|
+
// Structure based on intent
|
|
400
|
+
const structureMap = {
|
|
401
|
+
inspect: "direct_answer",
|
|
402
|
+
read: "direct_answer",
|
|
403
|
+
search: "direct_answer",
|
|
404
|
+
fix: "problem_solution",
|
|
405
|
+
plan: "stepwise_explanation",
|
|
406
|
+
refactor: "stepwise_explanation",
|
|
407
|
+
edit: "code_first",
|
|
408
|
+
test: "code_first",
|
|
409
|
+
verify: "direct_answer",
|
|
410
|
+
};
|
|
411
|
+
// Expansion based on mode + complexity
|
|
412
|
+
const expansion = mode === "CHAT" ? "soft"
|
|
413
|
+
: complexity === "trivial" ? "none"
|
|
414
|
+
: complexity === "massive" ? "full"
|
|
415
|
+
: "guided";
|
|
416
|
+
return {
|
|
417
|
+
structure: structureMap[intent] ?? "direct_answer",
|
|
418
|
+
expansion,
|
|
419
|
+
forbid: {
|
|
420
|
+
metaComment: mode !== "CHAT",
|
|
421
|
+
narration: false, // coding agent needs narration for tool calls
|
|
422
|
+
apology: true, // never apologize
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
// ─── ToolGate ───
|
|
427
|
+
function deriveToolGate(reasoning, vetoFlags) {
|
|
428
|
+
const { intent, taskStage } = reasoning;
|
|
429
|
+
// Read-only for inspect/read/search/review
|
|
430
|
+
if (["inspect", "read", "search"].includes(intent) || vetoFlags.editVetoed) {
|
|
431
|
+
return { level: "READ_ONLY", blockedTools: ["file_write", "file_edit", "git_ops"], verifierBudget: 0 };
|
|
432
|
+
}
|
|
433
|
+
// Limited for underspecified
|
|
434
|
+
if (taskStage === "underspecified") {
|
|
435
|
+
return { level: "LIMITED", blockedTools: ["file_write", "git_ops"], verifierBudget: 3 };
|
|
436
|
+
}
|
|
437
|
+
// Full for everything else
|
|
438
|
+
return { level: "FULL", blockedTools: [], verifierBudget: 10 };
|
|
439
|
+
}
|
|
440
|
+
// ─── ResponsePressure ───
|
|
441
|
+
function deriveResponsePressure(reasoning, affordance, mode, codeQuality) {
|
|
442
|
+
// Code generation → always assertive
|
|
443
|
+
if (codeQuality.isCodeTask && codeQuality.strictMode) {
|
|
444
|
+
return { pressure: "ASSERTIVE", momentum: "HIGH" };
|
|
445
|
+
}
|
|
446
|
+
// CHAT mode → gentle
|
|
447
|
+
if (mode === "CHAT") {
|
|
448
|
+
return { pressure: "GENTLE", momentum: "LOW" };
|
|
449
|
+
}
|
|
450
|
+
// Underspecified → gentle
|
|
451
|
+
if (reasoning.taskStage === "underspecified") {
|
|
452
|
+
return { pressure: "GENTLE", momentum: "LOW" };
|
|
453
|
+
}
|
|
454
|
+
// Compute momentum from affordance
|
|
455
|
+
const momentumScore = affordance.edit_now * 0.5 + affordance.run_checks * 0.3 + (1 - affordance.finalize) * 0.2;
|
|
456
|
+
const momentum = momentumScore > 0.65 ? "HIGH" : momentumScore > 0.35 ? "MEDIUM" : "LOW";
|
|
457
|
+
const pressure = momentum === "HIGH" ? "ASSERTIVE" : "NEUTRAL";
|
|
458
|
+
return { pressure, momentum };
|
|
459
|
+
}
|
|
460
|
+
// ─── ContinuityCapsule ───
|
|
461
|
+
function deriveContinuityCapsule(continuation) {
|
|
462
|
+
if (!continuation.isContinuation) {
|
|
463
|
+
return { enabled: false, rules: [] };
|
|
464
|
+
}
|
|
465
|
+
const rules = [
|
|
466
|
+
"This is a continuation of a previous task. Do not start over.",
|
|
467
|
+
"Do not ask 'what would you like me to do?' — the task is already defined.",
|
|
468
|
+
];
|
|
469
|
+
if (continuation.carryover.prevIntent) {
|
|
470
|
+
rules.push(`Previous intent was: ${continuation.carryover.prevIntent}. Continue in that direction.`);
|
|
471
|
+
}
|
|
472
|
+
if (continuation.carryover.modifiedFiles.length > 0) {
|
|
473
|
+
rules.push(`Previously modified files: ${continuation.carryover.modifiedFiles.join(", ")}. Build on these changes.`);
|
|
474
|
+
}
|
|
475
|
+
return { enabled: true, rules };
|
|
476
|
+
}
|
|
477
|
+
// ─── StyleHint ───
|
|
478
|
+
function deriveStyleHint(message) {
|
|
479
|
+
const hasKorean = /[가-힣]/.test(message);
|
|
480
|
+
const hasEnglish = /[a-zA-Z]{3,}/.test(message);
|
|
481
|
+
const hasSlang = /ㅋㅋ|ㅎㅎ|ㅇㅇ|ㄱㄱ|ㅅㅂ|lol|lmao|haha/i.test(message);
|
|
482
|
+
const isFormal = /please|부탁|감사|요청|확인 부탁/i.test(message);
|
|
483
|
+
const isShort = message.trim().length < 20;
|
|
484
|
+
return {
|
|
485
|
+
formality: hasSlang ? "casual" : isFormal ? "formal" : "neutral",
|
|
486
|
+
language: hasKorean && hasEnglish ? "mixed" : hasKorean ? "ko" : "en",
|
|
487
|
+
brevity: isShort ? "terse" : "normal",
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
// ─── Main Orchestrator ───
|
|
491
|
+
/**
|
|
492
|
+
* Single entry point for the Agent Decision Engine.
|
|
493
|
+
* Called once per user message. Produces an immutable `AgentDecisionContext`.
|
|
494
|
+
*
|
|
495
|
+
* Pipeline:
|
|
496
|
+
* agentReason → computeAgentAffordance → deriveComputePolicy →
|
|
497
|
+
* estimateFailureSurface → computeVetoFlags → deriveToolBudget →
|
|
498
|
+
* deriveMemoryIntent → detectTaskContinuation → deriveMicroPlan → deepFreeze
|
|
499
|
+
*
|
|
500
|
+
* Supports recoveryHint (GPT QA #8): if provided, adjusts the reasoning stage.
|
|
501
|
+
*/
|
|
502
|
+
export function agentDecide(input) {
|
|
503
|
+
const { message, projectContext, prevDecision, recoveryHint } = input;
|
|
504
|
+
// Step 1: Reasoning (pure heuristic, NO LLM)
|
|
505
|
+
const reasoning = agentReason(message, projectContext);
|
|
506
|
+
// Apply recovery hint stage override if present (GPT QA #18)
|
|
507
|
+
if (recoveryHint?.stageHint) {
|
|
508
|
+
reasoning.taskStage = recoveryHint.stageHint;
|
|
509
|
+
}
|
|
510
|
+
// Step 2: Affordance (pure math) + stuck breaker
|
|
511
|
+
let repeatCount = 0;
|
|
512
|
+
if (prevDecision) {
|
|
513
|
+
const prev = prevDecision.core.reasoning;
|
|
514
|
+
if (prev.intent === reasoning.intent && prev.taskStage === reasoning.taskStage) {
|
|
515
|
+
repeatCount = prevDecision.meta.repeatCount + 1;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
let affordance = computeAgentAffordance(reasoning, prevDecision?.core.affordance);
|
|
519
|
+
if (repeatCount >= 3) {
|
|
520
|
+
affordance = applyStuckBreaker(affordance, repeatCount);
|
|
521
|
+
}
|
|
522
|
+
// Step 3: Compute Policy (complexity → budget table)
|
|
523
|
+
const computePolicy = deriveComputePolicy(reasoning);
|
|
524
|
+
// Step 4: Failure Surface (intent × complexity × project context)
|
|
525
|
+
const failureSurface = estimateFailureSurface(reasoning, projectContext);
|
|
526
|
+
// Step 5: Tool Budget (complexity × affordance)
|
|
527
|
+
const toolBudget = deriveToolBudget(reasoning, affordance);
|
|
528
|
+
// Step 6: Plan required (GPT QA #4 — not just complexity, also intent/stage/surface)
|
|
529
|
+
const planRequired = reasoning.complexity === "complex" || reasoning.complexity === "massive" ||
|
|
530
|
+
reasoning.intent === "plan" || reasoning.intent === "refactor" ||
|
|
531
|
+
reasoning.taskStage === "underspecified" || reasoning.taskStage === "blocked" ||
|
|
532
|
+
failureSurface.blastRadius >= 0.6 ||
|
|
533
|
+
(affordance.run_checks > 0.7 && reasoning.complexity !== "trivial");
|
|
534
|
+
// Update veto with final planRequired
|
|
535
|
+
const vetoFlags = computeVetoFlags(failureSurface, planRequired);
|
|
536
|
+
// Step 8: Next action (GPT QA #5 — clarify path)
|
|
537
|
+
let nextAction = "proceed";
|
|
538
|
+
let clarification;
|
|
539
|
+
if (vetoFlags.clarifyForced || (reasoning.taskStage === "underspecified" && reasoning.confidence < 0.4)) {
|
|
540
|
+
nextAction = "ask_user";
|
|
541
|
+
clarification = {
|
|
542
|
+
reason: `Task is ${reasoning.taskStage} with confidence ${reasoning.confidence}`,
|
|
543
|
+
missingFields: reasoning.taskStage === "underspecified"
|
|
544
|
+
? ["specific files", "expected behavior"]
|
|
545
|
+
: [],
|
|
546
|
+
suggestedOptions: [],
|
|
547
|
+
allowProceedWithAssumptions: reasoning.confidence > 0.25,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
// Blocked stage → blocked_external (QA #10)
|
|
551
|
+
if (reasoning.taskStage === "blocked" && nextAction === "proceed") {
|
|
552
|
+
nextAction = "blocked_external";
|
|
553
|
+
}
|
|
554
|
+
// Recovery hint may force ask_user
|
|
555
|
+
if (recoveryHint?.action === "ask_user") {
|
|
556
|
+
nextAction = "ask_user";
|
|
557
|
+
if (!clarification) {
|
|
558
|
+
clarification = {
|
|
559
|
+
reason: recoveryHint.reason,
|
|
560
|
+
missingFields: [],
|
|
561
|
+
suggestedOptions: [],
|
|
562
|
+
allowProceedWithAssumptions: false,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// Step 9: Execution strategy
|
|
567
|
+
const scanBreadth = reasoning.complexity === "massive" ? "wide"
|
|
568
|
+
: reasoning.complexity === "complex" ? "normal"
|
|
569
|
+
: "narrow";
|
|
570
|
+
// Step 10: Verify depth (GPT QA #14 — finalize guard)
|
|
571
|
+
let verifyDepth = affordance.run_checks > 0.6 ? "thorough"
|
|
572
|
+
: affordance.run_checks > 0.3 ? "quick"
|
|
573
|
+
: "skip";
|
|
574
|
+
// verifyRequired veto: minimum quick
|
|
575
|
+
if (vetoFlags.verifyRequired && verifyDepth === "skip") {
|
|
576
|
+
verifyDepth = "quick";
|
|
577
|
+
}
|
|
578
|
+
// Affordance finalize guard (GPT QA #14): verify not run → cap finalize
|
|
579
|
+
if (verifyDepth !== "skip") {
|
|
580
|
+
affordance.finalize = Math.min(affordance.finalize, 0.25);
|
|
581
|
+
}
|
|
582
|
+
if (vetoFlags.finalizeBlocked) {
|
|
583
|
+
affordance.finalize = Math.min(affordance.finalize, 0.1);
|
|
584
|
+
}
|
|
585
|
+
// Step 11: Continuation detection
|
|
586
|
+
const continuation = detectTaskContinuation(message, prevDecision);
|
|
587
|
+
// Continuation adjustments (§14.4)
|
|
588
|
+
if (continuation.isContinuation) {
|
|
589
|
+
affordance.inspect_more = clamp01(affordance.inspect_more * 0.5);
|
|
590
|
+
affordance.edit_now = clamp01(affordance.edit_now * 1.3);
|
|
591
|
+
affordance.explain_plan = clamp01(affordance.explain_plan * 0.3);
|
|
592
|
+
// Previous failures increase patch risk
|
|
593
|
+
if (continuation.carryover.failedAttempts.length > 0) {
|
|
594
|
+
failureSurface.patchRisk = clamp01(failureSurface.patchRisk * 1.3);
|
|
595
|
+
// Recompute veto flags after failureSurface mutation (QA fix #2)
|
|
596
|
+
Object.assign(vetoFlags, computeVetoFlags(failureSurface, planRequired));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// Step 12: Memory intent
|
|
600
|
+
const memoryIntent = deriveMemoryIntent(reasoning);
|
|
601
|
+
// Step 13: MicroPlan (GPT QA #13 — disabled for ask_user/blocked_external)
|
|
602
|
+
const resolvedAction = nextAction;
|
|
603
|
+
const microPlan = resolvedAction === "proceed"
|
|
604
|
+
? deriveMicroPlan(reasoning.nextAnchors)
|
|
605
|
+
: undefined;
|
|
606
|
+
// Step 14: InteractionMode (Dual-Mode)
|
|
607
|
+
const interactionMode = deriveInteractionMode(reasoning);
|
|
608
|
+
// Step 15: SubAgent Plan & Skill Activation (Phase I SSOT)
|
|
609
|
+
const subAgentPlan = deriveSubAgentPlan(reasoning);
|
|
610
|
+
const skillActivation = deriveSkillActivation(reasoning);
|
|
611
|
+
// Step 15b: Persona Hint & Memory Load (Phase I+ SSOT)
|
|
612
|
+
const personaHint = derivePersonaHint(message);
|
|
613
|
+
const memoryLoad = deriveMemoryLoad(reasoning);
|
|
614
|
+
// Step 15c: Code Quality Policy (deterministic, no LLM)
|
|
615
|
+
const codeQuality = deriveCodeQualityPolicy(reasoning);
|
|
616
|
+
// Step 15d: LeadHint, ResponseHint, ToolGate, ResponsePressure, ContinuityCapsule, StyleHint
|
|
617
|
+
const leadHint = deriveLeadHint(reasoning, interactionMode);
|
|
618
|
+
const responseHint = deriveResponseHint(reasoning, interactionMode);
|
|
619
|
+
const toolGate = deriveToolGate(reasoning, vetoFlags);
|
|
620
|
+
const pressureDecision = deriveResponsePressure(reasoning, affordance, interactionMode, codeQuality);
|
|
621
|
+
const continuityCapsule = deriveContinuityCapsule(continuation);
|
|
622
|
+
const styleHint = deriveStyleHint(message);
|
|
623
|
+
// Step 16: Assemble core (GPT QA #2 — core vs meta separation)
|
|
624
|
+
const core = {
|
|
625
|
+
reasoning,
|
|
626
|
+
affordance,
|
|
627
|
+
computePolicy,
|
|
628
|
+
failureSurface,
|
|
629
|
+
vetoFlags,
|
|
630
|
+
toolBudget,
|
|
631
|
+
nextAction,
|
|
632
|
+
clarification,
|
|
633
|
+
planRequired,
|
|
634
|
+
scanBreadth,
|
|
635
|
+
verifyDepth,
|
|
636
|
+
memoryIntent,
|
|
637
|
+
continuation,
|
|
638
|
+
microPlan,
|
|
639
|
+
interactionMode,
|
|
640
|
+
subAgentPlan,
|
|
641
|
+
skillActivation,
|
|
642
|
+
personaHint,
|
|
643
|
+
memoryLoad,
|
|
644
|
+
codeQuality,
|
|
645
|
+
leadHint,
|
|
646
|
+
responseHint,
|
|
647
|
+
toolGate,
|
|
648
|
+
pressureDecision,
|
|
649
|
+
continuityCapsule,
|
|
650
|
+
styleHint,
|
|
651
|
+
};
|
|
652
|
+
// Step 17: Deep freeze and return (GPT QA #1)
|
|
653
|
+
return deepFreeze({
|
|
654
|
+
core,
|
|
655
|
+
meta: {
|
|
656
|
+
sessionId: randomUUID(),
|
|
657
|
+
createdAt: Date.now(),
|
|
658
|
+
repeatCount,
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
// ─── DEFAULT_DECISION ───
|
|
663
|
+
/**
|
|
664
|
+
* Safe default decision — used when agentDecide() fails or is skipped.
|
|
665
|
+
* Trivial/CHAT/proceed = most conservative, safest behavior.
|
|
666
|
+
* This ensures Decision is NEVER null in the pipeline.
|
|
667
|
+
*/
|
|
668
|
+
export const DEFAULT_DECISION = deepFreeze({
|
|
669
|
+
core: {
|
|
670
|
+
reasoning: {
|
|
671
|
+
intent: "inspect",
|
|
672
|
+
taskStage: "ready",
|
|
673
|
+
complexity: "simple",
|
|
674
|
+
confidence: 0.5,
|
|
675
|
+
depthHint: "shallow",
|
|
676
|
+
cognitiveLoad: "low",
|
|
677
|
+
nextAnchors: ["SEARCH_REPO", "READ_FILES"],
|
|
678
|
+
},
|
|
679
|
+
affordance: {
|
|
680
|
+
explain_plan: 0.3,
|
|
681
|
+
inspect_more: 0.5,
|
|
682
|
+
edit_now: 0.3,
|
|
683
|
+
run_checks: 0.3,
|
|
684
|
+
finalize: 0.3,
|
|
685
|
+
},
|
|
686
|
+
computePolicy: {
|
|
687
|
+
maxIterations: 15,
|
|
688
|
+
maxTokenBudget: 50_000,
|
|
689
|
+
modelTier: "standard",
|
|
690
|
+
parallelAgents: 0,
|
|
691
|
+
},
|
|
692
|
+
failureSurface: {
|
|
693
|
+
patchRisk: 0.1,
|
|
694
|
+
buildRisk: 0.1,
|
|
695
|
+
testRisk: 0.1,
|
|
696
|
+
blastRadius: 0.1,
|
|
697
|
+
ambiguityRisk: 0.2,
|
|
698
|
+
},
|
|
699
|
+
vetoFlags: {
|
|
700
|
+
editVetoed: false,
|
|
701
|
+
verifyRequired: false,
|
|
702
|
+
clarifyForced: false,
|
|
703
|
+
finalizeBlocked: false,
|
|
704
|
+
},
|
|
705
|
+
toolBudget: {
|
|
706
|
+
maxFileReads: 8,
|
|
707
|
+
maxEdits: 4,
|
|
708
|
+
maxShellExecs: 3,
|
|
709
|
+
maxTestRuns: 2,
|
|
710
|
+
maxSearches: 8,
|
|
711
|
+
maxWebLookups: 1,
|
|
712
|
+
shellCostUnits: 10,
|
|
713
|
+
maxSameFileEdits: 5,
|
|
714
|
+
},
|
|
715
|
+
nextAction: "proceed",
|
|
716
|
+
clarification: undefined,
|
|
717
|
+
planRequired: false,
|
|
718
|
+
scanBreadth: "narrow",
|
|
719
|
+
verifyDepth: "skip",
|
|
720
|
+
memoryIntent: { shouldSave: false, categories: [], priority: "low", target: "local" },
|
|
721
|
+
continuation: { isContinuation: false, continuityScore: 0, carryover: { modifiedFiles: [], failedAttempts: [] } },
|
|
722
|
+
microPlan: undefined,
|
|
723
|
+
interactionMode: "CHAT",
|
|
724
|
+
subAgentPlan: { enabled: false, maxAgents: 0, roles: [], strategy: "sequential" },
|
|
725
|
+
skillActivation: { enableToolPlanning: false, enableSkillLearning: true, enablePlugins: true, enableSpecialist: false, specialistDomain: undefined },
|
|
726
|
+
personaHint: { tone: "casual", language: "auto" },
|
|
727
|
+
memoryLoad: { shouldLoad: true, categories: ["project_rule"] },
|
|
728
|
+
codeQuality: { isCodeTask: false, codeTaskType: "none", primaryRisk: "none", constraints: [], strictMode: false, preEditVerify: false },
|
|
729
|
+
leadHint: "NONE",
|
|
730
|
+
responseHint: { structure: "direct_answer", expansion: "soft", forbid: { metaComment: false, narration: false, apology: true } },
|
|
731
|
+
toolGate: { level: "FULL", blockedTools: [], verifierBudget: 10 },
|
|
732
|
+
pressureDecision: { pressure: "NEUTRAL", momentum: "LOW" },
|
|
733
|
+
continuityCapsule: { enabled: false, rules: [] },
|
|
734
|
+
styleHint: { formality: "neutral", language: "en", brevity: "normal" },
|
|
735
|
+
},
|
|
736
|
+
meta: {
|
|
737
|
+
sessionId: "default",
|
|
738
|
+
createdAt: 0,
|
|
739
|
+
repeatCount: 0,
|
|
740
|
+
},
|
|
741
|
+
});
|
|
742
|
+
// ─── WorldState → ProjectContext Converter ───
|
|
743
|
+
/**
|
|
744
|
+
* Convert a WorldStateSnapshot into AgentProjectContext for Decision Engine input.
|
|
745
|
+
* This bridges the gap between runtime world state and decision-time project context.
|
|
746
|
+
*/
|
|
747
|
+
export function worldStateToProjectContext(ws) {
|
|
748
|
+
return {
|
|
749
|
+
hasTests: ws.test.testRunner !== "unknown",
|
|
750
|
+
fileCount: ws.files.totalFiles,
|
|
751
|
+
language: "typescript",
|
|
752
|
+
repoIndexed: true,
|
|
753
|
+
monorepo: false,
|
|
754
|
+
packageManager: ws.deps.packageManager !== "unknown" ? ws.deps.packageManager : undefined,
|
|
755
|
+
testFrameworkPresent: ws.test.testRunner !== "unknown",
|
|
756
|
+
dirtyWorkingTree: ws.git.uncommittedFiles.length > 0,
|
|
757
|
+
currentBranchProtected: ws.git.branch === "main" || ws.git.branch === "master",
|
|
758
|
+
recentFailureCount: ws.test.failingTests.length + ws.errors.recentRuntimeErrors.length,
|
|
759
|
+
changedFilesCount: ws.files.recentlyChanged.length,
|
|
760
|
+
hasBuildErrors: ws.build.lastResult === "fail",
|
|
761
|
+
hasConflicts: ws.git.hasConflicts,
|
|
762
|
+
missingDeps: ws.deps.missing.length > 0,
|
|
763
|
+
buildTool: ws.build.buildTool !== "unknown" ? ws.build.buildTool : undefined,
|
|
764
|
+
testRunner: ws.test.testRunner !== "unknown" ? ws.test.testRunner : undefined,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
//# sourceMappingURL=agent-decision.js.map
|