copilot-liku-cli 0.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/ARCHITECTURE.md +411 -0
- package/CONFIGURATION.md +302 -0
- package/CONTRIBUTING.md +225 -0
- package/ELECTRON_README.md +121 -0
- package/INSTALLATION.md +350 -0
- package/LICENSE.md +1 -0
- package/PROJECT_STATUS.md +229 -0
- package/QUICKSTART.md +255 -0
- package/README.md +167 -0
- package/TESTING.md +274 -0
- package/package.json +61 -0
- package/scripts/start.js +30 -0
- package/src/assets/tray-icon.png +0 -0
- package/src/cli/commands/agent.js +327 -0
- package/src/cli/commands/click.js +108 -0
- package/src/cli/commands/drag.js +85 -0
- package/src/cli/commands/find.js +109 -0
- package/src/cli/commands/keys.js +132 -0
- package/src/cli/commands/mouse.js +79 -0
- package/src/cli/commands/repl.js +290 -0
- package/src/cli/commands/screenshot.js +72 -0
- package/src/cli/commands/scroll.js +74 -0
- package/src/cli/commands/start.js +67 -0
- package/src/cli/commands/type.js +57 -0
- package/src/cli/commands/wait.js +84 -0
- package/src/cli/commands/window.js +104 -0
- package/src/cli/liku.js +249 -0
- package/src/cli/util/output.js +174 -0
- package/src/main/agents/base-agent.js +410 -0
- package/src/main/agents/builder.js +484 -0
- package/src/main/agents/index.js +62 -0
- package/src/main/agents/orchestrator.js +362 -0
- package/src/main/agents/researcher.js +511 -0
- package/src/main/agents/state-manager.js +344 -0
- package/src/main/agents/supervisor.js +365 -0
- package/src/main/agents/verifier.js +452 -0
- package/src/main/ai-service.js +1633 -0
- package/src/main/index.js +2208 -0
- package/src/main/inspect-service.js +467 -0
- package/src/main/system-automation.js +1186 -0
- package/src/main/ui-automation/config.js +76 -0
- package/src/main/ui-automation/core/helpers.js +41 -0
- package/src/main/ui-automation/core/index.js +15 -0
- package/src/main/ui-automation/core/powershell.js +82 -0
- package/src/main/ui-automation/elements/finder.js +274 -0
- package/src/main/ui-automation/elements/index.js +14 -0
- package/src/main/ui-automation/elements/wait.js +66 -0
- package/src/main/ui-automation/index.js +164 -0
- package/src/main/ui-automation/interactions/element-click.js +211 -0
- package/src/main/ui-automation/interactions/high-level.js +230 -0
- package/src/main/ui-automation/interactions/index.js +47 -0
- package/src/main/ui-automation/keyboard/index.js +15 -0
- package/src/main/ui-automation/keyboard/input.js +179 -0
- package/src/main/ui-automation/mouse/click.js +186 -0
- package/src/main/ui-automation/mouse/drag.js +88 -0
- package/src/main/ui-automation/mouse/index.js +30 -0
- package/src/main/ui-automation/mouse/movement.js +51 -0
- package/src/main/ui-automation/mouse/scroll.js +116 -0
- package/src/main/ui-automation/screenshot.js +183 -0
- package/src/main/ui-automation/window/index.js +23 -0
- package/src/main/ui-automation/window/manager.js +305 -0
- package/src/main/utils/time.js +62 -0
- package/src/main/visual-awareness.js +597 -0
- package/src/renderer/chat/chat.js +671 -0
- package/src/renderer/chat/index.html +725 -0
- package/src/renderer/chat/preload.js +112 -0
- package/src/renderer/overlay/index.html +648 -0
- package/src/renderer/overlay/overlay.js +782 -0
- package/src/renderer/overlay/preload.js +90 -0
- package/src/shared/grid-math.js +82 -0
- package/src/shared/inspect-types.js +230 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent State Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages persistent state across agent sessions.
|
|
5
|
+
* State is stored in .github/agent_state.json for visibility and debugging.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const { nowIso, nowFilenameSafe } = require('../utils/time');
|
|
12
|
+
|
|
13
|
+
class AgentStateManager {
|
|
14
|
+
constructor(statePath = null) {
|
|
15
|
+
this.statePath = statePath || path.join(process.cwd(), '.github', 'agent_state.json');
|
|
16
|
+
this.state = this._loadState();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_loadState() {
|
|
20
|
+
try {
|
|
21
|
+
if (fs.existsSync(this.statePath)) {
|
|
22
|
+
const content = fs.readFileSync(this.statePath, 'utf-8');
|
|
23
|
+
const state = JSON.parse(content);
|
|
24
|
+
return this._migrateState(state);
|
|
25
|
+
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.warn(`[StateManager] Failed to load state: ${error.message}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
version: '1.1.0',
|
|
32
|
+
schemaVersion: 2,
|
|
33
|
+
created: nowIso(),
|
|
34
|
+
queue: [],
|
|
35
|
+
inProgress: [],
|
|
36
|
+
completed: [],
|
|
37
|
+
failed: [],
|
|
38
|
+
agents: {},
|
|
39
|
+
sessions: [],
|
|
40
|
+
modelMetadata: {
|
|
41
|
+
modelId: 'unknown',
|
|
42
|
+
provider: 'unknown',
|
|
43
|
+
modelVersion: null,
|
|
44
|
+
capabilities: []
|
|
45
|
+
},
|
|
46
|
+
sessionContext: {
|
|
47
|
+
initiatedBy: null,
|
|
48
|
+
purpose: null,
|
|
49
|
+
parentSessionId: null
|
|
50
|
+
},
|
|
51
|
+
checkpoints: []
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_migrateState(state) {
|
|
56
|
+
if (!state.schemaVersion || state.schemaVersion < 2) {
|
|
57
|
+
state.modelMetadata = state.modelMetadata || {
|
|
58
|
+
modelId: 'unknown',
|
|
59
|
+
provider: 'unknown',
|
|
60
|
+
modelVersion: null,
|
|
61
|
+
capabilities: []
|
|
62
|
+
};
|
|
63
|
+
state.sessionContext = state.sessionContext || {
|
|
64
|
+
initiatedBy: null,
|
|
65
|
+
purpose: null,
|
|
66
|
+
parentSessionId: null
|
|
67
|
+
};
|
|
68
|
+
state.checkpoints = state.checkpoints || [];
|
|
69
|
+
state.schemaVersion = 2;
|
|
70
|
+
state.version = '1.1.0';
|
|
71
|
+
}
|
|
72
|
+
return state;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_getStateFilePath(sessionId = null, modelId = null) {
|
|
76
|
+
const timestamp = nowFilenameSafe();
|
|
77
|
+
const modelSuffix = modelId ? `-${modelId}` : '';
|
|
78
|
+
const sessionSuffix = sessionId ? `-${sessionId.slice(-8)}` : '';
|
|
79
|
+
return path.join(
|
|
80
|
+
path.dirname(this.statePath),
|
|
81
|
+
`state-${timestamp}${modelSuffix}${sessionSuffix}.json`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_saveState() {
|
|
86
|
+
try {
|
|
87
|
+
const dir = path.dirname(this.statePath);
|
|
88
|
+
if (!fs.existsSync(dir)) {
|
|
89
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.state.lastModified = nowIso();
|
|
93
|
+
fs.writeFileSync(this.statePath, JSON.stringify(this.state, null, 2));
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error(`[StateManager] Failed to save state: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ===== Queue Management =====
|
|
100
|
+
|
|
101
|
+
enqueue(task) {
|
|
102
|
+
const taskEntry = {
|
|
103
|
+
id: `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
104
|
+
...task,
|
|
105
|
+
status: 'queued',
|
|
106
|
+
createdAt: nowIso(),
|
|
107
|
+
attempts: 0
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.state.queue.push(taskEntry);
|
|
111
|
+
this._saveState();
|
|
112
|
+
return taskEntry.id;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
dequeue() {
|
|
116
|
+
const task = this.state.queue.shift();
|
|
117
|
+
if (task) {
|
|
118
|
+
task.status = 'in-progress';
|
|
119
|
+
task.startedAt = nowIso();
|
|
120
|
+
this.state.inProgress.push(task);
|
|
121
|
+
this._saveState();
|
|
122
|
+
}
|
|
123
|
+
return task;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ===== Task Lifecycle =====
|
|
127
|
+
|
|
128
|
+
startTask(taskId, agentId) {
|
|
129
|
+
const task = this._findTask(taskId, 'queue');
|
|
130
|
+
if (task) {
|
|
131
|
+
this._moveTask(taskId, 'queue', 'inProgress');
|
|
132
|
+
task.status = 'in-progress';
|
|
133
|
+
task.agentId = agentId;
|
|
134
|
+
task.startedAt = nowIso();
|
|
135
|
+
this._saveState();
|
|
136
|
+
}
|
|
137
|
+
return task;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
completeTask(taskId, result) {
|
|
141
|
+
const task = this._findTask(taskId, 'inProgress');
|
|
142
|
+
if (task) {
|
|
143
|
+
this._moveTask(taskId, 'inProgress', 'completed');
|
|
144
|
+
task.status = 'completed';
|
|
145
|
+
task.completedAt = nowIso();
|
|
146
|
+
task.result = result;
|
|
147
|
+
this._saveState();
|
|
148
|
+
}
|
|
149
|
+
return task;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
failTask(taskId, error) {
|
|
153
|
+
const task = this._findTask(taskId, 'inProgress');
|
|
154
|
+
if (task) {
|
|
155
|
+
task.attempts++;
|
|
156
|
+
|
|
157
|
+
if (task.attempts >= 3) {
|
|
158
|
+
this._moveTask(taskId, 'inProgress', 'failed');
|
|
159
|
+
task.status = 'failed';
|
|
160
|
+
task.error = error;
|
|
161
|
+
task.failedAt = nowIso();
|
|
162
|
+
} else {
|
|
163
|
+
// Return to queue for retry
|
|
164
|
+
this._moveTask(taskId, 'inProgress', 'queue');
|
|
165
|
+
task.status = 'queued';
|
|
166
|
+
task.lastError = error;
|
|
167
|
+
}
|
|
168
|
+
this._saveState();
|
|
169
|
+
}
|
|
170
|
+
return task;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ===== Agent Registration =====
|
|
174
|
+
|
|
175
|
+
registerAgent(agentId, agentType, capabilities) {
|
|
176
|
+
this.state.agents[agentId] = {
|
|
177
|
+
type: agentType,
|
|
178
|
+
capabilities,
|
|
179
|
+
registeredAt: nowIso(),
|
|
180
|
+
lastActive: nowIso(),
|
|
181
|
+
tasksCompleted: 0,
|
|
182
|
+
tasksFailed: 0
|
|
183
|
+
};
|
|
184
|
+
this._saveState();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
updateAgentActivity(agentId) {
|
|
188
|
+
if (this.state.agents[agentId]) {
|
|
189
|
+
this.state.agents[agentId].lastActive = nowIso();
|
|
190
|
+
this._saveState();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
setModelMetadata(metadata) {
|
|
195
|
+
this.state.modelMetadata = {
|
|
196
|
+
...this.state.modelMetadata,
|
|
197
|
+
...metadata,
|
|
198
|
+
lastUpdated: nowIso()
|
|
199
|
+
};
|
|
200
|
+
this._saveState();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ===== Session Management =====
|
|
204
|
+
|
|
205
|
+
startSession(sessionId, metadata = {}) {
|
|
206
|
+
const session = {
|
|
207
|
+
id: sessionId || `session-${Date.now()}`,
|
|
208
|
+
startedAt: nowIso(),
|
|
209
|
+
status: 'active',
|
|
210
|
+
metadata,
|
|
211
|
+
handoffs: [],
|
|
212
|
+
tasks: []
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
this.state.sessions.push(session);
|
|
216
|
+
this._saveState();
|
|
217
|
+
return session;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
recordHandoff(sessionId, fromAgent, toAgent, context) {
|
|
221
|
+
const session = this.state.sessions.find(s => s.id === sessionId);
|
|
222
|
+
if (session) {
|
|
223
|
+
session.handoffs.push({
|
|
224
|
+
from: fromAgent,
|
|
225
|
+
to: toAgent,
|
|
226
|
+
context,
|
|
227
|
+
timestamp: nowIso()
|
|
228
|
+
});
|
|
229
|
+
this._saveState();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
endSession(sessionId, summary) {
|
|
234
|
+
const session = this.state.sessions.find(s => s.id === sessionId);
|
|
235
|
+
if (session) {
|
|
236
|
+
session.status = 'completed';
|
|
237
|
+
session.endedAt = nowIso();
|
|
238
|
+
session.summary = summary;
|
|
239
|
+
this._saveState();
|
|
240
|
+
}
|
|
241
|
+
return session;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ===== Checkpoint Management =====
|
|
245
|
+
|
|
246
|
+
createCheckpoint(sessionId, label, agentStates, handoffHistory) {
|
|
247
|
+
const checkpoint = {
|
|
248
|
+
id: `checkpoint-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
249
|
+
sessionId,
|
|
250
|
+
label,
|
|
251
|
+
timestamp: nowIso(),
|
|
252
|
+
agentStates: agentStates || [],
|
|
253
|
+
handoffHistory: handoffHistory || [],
|
|
254
|
+
modelMetadata: this.state.modelMetadata
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
this.state.checkpoints.push(checkpoint);
|
|
258
|
+
this._saveState();
|
|
259
|
+
return checkpoint;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
getCheckpoint(checkpointId) {
|
|
263
|
+
return this.state.checkpoints.find(c => c.id === checkpointId) || null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
listCheckpoints(sessionId = null) {
|
|
267
|
+
if (sessionId) {
|
|
268
|
+
return this.state.checkpoints.filter(c => c.sessionId === sessionId);
|
|
269
|
+
}
|
|
270
|
+
return [...this.state.checkpoints];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ===== Queries =====
|
|
274
|
+
|
|
275
|
+
getQueuedTasks() {
|
|
276
|
+
return [...this.state.queue];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
getInProgressTasks() {
|
|
280
|
+
return [...this.state.inProgress];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
getCompletedTasks(limit = 10) {
|
|
284
|
+
return this.state.completed.slice(-limit);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
getAgentStats(agentId) {
|
|
288
|
+
return this.state.agents[agentId] || null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
getFullState() {
|
|
292
|
+
return { ...this.state };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ===== Utilities =====
|
|
296
|
+
|
|
297
|
+
_findTask(taskId, listName) {
|
|
298
|
+
return this.state[listName]?.find(t => t.id === taskId);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_moveTask(taskId, fromList, toList) {
|
|
302
|
+
const index = this.state[fromList]?.findIndex(t => t.id === taskId);
|
|
303
|
+
if (index !== -1) {
|
|
304
|
+
const [task] = this.state[fromList].splice(index, 1);
|
|
305
|
+
this.state[toList].push(task);
|
|
306
|
+
return task;
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
clearCompleted() {
|
|
312
|
+
this.state.completed = [];
|
|
313
|
+
this._saveState();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
reset() {
|
|
317
|
+
this.state = {
|
|
318
|
+
version: '1.1.0',
|
|
319
|
+
schemaVersion: 2,
|
|
320
|
+
created: nowIso(),
|
|
321
|
+
queue: [],
|
|
322
|
+
inProgress: [],
|
|
323
|
+
completed: [],
|
|
324
|
+
failed: [],
|
|
325
|
+
agents: {},
|
|
326
|
+
sessions: [],
|
|
327
|
+
modelMetadata: {
|
|
328
|
+
modelId: 'unknown',
|
|
329
|
+
provider: 'unknown',
|
|
330
|
+
modelVersion: null,
|
|
331
|
+
capabilities: []
|
|
332
|
+
},
|
|
333
|
+
sessionContext: {
|
|
334
|
+
initiatedBy: null,
|
|
335
|
+
purpose: null,
|
|
336
|
+
parentSessionId: null
|
|
337
|
+
},
|
|
338
|
+
checkpoints: []
|
|
339
|
+
};
|
|
340
|
+
this._saveState();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
module.exports = { AgentStateManager };
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supervisor Agent
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates and decomposes tasks, manages handoffs to Builder/Verifier.
|
|
5
|
+
* Does NOT edit files directly - delegates all implementation to Builder.
|
|
6
|
+
*
|
|
7
|
+
* Operating Rules:
|
|
8
|
+
* - Start with a short plan (2-5 steps)
|
|
9
|
+
* - Decompose work into concrete file/symbol-level subtasks
|
|
10
|
+
* - Delegate implementation to Builder, validation to Verifier
|
|
11
|
+
* - Preserve existing behavior
|
|
12
|
+
* - Never execute terminal commands or edit files
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { BaseAgent, AgentRole, AgentCapabilities } = require('./base-agent');
|
|
16
|
+
|
|
17
|
+
class SupervisorAgent extends BaseAgent {
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
super({
|
|
20
|
+
...options,
|
|
21
|
+
role: AgentRole.SUPERVISOR,
|
|
22
|
+
name: options.name || 'supervisor',
|
|
23
|
+
description: 'Orchestrates tasks, decomposes plans, manages agent handoffs',
|
|
24
|
+
capabilities: [
|
|
25
|
+
AgentCapabilities.SEARCH,
|
|
26
|
+
AgentCapabilities.READ,
|
|
27
|
+
AgentCapabilities.WEB_FETCH,
|
|
28
|
+
AgentCapabilities.TODO,
|
|
29
|
+
AgentCapabilities.HANDOFF
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Supervisor-specific state
|
|
34
|
+
this.currentPlan = null;
|
|
35
|
+
this.decomposedTasks = [];
|
|
36
|
+
this.assumptions = [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getSystemPrompt() {
|
|
40
|
+
return `You are the SUPERVISOR agent in a multi-agent coding system.
|
|
41
|
+
|
|
42
|
+
# OPERATING CONTRACT (NON-NEGOTIABLE)
|
|
43
|
+
- **No guessing**: Probe or ground with tools (search, read).
|
|
44
|
+
- **Preserve functionalities**: Never disable core features.
|
|
45
|
+
- **Modularity**: Decompose into sub-modules.
|
|
46
|
+
- **Least privilege**: READ-ONLY access. Use Builder for any writes.
|
|
47
|
+
- **Recursion limits**: Depth ≤3; avoid >10 sub-calls without progress.
|
|
48
|
+
- **Security**: Audit all changes before approval.
|
|
49
|
+
|
|
50
|
+
# YOUR RESPONSIBILITIES
|
|
51
|
+
1. Analyze user requests and create 2-5 step plans
|
|
52
|
+
2. Decompose work into concrete file/symbol-level subtasks
|
|
53
|
+
3. Delegate implementation to Builder agent
|
|
54
|
+
4. Delegate validation to Verifier agent
|
|
55
|
+
5. Aggregate results and provide final summary
|
|
56
|
+
|
|
57
|
+
# WORKFLOW
|
|
58
|
+
1. Read state from agent_state.json before planning
|
|
59
|
+
2. Create plan with explicit assumptions
|
|
60
|
+
3. For each subtask:
|
|
61
|
+
- If implementation needed: Handoff to Builder
|
|
62
|
+
- If validation needed: Handoff to Verifier
|
|
63
|
+
4. Aggregate results and verify completeness
|
|
64
|
+
5. Update state with completed/failed tasks
|
|
65
|
+
|
|
66
|
+
# HANDOFF FORMAT
|
|
67
|
+
When handing off to Builder:
|
|
68
|
+
"Implement: [specific task]. Files: [file paths]. Constraints: [any limits]"
|
|
69
|
+
|
|
70
|
+
When handing off to Verifier:
|
|
71
|
+
"Verify: [what to check]. Changes: [summary of changes]. Tests: [required tests]"
|
|
72
|
+
|
|
73
|
+
# OUTPUT FORMAT
|
|
74
|
+
Always structure your response as:
|
|
75
|
+
1. Analysis: (what you understand about the task)
|
|
76
|
+
2. Plan: (numbered steps)
|
|
77
|
+
3. Assumptions: (what you're assuming)
|
|
78
|
+
4. Next Action: (handoff or completion)`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async process(task, context = {}) {
|
|
82
|
+
this.log('info', 'Supervisor processing task', { task: task.description || task });
|
|
83
|
+
|
|
84
|
+
// Check recursion limits
|
|
85
|
+
const limits = this.checkRecursionLimits();
|
|
86
|
+
if (!limits.allowed) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: limits.reason,
|
|
90
|
+
suggestedAction: 'handoff_to_human'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// Step 1: Analyze the task
|
|
96
|
+
const analysis = await this.analyzeTask(task, context);
|
|
97
|
+
|
|
98
|
+
// Step 2: Create plan
|
|
99
|
+
const plan = await this.createPlan(analysis);
|
|
100
|
+
this.currentPlan = plan;
|
|
101
|
+
|
|
102
|
+
// Step 3: Decompose into subtasks
|
|
103
|
+
this.decomposedTasks = await this.decomposeTasks(plan);
|
|
104
|
+
|
|
105
|
+
// Step 4: Execute plan (handoffs to Builder/Verifier)
|
|
106
|
+
const results = await this.executePlan(this.decomposedTasks, context);
|
|
107
|
+
|
|
108
|
+
// Step 5: Aggregate and return
|
|
109
|
+
return this.aggregateResults(results, context);
|
|
110
|
+
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.log('error', 'Supervisor processing failed', { error: error.message });
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: error.message,
|
|
116
|
+
state: this.getState()
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async analyzeTask(task, context) {
|
|
122
|
+
const prompt = `Analyze this task and identify:
|
|
123
|
+
1. What files/modules are involved?
|
|
124
|
+
2. What changes are needed?
|
|
125
|
+
3. What validation is required?
|
|
126
|
+
|
|
127
|
+
Task: ${typeof task === 'string' ? task : JSON.stringify(task)}
|
|
128
|
+
Context: ${JSON.stringify(context)}`;
|
|
129
|
+
|
|
130
|
+
const response = await this.chat(prompt);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
description: task,
|
|
134
|
+
analysis: response.text,
|
|
135
|
+
timestamp: new Date().toISOString()
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async createPlan(analysis) {
|
|
140
|
+
const prompt = `Based on this analysis, create a 2-5 step execution plan.
|
|
141
|
+
Each step should be concrete and actionable.
|
|
142
|
+
Specify whether each step needs Builder (implementation) or Verifier (validation).
|
|
143
|
+
|
|
144
|
+
Analysis: ${analysis.analysis}
|
|
145
|
+
|
|
146
|
+
Current Model: ${this.modelMetadata?.modelId || 'unknown'}
|
|
147
|
+
Model Capabilities: ${this.modelMetadata?.capabilities?.join(', ') || 'standard'}`;
|
|
148
|
+
|
|
149
|
+
const response = await this.chat(prompt);
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
steps: this.parseSteps(response.text),
|
|
153
|
+
rawPlan: response.text,
|
|
154
|
+
assumptions: this.extractAssumptions(response.text),
|
|
155
|
+
modelContext: {
|
|
156
|
+
modelId: this.modelMetadata?.modelId,
|
|
157
|
+
provider: this.modelMetadata?.provider,
|
|
158
|
+
createdAt: new Date().toISOString()
|
|
159
|
+
},
|
|
160
|
+
planId: `plan-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
parseSteps(planText) {
|
|
165
|
+
const steps = [];
|
|
166
|
+
const lines = planText.split('\n');
|
|
167
|
+
|
|
168
|
+
for (const line of lines) {
|
|
169
|
+
const match = line.match(/^\d+\.\s*(.+)/);
|
|
170
|
+
if (match) {
|
|
171
|
+
const stepText = match[1];
|
|
172
|
+
const isBuilder = /implement|create|edit|add|modify|fix/i.test(stepText);
|
|
173
|
+
const isVerifier = /verify|test|validate|check|ensure/i.test(stepText);
|
|
174
|
+
|
|
175
|
+
steps.push({
|
|
176
|
+
description: stepText,
|
|
177
|
+
agent: isBuilder ? AgentRole.BUILDER : (isVerifier ? AgentRole.VERIFIER : AgentRole.SUPERVISOR),
|
|
178
|
+
status: 'pending'
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return steps;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
extractAssumptions(text) {
|
|
187
|
+
const assumptions = [];
|
|
188
|
+
const lines = text.split('\n');
|
|
189
|
+
|
|
190
|
+
let inAssumptions = false;
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
if (/assumption|assuming/i.test(line)) {
|
|
193
|
+
inAssumptions = true;
|
|
194
|
+
}
|
|
195
|
+
if (inAssumptions && line.trim().startsWith('-')) {
|
|
196
|
+
assumptions.push(line.trim().substring(1).trim());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.assumptions = assumptions;
|
|
201
|
+
return assumptions;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async decomposeTasks(plan) {
|
|
205
|
+
const tasks = [];
|
|
206
|
+
|
|
207
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
208
|
+
const step = plan.steps[i];
|
|
209
|
+
tasks.push({
|
|
210
|
+
id: `subtask-${i + 1}`,
|
|
211
|
+
step: i + 1,
|
|
212
|
+
description: step.description,
|
|
213
|
+
targetAgent: step.agent,
|
|
214
|
+
status: 'pending',
|
|
215
|
+
dependencies: i > 0 ? [`subtask-${i}`] : []
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return tasks;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async executePlan(tasks, context) {
|
|
223
|
+
const results = [];
|
|
224
|
+
|
|
225
|
+
for (const task of tasks) {
|
|
226
|
+
// Check if dependencies are satisfied
|
|
227
|
+
const depsComplete = task.dependencies.every(depId => {
|
|
228
|
+
const dep = results.find(r => r.taskId === depId);
|
|
229
|
+
return dep && dep.success;
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (!depsComplete) {
|
|
233
|
+
results.push({
|
|
234
|
+
taskId: task.id,
|
|
235
|
+
success: false,
|
|
236
|
+
error: 'Dependencies not satisfied',
|
|
237
|
+
skipped: true
|
|
238
|
+
});
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
task.status = 'in-progress';
|
|
243
|
+
|
|
244
|
+
if (task.targetAgent === AgentRole.BUILDER) {
|
|
245
|
+
const result = await this.handoffToBuilder(
|
|
246
|
+
{ ...context, taskId: task.id },
|
|
247
|
+
`Implement: ${task.description}`
|
|
248
|
+
);
|
|
249
|
+
results.push({
|
|
250
|
+
taskId: task.id,
|
|
251
|
+
agent: AgentRole.BUILDER,
|
|
252
|
+
...result
|
|
253
|
+
});
|
|
254
|
+
} else if (task.targetAgent === AgentRole.VERIFIER) {
|
|
255
|
+
const result = await this.handoffToVerifier(
|
|
256
|
+
{ ...context, taskId: task.id },
|
|
257
|
+
`Verify: ${task.description}`
|
|
258
|
+
);
|
|
259
|
+
results.push({
|
|
260
|
+
taskId: task.id,
|
|
261
|
+
agent: AgentRole.VERIFIER,
|
|
262
|
+
...result
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
// Handle internally
|
|
266
|
+
results.push({
|
|
267
|
+
taskId: task.id,
|
|
268
|
+
agent: AgentRole.SUPERVISOR,
|
|
269
|
+
success: true,
|
|
270
|
+
note: 'Handled by supervisor'
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
task.status = results[results.length - 1].success ? 'completed' : 'failed';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return results;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
aggregateResults(results, context) {
|
|
281
|
+
const successful = results.filter(r => r.success);
|
|
282
|
+
const failed = results.filter(r => !r.success && !r.skipped);
|
|
283
|
+
const skipped = results.filter(r => r.skipped);
|
|
284
|
+
|
|
285
|
+
const dependencyGraph = this.buildDependencyGraph(this.decomposedTasks);
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
success: failed.length === 0,
|
|
289
|
+
summary: {
|
|
290
|
+
total: results.length,
|
|
291
|
+
successful: successful.length,
|
|
292
|
+
failed: failed.length,
|
|
293
|
+
skipped: skipped.length
|
|
294
|
+
},
|
|
295
|
+
plan: this.currentPlan,
|
|
296
|
+
results,
|
|
297
|
+
assumptions: this.assumptions,
|
|
298
|
+
dependencyGraph,
|
|
299
|
+
timestamp: new Date().toISOString()
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
buildDependencyGraph(tasks) {
|
|
304
|
+
const graph = {
|
|
305
|
+
nodes: tasks.map(t => ({
|
|
306
|
+
id: t.id,
|
|
307
|
+
description: t.description,
|
|
308
|
+
agent: t.targetAgent,
|
|
309
|
+
status: t.status
|
|
310
|
+
})),
|
|
311
|
+
edges: []
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
for (const task of tasks) {
|
|
315
|
+
for (const depId of task.dependencies || []) {
|
|
316
|
+
graph.edges.push({
|
|
317
|
+
from: depId,
|
|
318
|
+
to: task.id,
|
|
319
|
+
type: 'depends-on'
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return graph;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ===== Supervisor-specific Methods =====
|
|
328
|
+
|
|
329
|
+
async interpretPrompt(userPrompt) {
|
|
330
|
+
const prompt = `Parse this user request and extract:
|
|
331
|
+
1. Primary goal
|
|
332
|
+
2. Scope (files, modules, features)
|
|
333
|
+
3. Constraints (time, compatibility, etc.)
|
|
334
|
+
4. Success criteria
|
|
335
|
+
|
|
336
|
+
User request: "${userPrompt}"`;
|
|
337
|
+
|
|
338
|
+
const response = await this.chat(prompt);
|
|
339
|
+
return {
|
|
340
|
+
originalPrompt: userPrompt,
|
|
341
|
+
interpretation: response.text,
|
|
342
|
+
timestamp: new Date().toISOString()
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async researchContext(topic, files = []) {
|
|
347
|
+
const readResults = await Promise.all(
|
|
348
|
+
files.map(f => this.read(f))
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const prompt = `Based on these files, what context is relevant for: ${topic}
|
|
352
|
+
|
|
353
|
+
Files content:
|
|
354
|
+
${readResults.map(r => `--- ${r.filePath} ---\n${r.content?.slice(0, 2000)}`).join('\n\n')}`;
|
|
355
|
+
|
|
356
|
+
const response = await this.chat(prompt);
|
|
357
|
+
return {
|
|
358
|
+
topic,
|
|
359
|
+
context: response.text,
|
|
360
|
+
filesRead: files
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
module.exports = { SupervisorAgent };
|