@tienne/gestalt 0.25.1 → 0.26.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/package.json +7 -2
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts.map +1 -1
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/code-graph/engine.d.ts +5 -0
- package/dist/src/code-graph/engine.d.ts.map +1 -1
- package/dist/src/code-graph/engine.js +15 -2
- package/dist/src/code-graph/engine.js.map +1 -1
- package/dist/src/code-graph/plugins/java.js +1 -1
- package/dist/src/code-graph/plugins/java.js.map +1 -1
- package/dist/src/code-graph/storage.d.ts.map +1 -1
- package/dist/src/code-graph/storage.js +12 -2
- package/dist/src/code-graph/storage.js.map +1 -1
- package/dist/src/core/config.js +1 -1
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/errors.d.ts.map +1 -1
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/events/store.d.ts +21 -2
- package/dist/src/events/store.d.ts.map +1 -1
- package/dist/src/events/store.js +141 -20
- package/dist/src/events/store.js.map +1 -1
- package/dist/src/execute/orchestrators/evaluation.d.ts.map +1 -1
- package/dist/src/execute/orchestrators/evaluation.js +1 -1
- package/dist/src/execute/orchestrators/evaluation.js.map +1 -1
- package/dist/src/execute/repository.d.ts +2 -2
- package/dist/src/execute/repository.d.ts.map +1 -1
- package/dist/src/execute/repository.js.map +1 -1
- package/dist/src/execute/session.d.ts +2 -2
- package/dist/src/execute/session.d.ts.map +1 -1
- package/dist/src/execute/session.js.map +1 -1
- package/dist/src/gestalt/analyzer.d.ts.map +1 -1
- package/dist/src/gestalt/analyzer.js.map +1 -1
- package/dist/src/interview/passthrough-engine.d.ts +1 -1
- package/dist/src/interview/passthrough-engine.d.ts.map +1 -1
- package/dist/src/interview/passthrough-engine.js +4 -1
- package/dist/src/interview/passthrough-engine.js.map +1 -1
- package/dist/src/interview/repository.d.ts +2 -2
- package/dist/src/interview/repository.d.ts.map +1 -1
- package/dist/src/interview/repository.js.map +1 -1
- package/dist/src/interview/session.d.ts +2 -2
- package/dist/src/interview/session.d.ts.map +1 -1
- package/dist/src/interview/session.js.map +1 -1
- package/dist/src/knowledge-base/embedding-index.d.ts.map +1 -1
- package/dist/src/knowledge-base/embedding-index.js.map +1 -1
- package/dist/src/knowledge-base/generator.d.ts.map +1 -1
- package/dist/src/knowledge-base/generator.js +1 -11
- package/dist/src/knowledge-base/generator.js.map +1 -1
- package/dist/src/mcp/schemas.d.ts +10 -10
- package/dist/src/mcp/tools/execute/evaluate.d.ts +5 -0
- package/dist/src/mcp/tools/execute/evaluate.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/evaluate.js +133 -0
- package/dist/src/mcp/tools/execute/evaluate.js.map +1 -0
- package/dist/src/mcp/tools/execute/evolve.d.ts +10 -0
- package/dist/src/mcp/tools/execute/evolve.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/evolve.js +273 -0
- package/dist/src/mcp/tools/execute/evolve.js.map +1 -0
- package/dist/src/mcp/tools/execute/execution.d.ts +6 -0
- package/dist/src/mcp/tools/execute/execution.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/execution.js +145 -0
- package/dist/src/mcp/tools/execute/execution.js.map +1 -0
- package/dist/src/mcp/tools/execute/planning.d.ts +7 -0
- package/dist/src/mcp/tools/execute/planning.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/planning.js +138 -0
- package/dist/src/mcp/tools/execute/planning.js.map +1 -0
- package/dist/src/mcp/tools/execute/roles.d.ts +6 -0
- package/dist/src/mcp/tools/execute/roles.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/roles.js +73 -0
- package/dist/src/mcp/tools/execute/roles.js.map +1 -0
- package/dist/src/mcp/tools/execute/types.d.ts +5 -0
- package/dist/src/mcp/tools/execute/types.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/types.js +2 -0
- package/dist/src/mcp/tools/execute/types.js.map +1 -0
- package/dist/src/mcp/tools/execute/utility.d.ts +8 -0
- package/dist/src/mcp/tools/execute/utility.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/utility.js +206 -0
- package/dist/src/mcp/tools/execute/utility.js.map +1 -0
- package/dist/src/mcp/tools/execute/utils.d.ts +5 -0
- package/dist/src/mcp/tools/execute/utils.d.ts.map +1 -0
- package/dist/src/mcp/tools/execute/utils.js +44 -0
- package/dist/src/mcp/tools/execute/utils.js.map +1 -0
- package/dist/src/mcp/tools/execute-passthrough.d.ts +1 -1
- package/dist/src/mcp/tools/execute-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute-passthrough.js +31 -997
- package/dist/src/mcp/tools/execute-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/generate-kb.d.ts.map +1 -1
- package/dist/src/mcp/tools/generate-kb.js +1 -1
- package/dist/src/mcp/tools/generate-kb.js.map +1 -1
- package/dist/src/mcp/tools/interview-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/interview-passthrough.js +7 -1
- package/dist/src/mcp/tools/interview-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/spec-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/spec-passthrough.js +6 -2
- package/dist/src/mcp/tools/spec-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/status.js.map +1 -1
- package/dist/src/registry/base-registry.d.ts.map +1 -1
- package/dist/src/registry/base-registry.js +16 -3
- package/dist/src/registry/base-registry.js.map +1 -1
- package/dist/src/review/context-collector.d.ts +4 -3
- package/dist/src/review/context-collector.d.ts.map +1 -1
- package/dist/src/review/context-collector.js +22 -5
- package/dist/src/review/context-collector.js.map +1 -1
- package/dist/src/review/passthrough-engine.d.ts.map +1 -1
- package/dist/src/review/passthrough-engine.js +1 -1
- package/dist/src/review/passthrough-engine.js.map +1 -1
- package/package.json +7 -2
|
@@ -1,1000 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { handleStart, handlePlanStep, handlePlanComplete } from './execute/planning.js';
|
|
2
|
+
import { handleExecuteStart, handleExecuteTask } from './execute/execution.js';
|
|
3
|
+
import { handleEvaluate } from './execute/evaluate.js';
|
|
4
|
+
import { handleEvolveFix, handleEvolve, handleEvolvePatch, handleEvolveReExecute, handleEvolveLateral, handleEvolveLateralResult, } from './execute/evolve.js';
|
|
5
|
+
import { handleRoleMatch, handleRoleConsensus } from './execute/roles.js';
|
|
6
|
+
import { handleStatusAction, handleResume, handleAudit, handleSpawn } from './execute/utility.js';
|
|
7
|
+
import { formatError } from './execute/utils.js';
|
|
8
|
+
const handlers = {
|
|
9
|
+
start: handleStart,
|
|
10
|
+
plan_step: handlePlanStep,
|
|
11
|
+
plan_complete: handlePlanComplete,
|
|
12
|
+
execute_start: handleExecuteStart,
|
|
13
|
+
execute_task: handleExecuteTask,
|
|
14
|
+
evaluate: handleEvaluate,
|
|
15
|
+
status: handleStatusAction,
|
|
16
|
+
resume: handleResume,
|
|
17
|
+
audit: handleAudit,
|
|
18
|
+
spawn: handleSpawn,
|
|
19
|
+
evolve_fix: handleEvolveFix,
|
|
20
|
+
evolve: handleEvolve,
|
|
21
|
+
evolve_patch: handleEvolvePatch,
|
|
22
|
+
evolve_re_execute: handleEvolveReExecute,
|
|
23
|
+
role_match: handleRoleMatch,
|
|
24
|
+
role_consensus: handleRoleConsensus,
|
|
25
|
+
evolve_lateral: handleEvolveLateral,
|
|
26
|
+
evolve_lateral_result: handleEvolveLateralResult,
|
|
27
|
+
};
|
|
8
28
|
export async function handleExecutePassthrough(engine, input, client = 'claude-code') {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return formatError('spec is required for start action');
|
|
14
|
-
const result = engine.start(input.spec, {
|
|
15
|
-
codeGraphRepoRoot: input.codeGraphRepoRoot,
|
|
16
|
-
});
|
|
17
|
-
if (!result.ok)
|
|
18
|
-
return formatError(result.error.message);
|
|
19
|
-
const { session, executeContext } = result.value;
|
|
20
|
-
const startGuide = {
|
|
21
|
-
nextAction: 'plan_step',
|
|
22
|
-
nextActionParams: { sessionId: session.sessionId },
|
|
23
|
-
hint: 'executeContext.planningPrompt를 사용해 figure_ground 분류를 수행하세요.',
|
|
24
|
-
};
|
|
25
|
-
return JSON.stringify({
|
|
26
|
-
status: 'started',
|
|
27
|
-
sessionId: session.sessionId,
|
|
28
|
-
specId: session.specId,
|
|
29
|
-
executeContext: verbose
|
|
30
|
-
? executeContext
|
|
31
|
-
: stripContextPrompts(executeContext),
|
|
32
|
-
message: `Execute session started. Use the executeContext.planningPrompt with executeContext.systemPrompt to generate the Figure-Ground classification.`,
|
|
33
|
-
...startGuide,
|
|
34
|
-
}, null, 2);
|
|
35
|
-
}
|
|
36
|
-
case 'plan_step': {
|
|
37
|
-
if (!input.sessionId)
|
|
38
|
-
return formatError('sessionId is required for plan_step action');
|
|
39
|
-
if (!input.stepResult)
|
|
40
|
-
return formatError('stepResult is required for plan_step action');
|
|
41
|
-
const stepResult = buildStepResult(input.stepResult);
|
|
42
|
-
if (!stepResult)
|
|
43
|
-
return formatError('Invalid stepResult: missing required fields for the given principle');
|
|
44
|
-
const result = engine.planStep(input.sessionId, stepResult);
|
|
45
|
-
if (!result.ok)
|
|
46
|
-
return formatError(result.error.message);
|
|
47
|
-
const { session, executeContext, isLastStep } = result.value;
|
|
48
|
-
if (isLastStep) {
|
|
49
|
-
const planStepLastGuide = {
|
|
50
|
-
nextAction: 'plan_complete',
|
|
51
|
-
nextActionParams: { sessionId: session.sessionId },
|
|
52
|
-
hint: '4단계 Planning 완료. plan_complete를 호출하세요.',
|
|
53
|
-
};
|
|
54
|
-
return JSON.stringify({
|
|
55
|
-
status: 'planning_complete',
|
|
56
|
-
sessionId: session.sessionId,
|
|
57
|
-
stepsCompleted: session.planningSteps.length,
|
|
58
|
-
isLastStep: true,
|
|
59
|
-
message: 'All planning steps completed. Call plan_complete to assemble the execution plan.',
|
|
60
|
-
...planStepLastGuide,
|
|
61
|
-
}, null, 2);
|
|
62
|
-
}
|
|
63
|
-
const currentPrinciple = executeContext?.currentPrinciple ?? 'next';
|
|
64
|
-
const planStepGuide = {
|
|
65
|
-
nextAction: 'plan_step',
|
|
66
|
-
nextActionParams: { sessionId: session.sessionId },
|
|
67
|
-
hint: `다음 단계: ${currentPrinciple}. executeContext.planningPrompt를 사용하세요.`,
|
|
68
|
-
};
|
|
69
|
-
return JSON.stringify({
|
|
70
|
-
status: 'planning',
|
|
71
|
-
sessionId: session.sessionId,
|
|
72
|
-
stepsCompleted: session.planningSteps.length,
|
|
73
|
-
isLastStep: false,
|
|
74
|
-
executeContext: verbose
|
|
75
|
-
? executeContext
|
|
76
|
-
: stripContextPrompts(executeContext),
|
|
77
|
-
message: `Step ${session.planningSteps.length}/${4} complete. Use the executeContext.planningPrompt to generate the next step.`,
|
|
78
|
-
...planStepGuide,
|
|
79
|
-
}, null, 2);
|
|
80
|
-
}
|
|
81
|
-
case 'plan_complete': {
|
|
82
|
-
if (!input.sessionId)
|
|
83
|
-
return formatError('sessionId is required for plan_complete action');
|
|
84
|
-
const result = engine.planComplete(input.sessionId);
|
|
85
|
-
if (!result.ok)
|
|
86
|
-
return formatError(result.error.message);
|
|
87
|
-
const { executionPlan } = result.value;
|
|
88
|
-
const planSummary = {
|
|
89
|
-
totalTasks: executionPlan.atomicTasks.length,
|
|
90
|
-
groupCount: executionPlan.taskGroups.length,
|
|
91
|
-
criticalPathLength: executionPlan.dagValidation.criticalPath.length,
|
|
92
|
-
parallelGroupCount: executionPlan.parallelGroups?.length ?? 0,
|
|
93
|
-
};
|
|
94
|
-
const planCompleteGuide = {
|
|
95
|
-
nextAction: 'execute_start',
|
|
96
|
-
nextActionParams: { sessionId: result.value.session.sessionId },
|
|
97
|
-
hint: '실행 계획이 준비됐습니다. execute_start를 호출하세요.',
|
|
98
|
-
};
|
|
99
|
-
return JSON.stringify({
|
|
100
|
-
status: 'plan_complete',
|
|
101
|
-
sessionId: result.value.session.sessionId,
|
|
102
|
-
planSummary,
|
|
103
|
-
executionPlan: {
|
|
104
|
-
planId: executionPlan.planId,
|
|
105
|
-
specId: executionPlan.specId,
|
|
106
|
-
totalTasks: executionPlan.atomicTasks.length,
|
|
107
|
-
totalGroups: executionPlan.taskGroups.length,
|
|
108
|
-
dagValid: executionPlan.dagValidation.isValid,
|
|
109
|
-
criticalPathLength: executionPlan.dagValidation.criticalPath.length,
|
|
110
|
-
classifiedACs: executionPlan.classifiedACs,
|
|
111
|
-
atomicTasks: executionPlan.atomicTasks,
|
|
112
|
-
taskGroups: executionPlan.taskGroups,
|
|
113
|
-
dagValidation: executionPlan.dagValidation,
|
|
114
|
-
parallelGroups: executionPlan.parallelGroups,
|
|
115
|
-
},
|
|
116
|
-
nextStep: 'Call execute_start to begin task execution. Tasks will run in topological order — critical path has ' +
|
|
117
|
-
planSummary.criticalPathLength +
|
|
118
|
-
' tasks.',
|
|
119
|
-
message: 'Execution plan assembled and validated. Call execute_start to begin task execution.',
|
|
120
|
-
...planCompleteGuide,
|
|
121
|
-
}, null, 2);
|
|
122
|
-
}
|
|
123
|
-
case 'execute_start': {
|
|
124
|
-
if (!input.sessionId)
|
|
125
|
-
return formatError('sessionId is required for execute_start action');
|
|
126
|
-
const result = engine.startExecution(input.sessionId);
|
|
127
|
-
if (!result.ok)
|
|
128
|
-
return formatError(result.error.message);
|
|
129
|
-
const { session, allTasksCompleted } = result.value;
|
|
130
|
-
let { taskContext } = result.value;
|
|
131
|
-
if (allTasksCompleted) {
|
|
132
|
-
const execStartDoneGuide = {
|
|
133
|
-
nextAction: 'evaluate',
|
|
134
|
-
nextActionParams: { sessionId: session.sessionId },
|
|
135
|
-
hint: '모든 태스크 완료. evaluate를 호출하세요.',
|
|
136
|
-
};
|
|
137
|
-
return JSON.stringify({
|
|
138
|
-
status: 'all_tasks_completed',
|
|
139
|
-
sessionId: session.sessionId,
|
|
140
|
-
message: 'All tasks already completed. Call evaluate to verify acceptance criteria.',
|
|
141
|
-
...execStartDoneGuide,
|
|
142
|
-
}, null, 2);
|
|
143
|
-
}
|
|
144
|
-
// Hybrid search로 suggestedFiles 업그레이드 (codeGraphRepoRoot 있을 때)
|
|
145
|
-
if (taskContext && session.codeGraphRepoRoot) {
|
|
146
|
-
taskContext = await engine.hydrateSuggestedFiles(taskContext, session.codeGraphRepoRoot);
|
|
147
|
-
}
|
|
148
|
-
if (input.cwd) {
|
|
149
|
-
try {
|
|
150
|
-
const currentTask = taskContext
|
|
151
|
-
? { taskId: taskContext.currentTask.taskId, title: taskContext.currentTask.title }
|
|
152
|
-
: null;
|
|
153
|
-
writeGestaltRule(input.cwd, { goal: session.spec.goal, constraints: session.spec.constraints }, currentTask, client);
|
|
154
|
-
writeActiveSession(input.cwd, session.sessionId, session.specId);
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
// Rule file creation failure should not block execution
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const execStartGuide = {
|
|
161
|
-
nextAction: 'execute_task',
|
|
162
|
-
nextActionParams: { sessionId: session.sessionId },
|
|
163
|
-
hint: `첫 번째 태스크: ${taskContext?.currentTask.title ?? ''}. taskContext.taskPrompt를 사용해 구현하세요.`,
|
|
164
|
-
};
|
|
165
|
-
return JSON.stringify({
|
|
166
|
-
status: 'executing',
|
|
167
|
-
sessionId: session.sessionId,
|
|
168
|
-
taskContext: taskContext
|
|
169
|
-
? applyTaskContextFilters(taskContext, verbose)
|
|
170
|
-
: taskContext,
|
|
171
|
-
message: `Execution started. Use taskContext.taskPrompt to implement the task, then submit with execute_task.`,
|
|
172
|
-
...execStartGuide,
|
|
173
|
-
}, null, 2);
|
|
174
|
-
}
|
|
175
|
-
case 'execute_task': {
|
|
176
|
-
if (!input.sessionId)
|
|
177
|
-
return formatError('sessionId is required for execute_task action');
|
|
178
|
-
if (!input.taskResult)
|
|
179
|
-
return formatError('taskResult is required for execute_task action');
|
|
180
|
-
const result = engine.submitTaskResult(input.sessionId, input.taskResult);
|
|
181
|
-
if (!result.ok)
|
|
182
|
-
return formatError(result.error.message);
|
|
183
|
-
const { session, allTasksCompleted, driftScore, retrospectiveContext } = result.value;
|
|
184
|
-
let { taskContext } = result.value;
|
|
185
|
-
if (allTasksCompleted) {
|
|
186
|
-
gestaltNotify({
|
|
187
|
-
event: 'tasks_completed',
|
|
188
|
-
message: `모든 태스크 완료 (${session.taskResults.length}개) — evaluate를 호출하세요`,
|
|
189
|
-
});
|
|
190
|
-
const execTaskDoneGuide = {
|
|
191
|
-
nextAction: 'evaluate',
|
|
192
|
-
nextActionParams: { sessionId: session.sessionId },
|
|
193
|
-
hint: '모든 태스크 완료. evaluate를 호출하세요.',
|
|
194
|
-
};
|
|
195
|
-
const doneTotal = session.executionPlan?.atomicTasks.length ?? 0;
|
|
196
|
-
const doneCompleted = session.taskResults.length;
|
|
197
|
-
const doneProgress = {
|
|
198
|
-
completed: doneCompleted,
|
|
199
|
-
total: doneTotal,
|
|
200
|
-
percent: doneTotal > 0 ? Math.round((doneCompleted / doneTotal) * 100) : 0,
|
|
201
|
-
};
|
|
202
|
-
return JSON.stringify({
|
|
203
|
-
status: 'all_tasks_completed',
|
|
204
|
-
sessionId: session.sessionId,
|
|
205
|
-
completedTasks: session.taskResults.length,
|
|
206
|
-
progress: doneProgress,
|
|
207
|
-
...(driftScore ? { driftScore } : {}),
|
|
208
|
-
...(retrospectiveContext
|
|
209
|
-
? { retrospectiveContext: slimRetrospectiveContext(retrospectiveContext) }
|
|
210
|
-
: {}),
|
|
211
|
-
message: 'All tasks completed. Call evaluate to verify acceptance criteria.',
|
|
212
|
-
...execTaskDoneGuide,
|
|
213
|
-
}, null, 2);
|
|
214
|
-
}
|
|
215
|
-
// Hybrid search로 suggestedFiles 업그레이드
|
|
216
|
-
if (taskContext && session.codeGraphRepoRoot) {
|
|
217
|
-
taskContext = await engine.hydrateSuggestedFiles(taskContext, session.codeGraphRepoRoot);
|
|
218
|
-
}
|
|
219
|
-
if (input.cwd && taskContext) {
|
|
220
|
-
try {
|
|
221
|
-
updateGestaltRule(input.cwd, { goal: session.spec.goal, constraints: session.spec.constraints }, { taskId: taskContext.currentTask.taskId, title: taskContext.currentTask.title }, client);
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
// Rule file update failure should not block execution
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
const compressionAvailable = session.taskResults.length > 5;
|
|
228
|
-
const execTaskNextId = taskContext?.currentTask.taskId ?? '';
|
|
229
|
-
const execTaskGuide = {
|
|
230
|
-
nextAction: 'execute_task',
|
|
231
|
-
nextActionParams: { sessionId: session.sessionId },
|
|
232
|
-
hint: `다음 태스크: ${execTaskNextId}. 계속 실행하세요.`,
|
|
233
|
-
};
|
|
234
|
-
const execTotal = session.executionPlan?.atomicTasks.length ?? 0;
|
|
235
|
-
const execCompleted = session.taskResults.length;
|
|
236
|
-
const execProgress = {
|
|
237
|
-
completed: execCompleted,
|
|
238
|
-
total: execTotal,
|
|
239
|
-
percent: execTotal > 0 ? Math.round((execCompleted / execTotal) * 100) : 0,
|
|
240
|
-
};
|
|
241
|
-
return JSON.stringify({
|
|
242
|
-
status: 'executing',
|
|
243
|
-
sessionId: session.sessionId,
|
|
244
|
-
completedTasks: session.taskResults.length,
|
|
245
|
-
progress: execProgress,
|
|
246
|
-
taskContext: taskContext
|
|
247
|
-
? applyTaskContextFilters(taskContext, verbose)
|
|
248
|
-
: taskContext,
|
|
249
|
-
...(compressionAvailable ? { compressionAvailable: true } : {}),
|
|
250
|
-
...(driftScore ? { driftScore } : {}),
|
|
251
|
-
...(retrospectiveContext
|
|
252
|
-
? { retrospectiveContext: slimRetrospectiveContext(retrospectiveContext) }
|
|
253
|
-
: {}),
|
|
254
|
-
message: `Task "${input.taskResult.taskId}" recorded.${driftScore?.thresholdExceeded ? ' WARNING: Drift threshold exceeded! Review retrospectiveContext.' : ''}${compressionAvailable ? ' TIP: Context is getting long — consider calling compress to summarize completed work.' : ''} Use taskContext.taskPrompt to implement the next task.`,
|
|
255
|
-
...execTaskGuide,
|
|
256
|
-
}, null, 2);
|
|
257
|
-
}
|
|
258
|
-
case 'evaluate': {
|
|
259
|
-
if (!input.sessionId)
|
|
260
|
-
return formatError('sessionId is required for evaluate action');
|
|
261
|
-
// Call 3: Submit contextual evaluation result
|
|
262
|
-
if (input.evaluationResult) {
|
|
263
|
-
const result = engine.submitEvaluation(input.sessionId, input.evaluationResult);
|
|
264
|
-
if (!result.ok)
|
|
265
|
-
return formatError(result.error.message);
|
|
266
|
-
const { evaluationResult } = result.value;
|
|
267
|
-
const completedSession = result.value.session;
|
|
268
|
-
// Update project memory on execution completion
|
|
269
|
-
try {
|
|
270
|
-
const memoryStore = new ProjectMemoryStore();
|
|
271
|
-
const completedTasks = completedSession.taskResults
|
|
272
|
-
.filter((t) => t.status === 'completed')
|
|
273
|
-
.map((t) => t.taskId);
|
|
274
|
-
const failedTasks = completedSession.taskResults
|
|
275
|
-
.filter((t) => t.status === 'failed')
|
|
276
|
-
.map((t) => t.taskId);
|
|
277
|
-
const score = evaluationResult?.overallScore ?? 0;
|
|
278
|
-
const goalAlign = evaluationResult?.goalAlignment ?? 0;
|
|
279
|
-
memoryStore.addExecution({
|
|
280
|
-
executeSessionId: completedSession.sessionId,
|
|
281
|
-
specId: completedSession.specId,
|
|
282
|
-
completedTasks,
|
|
283
|
-
failedTasks,
|
|
284
|
-
resultSummary: `Score: ${score.toFixed(2)}, goalAlignment: ${goalAlign.toFixed(2)}`,
|
|
285
|
-
completedAt: new Date().toISOString(),
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
catch {
|
|
289
|
-
// Memory update failure should not block the response
|
|
290
|
-
}
|
|
291
|
-
if (input.cwd) {
|
|
292
|
-
try {
|
|
293
|
-
deleteGestaltRule(input.cwd, client);
|
|
294
|
-
deleteActiveSession(input.cwd);
|
|
295
|
-
}
|
|
296
|
-
catch {
|
|
297
|
-
// Cleanup failure should not block the response
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
const score = evaluationResult.overallScore;
|
|
301
|
-
const alignment = evaluationResult.goalAlignment;
|
|
302
|
-
const success = score >= 0.85 && alignment >= 0.8;
|
|
303
|
-
gestaltNotify({
|
|
304
|
-
event: success ? 'evaluation_success' : 'evaluation_failed',
|
|
305
|
-
message: success
|
|
306
|
-
? `평가 성공 ✓ score: ${score.toFixed(2)}, alignment: ${alignment.toFixed(2)}`
|
|
307
|
-
: `평가 미달 score: ${score.toFixed(2)}, alignment: ${alignment.toFixed(2)} — Evolve 진입`,
|
|
308
|
-
});
|
|
309
|
-
const evalCompleteExtra = success
|
|
310
|
-
? { hint: '구현 완료! score ≥ 0.85, alignment ≥ 0.80 달성.' }
|
|
311
|
-
: {
|
|
312
|
-
nextAction: 'evolve',
|
|
313
|
-
nextActionParams: { sessionId: completedSession.sessionId },
|
|
314
|
-
hint: '점수 미달. evolve를 호출해 개선하세요.',
|
|
315
|
-
};
|
|
316
|
-
return JSON.stringify({
|
|
317
|
-
status: 'completed',
|
|
318
|
-
sessionId: completedSession.sessionId,
|
|
319
|
-
stage: 'complete',
|
|
320
|
-
evaluationResult,
|
|
321
|
-
message: `Evaluation complete. Overall score: ${score.toFixed(2)}, goal alignment: ${alignment.toFixed(2)}. Session is now completed.`,
|
|
322
|
-
...evalCompleteExtra,
|
|
323
|
-
}, null, 2);
|
|
324
|
-
}
|
|
325
|
-
// Call 2: Submit structural results
|
|
326
|
-
if (input.structuralResult) {
|
|
327
|
-
const result = engine.submitStructuralResult(input.sessionId, input.structuralResult);
|
|
328
|
-
if (!result.ok)
|
|
329
|
-
return formatError(result.error.message);
|
|
330
|
-
const { stage, shortCircuited, contextualContext, evaluationResult } = result.value;
|
|
331
|
-
if (shortCircuited) {
|
|
332
|
-
gestaltNotify({
|
|
333
|
-
event: 'structural_failed',
|
|
334
|
-
message: 'Structural 검사 실패 — lint/build/test를 확인하세요',
|
|
335
|
-
});
|
|
336
|
-
const evalShortCircuitGuide = {
|
|
337
|
-
nextAction: 'evolve',
|
|
338
|
-
nextActionParams: { sessionId: result.value.session.sessionId },
|
|
339
|
-
hint: '점수 미달. evolve를 호출해 개선하세요.',
|
|
340
|
-
};
|
|
341
|
-
return JSON.stringify({
|
|
342
|
-
status: 'completed',
|
|
343
|
-
sessionId: result.value.session.sessionId,
|
|
344
|
-
stage: 'complete',
|
|
345
|
-
shortCircuited: true,
|
|
346
|
-
evaluationResult,
|
|
347
|
-
message: 'Structural checks failed. Evaluation short-circuited. Fix structural issues and retry.',
|
|
348
|
-
...evalShortCircuitGuide,
|
|
349
|
-
}, null, 2);
|
|
350
|
-
}
|
|
351
|
-
const evalContextualGuide = {
|
|
352
|
-
nextAction: 'evaluate',
|
|
353
|
-
nextActionParams: { sessionId: result.value.session.sessionId },
|
|
354
|
-
hint: 'evaluationContext에 따라 각 AC를 검증하고 결과를 제출하세요.',
|
|
355
|
-
};
|
|
356
|
-
return JSON.stringify({
|
|
357
|
-
status: 'evaluating',
|
|
358
|
-
sessionId: result.value.session.sessionId,
|
|
359
|
-
stage,
|
|
360
|
-
contextualContext: verbose
|
|
361
|
-
? contextualContext
|
|
362
|
-
: stripContextPrompts(contextualContext),
|
|
363
|
-
message: 'Structural checks passed. Use contextualContext.evaluatePrompt with contextualContext.systemPrompt to generate the contextual evaluation.',
|
|
364
|
-
...evalContextualGuide,
|
|
365
|
-
}, null, 2);
|
|
366
|
-
}
|
|
367
|
-
// Call 1: Start evaluation → return structural commands
|
|
368
|
-
const result = engine.startEvaluation(input.sessionId);
|
|
369
|
-
if (!result.ok)
|
|
370
|
-
return formatError(result.error.message);
|
|
371
|
-
const evalStructuralGuide = {
|
|
372
|
-
nextAction: 'evaluate',
|
|
373
|
-
nextActionParams: { sessionId: result.value.session.sessionId },
|
|
374
|
-
hint: 'structuralContext에 따라 lint/build/test를 실행하고 결과를 제출하세요.',
|
|
375
|
-
};
|
|
376
|
-
return JSON.stringify({
|
|
377
|
-
status: 'evaluating',
|
|
378
|
-
sessionId: result.value.session.sessionId,
|
|
379
|
-
stage: 'structural',
|
|
380
|
-
structuralContext: result.value.structuralContext,
|
|
381
|
-
message: 'Run structural checks (lint, build, test) and submit results with structuralResult.',
|
|
382
|
-
...evalStructuralGuide,
|
|
383
|
-
}, null, 2);
|
|
384
|
-
}
|
|
385
|
-
case 'status': {
|
|
386
|
-
return handleStatus(engine, input.sessionId, input.cwd);
|
|
387
|
-
}
|
|
388
|
-
// ─── Execution Continuity Actions ─────────────────────────
|
|
389
|
-
case 'resume': {
|
|
390
|
-
if (!input.sessionId)
|
|
391
|
-
return formatError('sessionId is required for resume action');
|
|
392
|
-
try {
|
|
393
|
-
const session = engine.getSession(input.sessionId);
|
|
394
|
-
if (session.status !== 'executing' && session.status !== 'planning') {
|
|
395
|
-
return formatError(`Session status is "${session.status}". Resume is only available for in-progress sessions.`);
|
|
396
|
-
}
|
|
397
|
-
if (!session.executionPlan) {
|
|
398
|
-
return formatError('No execution plan found. Complete planning first.');
|
|
399
|
-
}
|
|
400
|
-
const completedSet = new Set(session.completedTaskIds);
|
|
401
|
-
const remaining = session.executionPlan.dagValidation.topologicalOrder.filter((id) => !completedSet.has(id));
|
|
402
|
-
const nextTaskId = remaining[0] ?? null;
|
|
403
|
-
const totalTasks = session.executionPlan.atomicTasks.length;
|
|
404
|
-
const progressPercent = totalTasks > 0 ? Math.round((session.completedTaskIds.length / totalTasks) * 100) : 0;
|
|
405
|
-
const resumeContext = {
|
|
406
|
-
completedTaskIds: session.completedTaskIds,
|
|
407
|
-
nextTaskId,
|
|
408
|
-
totalTasks,
|
|
409
|
-
progressPercent,
|
|
410
|
-
};
|
|
411
|
-
return JSON.stringify({
|
|
412
|
-
status: 'resume_context',
|
|
413
|
-
sessionId: session.sessionId,
|
|
414
|
-
resumeContext,
|
|
415
|
-
message: nextTaskId
|
|
416
|
-
? `Resuming from task "${nextTaskId}". ${session.completedTaskIds.length}/${totalTasks} tasks completed (${progressPercent}%). Use execute_task to continue.`
|
|
417
|
-
: 'All tasks completed. Call evaluate to verify acceptance criteria.',
|
|
418
|
-
}, null, 2);
|
|
419
|
-
}
|
|
420
|
-
catch (e) {
|
|
421
|
-
return formatError(e instanceof Error ? e.message : String(e));
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
// ─── Audit Actions ─────────────────────────────────────────
|
|
425
|
-
case 'audit': {
|
|
426
|
-
if (!input.sessionId)
|
|
427
|
-
return formatError('sessionId is required for audit action');
|
|
428
|
-
try {
|
|
429
|
-
const session = engine.getSession(input.sessionId);
|
|
430
|
-
const auditEngine = new AuditEngine();
|
|
431
|
-
// Call 2: Submit audit result
|
|
432
|
-
if (input.auditResult) {
|
|
433
|
-
const auditResult = auditEngine.buildAuditResult(input.auditResult);
|
|
434
|
-
engine.getSessionManager().setAuditResult(session.sessionId, auditResult);
|
|
435
|
-
return JSON.stringify({
|
|
436
|
-
status: 'audit_complete',
|
|
437
|
-
sessionId: session.sessionId,
|
|
438
|
-
auditResult,
|
|
439
|
-
summary: {
|
|
440
|
-
total: session.spec.acceptanceCriteria.length,
|
|
441
|
-
implemented: auditResult.implementedACs.length,
|
|
442
|
-
partial: auditResult.partialACs.length,
|
|
443
|
-
missing: auditResult.missingACs.length,
|
|
444
|
-
},
|
|
445
|
-
message: `Audit complete. ${auditResult.missingACs.length} ACs missing, ${auditResult.partialACs.length} partial. Review gapAnalysis for next steps.`,
|
|
446
|
-
}, null, 2);
|
|
447
|
-
}
|
|
448
|
-
// Call 1: Return audit context
|
|
449
|
-
const codebaseSnapshot = input.codebaseSnapshot ?? '(no snapshot provided — analyze based on spec ACs only)';
|
|
450
|
-
const auditContext = auditEngine.buildAuditContext(session.spec, codebaseSnapshot);
|
|
451
|
-
return JSON.stringify({
|
|
452
|
-
status: 'auditing',
|
|
453
|
-
sessionId: session.sessionId,
|
|
454
|
-
auditContext,
|
|
455
|
-
message: 'Use auditContext.systemPrompt + auditContext.auditPrompt to analyze the codebase, then submit auditResult.',
|
|
456
|
-
}, null, 2);
|
|
457
|
-
}
|
|
458
|
-
catch (e) {
|
|
459
|
-
return formatError(e instanceof Error ? e.message : String(e));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
// ─── Sub-task Spawning Actions ─────────────────────────────
|
|
463
|
-
case 'spawn': {
|
|
464
|
-
if (!input.sessionId)
|
|
465
|
-
return formatError('sessionId is required for spawn action');
|
|
466
|
-
if (!input.parentTaskId)
|
|
467
|
-
return formatError('parentTaskId is required for spawn action');
|
|
468
|
-
if (!input.subTasks || input.subTasks.length === 0)
|
|
469
|
-
return formatError('subTasks is required for spawn action');
|
|
470
|
-
try {
|
|
471
|
-
const session = engine.getSession(input.sessionId);
|
|
472
|
-
const parentTask = session.executionPlan?.atomicTasks.find((t) => t.taskId === input.parentTaskId);
|
|
473
|
-
const inheritedContext = parentTask
|
|
474
|
-
? `Parent task: "${parentTask.title}"\n${parentTask.description}`
|
|
475
|
-
: `Parent task ID: ${input.parentTaskId}`;
|
|
476
|
-
const spawnedTasks = input.subTasks.map((st) => ({
|
|
477
|
-
taskId: `spawned-${randomUUID().slice(0, 8)}`,
|
|
478
|
-
parentTaskId: input.parentTaskId,
|
|
479
|
-
title: st.title,
|
|
480
|
-
description: st.description,
|
|
481
|
-
inheritedContext,
|
|
482
|
-
dependsOn: st.dependsOn ?? [],
|
|
483
|
-
status: 'pending',
|
|
484
|
-
createdAt: new Date().toISOString(),
|
|
485
|
-
}));
|
|
486
|
-
engine.getSessionManager().addSubTasks(session.sessionId, spawnedTasks);
|
|
487
|
-
return JSON.stringify({
|
|
488
|
-
status: 'spawned',
|
|
489
|
-
sessionId: session.sessionId,
|
|
490
|
-
parentTaskId: input.parentTaskId,
|
|
491
|
-
spawnedTasks: spawnedTasks.map((t) => ({
|
|
492
|
-
taskId: t.taskId,
|
|
493
|
-
title: t.title,
|
|
494
|
-
dependsOn: t.dependsOn,
|
|
495
|
-
})),
|
|
496
|
-
message: `${spawnedTasks.length} sub-tasks spawned from "${input.parentTaskId}". Submit results with execute_task using each sub-task's taskId.`,
|
|
497
|
-
}, null, 2);
|
|
498
|
-
}
|
|
499
|
-
catch (e) {
|
|
500
|
-
return formatError(e instanceof Error ? e.message : String(e));
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
// ─── Evolution Loop Actions ───────────────────────────────
|
|
504
|
-
case 'evolve_fix': {
|
|
505
|
-
if (!input.sessionId)
|
|
506
|
-
return formatError('sessionId is required for evolve_fix action');
|
|
507
|
-
const fixResult = engine.startStructuralFix(input.sessionId, input.fixTasks);
|
|
508
|
-
if (!fixResult.ok)
|
|
509
|
-
return formatError(fixResult.error.message);
|
|
510
|
-
const { fixContext } = fixResult.value;
|
|
511
|
-
if (fixContext) {
|
|
512
|
-
const evolveFixGuide = {
|
|
513
|
-
nextAction: 'evolve_fix',
|
|
514
|
-
nextActionParams: { sessionId: fixResult.value.session.sessionId },
|
|
515
|
-
hint: 'fixContext에 따라 lint/build/test 오류를 수정하세요.',
|
|
516
|
-
};
|
|
517
|
-
return JSON.stringify({
|
|
518
|
-
status: 'evolving',
|
|
519
|
-
sessionId: fixResult.value.session.sessionId,
|
|
520
|
-
stage: 'fix',
|
|
521
|
-
fixContext,
|
|
522
|
-
message: 'Structural failures detected. Use fixContext to generate fix tasks, then re-submit with fixTasks.',
|
|
523
|
-
...evolveFixGuide,
|
|
524
|
-
}, null, 2);
|
|
525
|
-
}
|
|
526
|
-
const evolveFixAppliedGuide = {
|
|
527
|
-
nextAction: 'evaluate',
|
|
528
|
-
nextActionParams: { sessionId: fixResult.value.session.sessionId },
|
|
529
|
-
hint: 'fixContext에 따라 lint/build/test 오류를 수정하세요.',
|
|
530
|
-
};
|
|
531
|
-
return JSON.stringify({
|
|
532
|
-
status: 'fix_applied',
|
|
533
|
-
sessionId: fixResult.value.session.sessionId,
|
|
534
|
-
message: 'Fix tasks recorded. Call evaluate to re-run structural checks.',
|
|
535
|
-
...evolveFixAppliedGuide,
|
|
536
|
-
}, null, 2);
|
|
537
|
-
}
|
|
538
|
-
case 'evolve': {
|
|
539
|
-
if (!input.sessionId)
|
|
540
|
-
return formatError('sessionId is required for evolve action');
|
|
541
|
-
const evolveResult = engine.startContextualEvolve(input.sessionId, input.terminateReason);
|
|
542
|
-
if (!evolveResult.ok)
|
|
543
|
-
return formatError(evolveResult.error.message);
|
|
544
|
-
if (evolveResult.value.lateralContext) {
|
|
545
|
-
const evolveLateralGuide = {
|
|
546
|
-
nextAction: 'evolve_lateral_result',
|
|
547
|
-
nextActionParams: { sessionId: evolveResult.value.session.sessionId },
|
|
548
|
-
hint: 'lateralContext.lateralPrompt를 사용해 관점 전환 결과를 제출하세요.',
|
|
549
|
-
};
|
|
550
|
-
return JSON.stringify({
|
|
551
|
-
status: 'lateral_thinking',
|
|
552
|
-
sessionId: evolveResult.value.session.sessionId,
|
|
553
|
-
lateralContext: evolveResult.value.lateralContext,
|
|
554
|
-
message: `Stagnation detected. Lateral thinking activated: ${evolveResult.value.lateralContext.persona} persona (attempt ${evolveResult.value.lateralContext.attemptNumber}/4). Use lateralContext to generate a lateral SpecPatch, then submit with evolve_lateral_result.`,
|
|
555
|
-
...evolveLateralGuide,
|
|
556
|
-
}, null, 2);
|
|
557
|
-
}
|
|
558
|
-
if (evolveResult.value.humanEscalation) {
|
|
559
|
-
if (input.cwd) {
|
|
560
|
-
try {
|
|
561
|
-
deleteGestaltRule(input.cwd, client);
|
|
562
|
-
deleteActiveSession(input.cwd);
|
|
563
|
-
}
|
|
564
|
-
catch {
|
|
565
|
-
/* ignore */
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
gestaltNotify({
|
|
569
|
-
event: 'human_escalation',
|
|
570
|
-
message: '⚠️ Human Escalation — 모든 Lateral Thinking 소진, 수동 개입이 필요해요',
|
|
571
|
-
});
|
|
572
|
-
return JSON.stringify({
|
|
573
|
-
status: 'human_escalation',
|
|
574
|
-
sessionId: evolveResult.value.session.sessionId,
|
|
575
|
-
terminationReason: 'human_escalation',
|
|
576
|
-
escalationContext: evolveResult.value.humanEscalation,
|
|
577
|
-
evolutionHistory: evolveResult.value.session.evolutionHistory.map((g) => ({
|
|
578
|
-
generation: g.generation,
|
|
579
|
-
score: g.evaluationScore,
|
|
580
|
-
goalAlignment: g.goalAlignment,
|
|
581
|
-
fieldsChanged: g.delta.fieldsChanged,
|
|
582
|
-
})),
|
|
583
|
-
message: 'All lateral thinking personas exhausted. Human intervention required.',
|
|
584
|
-
}, null, 2);
|
|
585
|
-
}
|
|
586
|
-
if (evolveResult.value.terminated) {
|
|
587
|
-
if (input.cwd) {
|
|
588
|
-
try {
|
|
589
|
-
deleteGestaltRule(input.cwd, client);
|
|
590
|
-
deleteActiveSession(input.cwd);
|
|
591
|
-
}
|
|
592
|
-
catch {
|
|
593
|
-
/* ignore */
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
gestaltNotify({
|
|
597
|
-
event: 'terminated',
|
|
598
|
-
message: `세션 종료: ${evolveResult.value.terminationReason ?? 'unknown'}`,
|
|
599
|
-
});
|
|
600
|
-
return JSON.stringify({
|
|
601
|
-
status: 'terminated',
|
|
602
|
-
sessionId: evolveResult.value.session.sessionId,
|
|
603
|
-
terminationReason: evolveResult.value.terminationReason,
|
|
604
|
-
evolutionHistory: evolveResult.value.session.evolutionHistory.map((g) => ({
|
|
605
|
-
generation: g.generation,
|
|
606
|
-
score: g.evaluationScore,
|
|
607
|
-
goalAlignment: g.goalAlignment,
|
|
608
|
-
fieldsChanged: g.delta.fieldsChanged,
|
|
609
|
-
})),
|
|
610
|
-
message: `Evolution terminated: ${evolveResult.value.terminationReason}.`,
|
|
611
|
-
}, null, 2);
|
|
612
|
-
}
|
|
613
|
-
const evolveGuide = {
|
|
614
|
-
nextAction: 'evolve_patch',
|
|
615
|
-
nextActionParams: { sessionId: evolveResult.value.session.sessionId },
|
|
616
|
-
hint: 'evolveContext에 따라 specPatch를 작성하세요.',
|
|
617
|
-
};
|
|
618
|
-
return JSON.stringify({
|
|
619
|
-
status: 'evolving',
|
|
620
|
-
sessionId: evolveResult.value.session.sessionId,
|
|
621
|
-
stage: 'evolve',
|
|
622
|
-
evolveContext: evolveResult.value.evolveContext,
|
|
623
|
-
message: 'Use evolveContext to generate a Spec patch, then submit with evolve_patch.',
|
|
624
|
-
...evolveGuide,
|
|
625
|
-
}, null, 2);
|
|
626
|
-
}
|
|
627
|
-
case 'evolve_patch': {
|
|
628
|
-
if (!input.sessionId)
|
|
629
|
-
return formatError('sessionId is required for evolve_patch action');
|
|
630
|
-
if (!input.specPatch)
|
|
631
|
-
return formatError('specPatch is required for evolve_patch action');
|
|
632
|
-
const patchResult = engine.submitSpecPatch(input.sessionId, input.specPatch);
|
|
633
|
-
if (!patchResult.ok)
|
|
634
|
-
return formatError(patchResult.error.message);
|
|
635
|
-
const { impactedTaskIds, reExecuteContext } = patchResult.value;
|
|
636
|
-
if (input.cwd) {
|
|
637
|
-
try {
|
|
638
|
-
const patchedSession = patchResult.value.session;
|
|
639
|
-
updateGestaltRule(input.cwd, { goal: patchedSession.spec.goal, constraints: patchedSession.spec.constraints }, null, client);
|
|
640
|
-
}
|
|
641
|
-
catch {
|
|
642
|
-
// Rule file update failure should not block execution
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
if (impactedTaskIds.length === 0) {
|
|
646
|
-
const evolvePatchNoTasksGuide = {
|
|
647
|
-
nextAction: 'evaluate',
|
|
648
|
-
nextActionParams: { sessionId: patchResult.value.session.sessionId },
|
|
649
|
-
hint: 'impactedTaskIds의 태스크를 재실행하세요.',
|
|
650
|
-
};
|
|
651
|
-
return JSON.stringify({
|
|
652
|
-
status: 'patch_applied',
|
|
653
|
-
sessionId: patchResult.value.session.sessionId,
|
|
654
|
-
impactedTaskIds: [],
|
|
655
|
-
message: 'Spec patched. No tasks need re-execution. Call evaluate to re-assess.',
|
|
656
|
-
...evolvePatchNoTasksGuide,
|
|
657
|
-
}, null, 2);
|
|
658
|
-
}
|
|
659
|
-
const evolvePatchGuide = {
|
|
660
|
-
nextAction: 'evolve_re_execute',
|
|
661
|
-
nextActionParams: { sessionId: patchResult.value.session.sessionId },
|
|
662
|
-
hint: 'impactedTaskIds의 태스크를 재실행하세요.',
|
|
663
|
-
};
|
|
664
|
-
return JSON.stringify({
|
|
665
|
-
status: 're_executing',
|
|
666
|
-
sessionId: patchResult.value.session.sessionId,
|
|
667
|
-
impactedTaskIds,
|
|
668
|
-
reExecuteContext,
|
|
669
|
-
message: `Spec patched. ${impactedTaskIds.length} tasks need re-execution. Use reExecuteContext to implement the task.`,
|
|
670
|
-
...evolvePatchGuide,
|
|
671
|
-
}, null, 2);
|
|
672
|
-
}
|
|
673
|
-
case 'evolve_re_execute': {
|
|
674
|
-
if (!input.sessionId)
|
|
675
|
-
return formatError('sessionId is required for evolve_re_execute action');
|
|
676
|
-
if (!input.reExecuteTaskResult)
|
|
677
|
-
return formatError('reExecuteTaskResult is required for evolve_re_execute action');
|
|
678
|
-
const reExecResult = engine.submitReExecuteTaskResult(input.sessionId, input.reExecuteTaskResult);
|
|
679
|
-
if (!reExecResult.ok)
|
|
680
|
-
return formatError(reExecResult.error.message);
|
|
681
|
-
const { reExecuteContext: nextContext, allTasksCompleted } = reExecResult.value;
|
|
682
|
-
if (allTasksCompleted) {
|
|
683
|
-
const reExecDoneGuide = {
|
|
684
|
-
nextAction: 'evaluate',
|
|
685
|
-
nextActionParams: { sessionId: reExecResult.value.session.sessionId },
|
|
686
|
-
hint: '재실행 완료. evaluate를 다시 호출하세요.',
|
|
687
|
-
};
|
|
688
|
-
return JSON.stringify({
|
|
689
|
-
status: 're_execute_complete',
|
|
690
|
-
sessionId: reExecResult.value.session.sessionId,
|
|
691
|
-
message: 'All impacted tasks re-executed. Call evaluate to re-assess.',
|
|
692
|
-
...reExecDoneGuide,
|
|
693
|
-
}, null, 2);
|
|
694
|
-
}
|
|
695
|
-
const reExecGuide = {
|
|
696
|
-
nextAction: 'evolve_re_execute',
|
|
697
|
-
nextActionParams: { sessionId: reExecResult.value.session.sessionId },
|
|
698
|
-
hint: '재실행 계속. 남은 태스크를 완료하세요.',
|
|
699
|
-
};
|
|
700
|
-
return JSON.stringify({
|
|
701
|
-
status: 're_executing',
|
|
702
|
-
sessionId: reExecResult.value.session.sessionId,
|
|
703
|
-
reExecuteContext: nextContext,
|
|
704
|
-
message: 'Task recorded. Use reExecuteContext to implement the next re-execution task.',
|
|
705
|
-
...reExecGuide,
|
|
706
|
-
}, null, 2);
|
|
707
|
-
}
|
|
708
|
-
// ─── Role Agent System Actions ────────────────────────────
|
|
709
|
-
case 'role_match': {
|
|
710
|
-
if (!input.sessionId)
|
|
711
|
-
return formatError('sessionId is required for role_match action');
|
|
712
|
-
const rmResult = engine.roleMatch(input.sessionId, input.matchResult);
|
|
713
|
-
if (!rmResult.ok)
|
|
714
|
-
return formatError(rmResult.error.message);
|
|
715
|
-
const { matchContext, perspectivePrompts } = rmResult.value;
|
|
716
|
-
if (matchContext) {
|
|
717
|
-
const roleMatchCall1Guide = {
|
|
718
|
-
nextAction: 'role_match',
|
|
719
|
-
nextActionParams: { sessionId: rmResult.value.session.sessionId },
|
|
720
|
-
hint: 'matchContext를 사용해 관련 Role Agent를 선택하고 matchResult를 제출하세요.',
|
|
721
|
-
};
|
|
722
|
-
return JSON.stringify({
|
|
723
|
-
status: 'role_matching',
|
|
724
|
-
sessionId: rmResult.value.session.sessionId,
|
|
725
|
-
matchContext,
|
|
726
|
-
message: 'Use matchContext.systemPrompt + matchContext.matchingPrompt to determine which role agents match this task. Submit matchResult with role_match.',
|
|
727
|
-
...roleMatchCall1Guide,
|
|
728
|
-
}, null, 2);
|
|
729
|
-
}
|
|
730
|
-
const roleMatchCall2Guide = {
|
|
731
|
-
nextAction: 'role_consensus',
|
|
732
|
-
nextActionParams: { sessionId: rmResult.value.session.sessionId },
|
|
733
|
-
hint: '각 에이전트의 관점을 생성하고 perspectives를 제출하세요.',
|
|
734
|
-
};
|
|
735
|
-
return JSON.stringify({
|
|
736
|
-
status: 'role_matched',
|
|
737
|
-
sessionId: rmResult.value.session.sessionId,
|
|
738
|
-
perspectivePrompts: perspectivePrompts ?? [],
|
|
739
|
-
matchCount: perspectivePrompts?.length ?? 0,
|
|
740
|
-
message: perspectivePrompts?.length
|
|
741
|
-
? `${perspectivePrompts.length} agents matched. Use each perspectivePrompt for parallel LLM calls, then submit perspectives with role_consensus.`
|
|
742
|
-
: 'No agents matched. Proceed directly to execute_task.',
|
|
743
|
-
...roleMatchCall2Guide,
|
|
744
|
-
}, null, 2);
|
|
745
|
-
}
|
|
746
|
-
case 'role_consensus': {
|
|
747
|
-
if (!input.sessionId)
|
|
748
|
-
return formatError('sessionId is required for role_consensus action');
|
|
749
|
-
const rcResult = engine.roleConsensus(input.sessionId, input.perspectives, input.consensus);
|
|
750
|
-
if (!rcResult.ok)
|
|
751
|
-
return formatError(rcResult.error.message);
|
|
752
|
-
const { synthesisContext, roleGuidance } = rcResult.value;
|
|
753
|
-
if (synthesisContext) {
|
|
754
|
-
const roleConsensusCall1Guide = {
|
|
755
|
-
nextAction: 'role_consensus',
|
|
756
|
-
nextActionParams: { sessionId: rcResult.value.session.sessionId },
|
|
757
|
-
hint: 'synthesisContext를 사용해 관점을 통합하고 consensus를 제출하세요.',
|
|
758
|
-
};
|
|
759
|
-
return JSON.stringify({
|
|
760
|
-
status: 'synthesizing',
|
|
761
|
-
sessionId: rcResult.value.session.sessionId,
|
|
762
|
-
synthesisContext,
|
|
763
|
-
message: 'Use synthesisContext.systemPrompt + synthesisContext.synthesisPrompt to synthesize consensus. Submit consensus with role_consensus.',
|
|
764
|
-
...roleConsensusCall1Guide,
|
|
765
|
-
}, null, 2);
|
|
766
|
-
}
|
|
767
|
-
const roleConsensusCall2Guide = {
|
|
768
|
-
nextAction: 'execute_task',
|
|
769
|
-
nextActionParams: { sessionId: rcResult.value.session.sessionId },
|
|
770
|
-
hint: 'roleGuidance를 참조해 태스크를 실행하세요.',
|
|
771
|
-
};
|
|
772
|
-
return JSON.stringify({
|
|
773
|
-
status: 'consensus_complete',
|
|
774
|
-
sessionId: rcResult.value.session.sessionId,
|
|
775
|
-
roleGuidance,
|
|
776
|
-
message: 'Role consensus stored. Use roleGuidance to inform task implementation, then submit with execute_task.',
|
|
777
|
-
...roleConsensusCall2Guide,
|
|
778
|
-
}, null, 2);
|
|
779
|
-
}
|
|
780
|
-
// ─── Lateral Thinking Actions ──────────────────────────────
|
|
781
|
-
case 'evolve_lateral': {
|
|
782
|
-
if (!input.sessionId)
|
|
783
|
-
return formatError('sessionId is required for evolve_lateral action');
|
|
784
|
-
const lateralResult = engine.startLateralEvolve(input.sessionId);
|
|
785
|
-
if (!lateralResult.ok)
|
|
786
|
-
return formatError(lateralResult.error.message);
|
|
787
|
-
if (lateralResult.value.terminated) {
|
|
788
|
-
if (input.cwd) {
|
|
789
|
-
try {
|
|
790
|
-
deleteGestaltRule(input.cwd, client);
|
|
791
|
-
deleteActiveSession(input.cwd);
|
|
792
|
-
}
|
|
793
|
-
catch {
|
|
794
|
-
/* ignore */
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
return JSON.stringify({
|
|
798
|
-
status: lateralResult.value.terminationReason === 'human_escalation'
|
|
799
|
-
? 'human_escalation'
|
|
800
|
-
: 'terminated',
|
|
801
|
-
sessionId: lateralResult.value.session.sessionId,
|
|
802
|
-
terminationReason: lateralResult.value.terminationReason,
|
|
803
|
-
...(lateralResult.value.humanEscalation
|
|
804
|
-
? { escalationContext: lateralResult.value.humanEscalation }
|
|
805
|
-
: {}),
|
|
806
|
-
message: lateralResult.value.terminationReason === 'human_escalation'
|
|
807
|
-
? 'All lateral thinking personas exhausted. Human intervention required.'
|
|
808
|
-
: `Evolution terminated: ${lateralResult.value.terminationReason}.`,
|
|
809
|
-
}, null, 2);
|
|
810
|
-
}
|
|
811
|
-
if (lateralResult.value.lateralContext) {
|
|
812
|
-
return JSON.stringify({
|
|
813
|
-
status: 'lateral_thinking',
|
|
814
|
-
sessionId: lateralResult.value.session.sessionId,
|
|
815
|
-
lateralContext: lateralResult.value.lateralContext,
|
|
816
|
-
message: `Lateral thinking: ${lateralResult.value.lateralContext.persona} persona (attempt ${lateralResult.value.lateralContext.attemptNumber}/4). Use lateralContext to generate a lateral SpecPatch, then submit with evolve_lateral_result.`,
|
|
817
|
-
}, null, 2);
|
|
818
|
-
}
|
|
819
|
-
return formatError('Unexpected state in evolve_lateral');
|
|
820
|
-
}
|
|
821
|
-
case 'evolve_lateral_result': {
|
|
822
|
-
if (!input.sessionId)
|
|
823
|
-
return formatError('sessionId is required for evolve_lateral_result action');
|
|
824
|
-
if (!input.lateralResult)
|
|
825
|
-
return formatError('lateralResult is required for evolve_lateral_result action');
|
|
826
|
-
const lrResult = engine.submitLateralResult(input.sessionId, input.lateralResult);
|
|
827
|
-
if (!lrResult.ok)
|
|
828
|
-
return formatError(lrResult.error.message);
|
|
829
|
-
const { impactedTaskIds, reExecuteContext: lrReExecCtx } = lrResult.value;
|
|
830
|
-
if (impactedTaskIds.length === 0) {
|
|
831
|
-
return JSON.stringify({
|
|
832
|
-
status: 'lateral_patch_applied',
|
|
833
|
-
sessionId: lrResult.value.session.sessionId,
|
|
834
|
-
impactedTaskIds: [],
|
|
835
|
-
message: 'Lateral spec patch applied. No tasks need re-execution. Call evaluate to re-assess.',
|
|
836
|
-
}, null, 2);
|
|
837
|
-
}
|
|
838
|
-
return JSON.stringify({
|
|
839
|
-
status: 're_executing',
|
|
840
|
-
sessionId: lrResult.value.session.sessionId,
|
|
841
|
-
impactedTaskIds,
|
|
842
|
-
reExecuteContext: lrReExecCtx,
|
|
843
|
-
message: `Lateral spec patch applied. ${impactedTaskIds.length} tasks need re-execution. Use reExecuteContext to implement the task.`,
|
|
844
|
-
}, null, 2);
|
|
845
|
-
}
|
|
846
|
-
default:
|
|
847
|
-
return formatError(`Unknown action: ${input.action}`);
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
function buildStepResult(raw) {
|
|
851
|
-
switch (raw.principle) {
|
|
852
|
-
case 'figure_ground':
|
|
853
|
-
if (!raw.classifiedACs)
|
|
854
|
-
return null;
|
|
855
|
-
return { principle: 'figure_ground', classifiedACs: raw.classifiedACs };
|
|
856
|
-
case 'closure':
|
|
857
|
-
if (!raw.atomicTasks)
|
|
858
|
-
return null;
|
|
859
|
-
return { principle: 'closure', atomicTasks: raw.atomicTasks };
|
|
860
|
-
case 'proximity':
|
|
861
|
-
if (!raw.taskGroups)
|
|
862
|
-
return null;
|
|
863
|
-
return { principle: 'proximity', taskGroups: raw.taskGroups };
|
|
864
|
-
case 'continuity':
|
|
865
|
-
if (!raw.dagValidation)
|
|
866
|
-
return null;
|
|
867
|
-
return { principle: 'continuity', dagValidation: raw.dagValidation };
|
|
868
|
-
default:
|
|
869
|
-
return null;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
function handleStatus(engine, sessionId, cwd) {
|
|
873
|
-
try {
|
|
874
|
-
if (sessionId) {
|
|
875
|
-
const session = engine.getSession(sessionId);
|
|
876
|
-
// Build resumeContext for in-progress sessions
|
|
877
|
-
let resumeContext;
|
|
878
|
-
if (session.status === 'executing' && session.executionPlan) {
|
|
879
|
-
const completedSet = new Set(session.completedTaskIds);
|
|
880
|
-
const totalTasks = session.executionPlan.atomicTasks.length;
|
|
881
|
-
const nextTaskId = session.executionPlan.dagValidation.topologicalOrder.find((id) => !completedSet.has(id)) ?? null;
|
|
882
|
-
resumeContext = {
|
|
883
|
-
completedTaskIds: session.completedTaskIds,
|
|
884
|
-
nextTaskId,
|
|
885
|
-
totalTasks,
|
|
886
|
-
progressPercent: totalTasks > 0 ? Math.round((session.completedTaskIds.length / totalTasks) * 100) : 0,
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
return JSON.stringify({
|
|
890
|
-
session: {
|
|
891
|
-
sessionId: session.sessionId,
|
|
892
|
-
specId: session.specId,
|
|
893
|
-
status: session.status,
|
|
894
|
-
currentStep: session.currentStep,
|
|
895
|
-
stepsCompleted: session.planningSteps.length,
|
|
896
|
-
hasPlan: !!session.executionPlan,
|
|
897
|
-
taskResults: session.taskResults.length,
|
|
898
|
-
evaluateStage: session.evaluateStage ?? null,
|
|
899
|
-
hasEvaluation: !!session.evaluationResult,
|
|
900
|
-
driftAlerts: session.driftHistory.filter((d) => d.thresholdExceeded).length,
|
|
901
|
-
evolveStage: session.evolveStage ?? null,
|
|
902
|
-
currentGeneration: session.currentGeneration,
|
|
903
|
-
evolutionCount: session.evolutionHistory.length,
|
|
904
|
-
terminationReason: session.terminationReason ?? null,
|
|
905
|
-
...(resumeContext ? { resumeContext } : {}),
|
|
906
|
-
...(session.auditResult ? { hasAuditResult: true } : {}),
|
|
907
|
-
subTaskCount: session.subTasks.length,
|
|
908
|
-
createdAt: session.createdAt,
|
|
909
|
-
updatedAt: session.updatedAt,
|
|
910
|
-
},
|
|
911
|
-
}, null, 2);
|
|
912
|
-
}
|
|
913
|
-
const sessions = engine.listSessions();
|
|
914
|
-
let resumeHint;
|
|
915
|
-
if (cwd) {
|
|
916
|
-
try {
|
|
917
|
-
const activeSessionPath = join(cwd, '.gestalt', 'active-session.json');
|
|
918
|
-
if (existsSync(activeSessionPath)) {
|
|
919
|
-
resumeHint = JSON.parse(readFileSync(activeSessionPath, 'utf-8'));
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
catch {
|
|
923
|
-
// ignore
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
return JSON.stringify({
|
|
927
|
-
sessions: sessions.map((s) => ({
|
|
928
|
-
sessionId: s.sessionId,
|
|
929
|
-
specId: s.specId,
|
|
930
|
-
status: s.status,
|
|
931
|
-
stepsCompleted: s.planningSteps.length,
|
|
932
|
-
hasPlan: !!s.executionPlan,
|
|
933
|
-
taskResults: s.taskResults.length,
|
|
934
|
-
hasEvaluation: !!s.evaluationResult,
|
|
935
|
-
...(s.status === 'executing'
|
|
936
|
-
? {
|
|
937
|
-
resumeContext: {
|
|
938
|
-
completedTaskIds: s.completedTaskIds,
|
|
939
|
-
nextTaskId: s.nextTaskId,
|
|
940
|
-
totalTasks: s.executionPlan?.atomicTasks.length ?? 0,
|
|
941
|
-
progressPercent: s.executionPlan
|
|
942
|
-
? Math.round((s.completedTaskIds.length / s.executionPlan.atomicTasks.length) * 100)
|
|
943
|
-
: 0,
|
|
944
|
-
},
|
|
945
|
-
}
|
|
946
|
-
: {}),
|
|
947
|
-
createdAt: s.createdAt,
|
|
948
|
-
})),
|
|
949
|
-
total: sessions.length,
|
|
950
|
-
...(resumeHint ? { resumeHint } : {}),
|
|
951
|
-
}, null, 2);
|
|
952
|
-
}
|
|
953
|
-
catch (e) {
|
|
954
|
-
return formatError(e instanceof Error ? e.message : String(e));
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
// ─── Response Slim Helpers ─────────────────────────────────────────────────
|
|
958
|
-
// systemPrompt is static per session (same agent persona every call).
|
|
959
|
-
// Stripping it from responses saves ~500 tokens × N calls in context history.
|
|
960
|
-
// pendingTasks description/sourceAC are not needed at execution time.
|
|
961
|
-
function slimTaskContext(ctx) {
|
|
962
|
-
const { pendingTasks, ...rest } = ctx;
|
|
963
|
-
return {
|
|
964
|
-
...rest,
|
|
965
|
-
...(Array.isArray(pendingTasks)
|
|
966
|
-
? {
|
|
967
|
-
pendingTasks: pendingTasks.map(({ taskId, title, dependsOn }) => ({
|
|
968
|
-
taskId,
|
|
969
|
-
title,
|
|
970
|
-
dependsOn,
|
|
971
|
-
})),
|
|
972
|
-
}
|
|
973
|
-
: {}),
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
function slimRetrospectiveContext(ctx) {
|
|
977
|
-
const { systemPrompt: _sp, ...rest } = ctx;
|
|
978
|
-
return rest;
|
|
979
|
-
}
|
|
980
|
-
// ─── Verbose=false Helpers ──────────────────────────────────────────────────
|
|
981
|
-
// When verbose=false, strip large prompt fields to reduce response token usage.
|
|
982
|
-
// Callers that omit verbose (default=true) receive identical responses as before.
|
|
983
|
-
const PROMPT_KEYS = ['systemPrompt', 'planningPrompt', 'taskPrompt'];
|
|
984
|
-
function stripContextPrompts(ctx) {
|
|
985
|
-
if (!ctx || typeof ctx !== 'object')
|
|
986
|
-
return ctx;
|
|
987
|
-
const result = { ...ctx };
|
|
988
|
-
for (const key of PROMPT_KEYS) {
|
|
989
|
-
delete result[key];
|
|
990
|
-
}
|
|
991
|
-
return result;
|
|
992
|
-
}
|
|
993
|
-
function applyTaskContextFilters(ctx, verbose) {
|
|
994
|
-
const slimmed = slimTaskContext(ctx);
|
|
995
|
-
return verbose ? slimmed : stripContextPrompts(slimmed);
|
|
996
|
-
}
|
|
997
|
-
function formatError(message) {
|
|
998
|
-
return JSON.stringify({ error: message }, null, 2);
|
|
29
|
+
const handler = handlers[input.action];
|
|
30
|
+
if (!handler)
|
|
31
|
+
return formatError(`Unknown action: ${input.action}`);
|
|
32
|
+
return handler(engine, input, client);
|
|
999
33
|
}
|
|
1000
34
|
//# sourceMappingURL=execute-passthrough.js.map
|