chainlesschain 0.37.12 → 0.40.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/package.json +3 -2
- package/src/commands/agent.js +7 -1
- package/src/commands/ask.js +24 -9
- package/src/commands/chat.js +7 -1
- package/src/commands/cli-anything.js +266 -0
- package/src/commands/compliance.js +216 -0
- package/src/commands/dao.js +312 -0
- package/src/commands/dlp.js +278 -0
- package/src/commands/evomap.js +558 -0
- package/src/commands/hardening.js +230 -0
- package/src/commands/matrix.js +168 -0
- package/src/commands/nostr.js +185 -0
- package/src/commands/pqc.js +162 -0
- package/src/commands/scim.js +218 -0
- package/src/commands/serve.js +109 -0
- package/src/commands/siem.js +156 -0
- package/src/commands/social.js +480 -0
- package/src/commands/terraform.js +148 -0
- package/src/constants.js +1 -0
- package/src/index.js +60 -0
- package/src/lib/autonomous-agent.js +487 -0
- package/src/lib/cli-anything-bridge.js +379 -0
- package/src/lib/cli-context-engineering.js +472 -0
- package/src/lib/compliance-manager.js +290 -0
- package/src/lib/content-recommender.js +205 -0
- package/src/lib/dao-governance.js +296 -0
- package/src/lib/dlp-engine.js +304 -0
- package/src/lib/evomap-client.js +135 -0
- package/src/lib/evomap-federation.js +240 -0
- package/src/lib/evomap-governance.js +250 -0
- package/src/lib/evomap-manager.js +227 -0
- package/src/lib/git-integration.js +1 -1
- package/src/lib/hardening-manager.js +275 -0
- package/src/lib/llm-providers.js +14 -1
- package/src/lib/matrix-bridge.js +196 -0
- package/src/lib/nostr-bridge.js +195 -0
- package/src/lib/permanent-memory.js +370 -0
- package/src/lib/plan-mode.js +211 -0
- package/src/lib/pqc-manager.js +196 -0
- package/src/lib/scim-manager.js +212 -0
- package/src/lib/session-manager.js +38 -0
- package/src/lib/siem-exporter.js +137 -0
- package/src/lib/social-manager.js +283 -0
- package/src/lib/task-model-selector.js +232 -0
- package/src/lib/terraform-manager.js +201 -0
- package/src/lib/ws-server.js +474 -0
- package/src/repl/agent-repl.js +796 -41
- package/src/repl/chat-repl.js +14 -6
package/src/index.js
CHANGED
|
@@ -47,14 +47,44 @@ import { registerA2aCommand } from "./commands/a2a.js";
|
|
|
47
47
|
import { registerSandboxCommand } from "./commands/sandbox.js";
|
|
48
48
|
import { registerEvolutionCommand } from "./commands/evolution.js";
|
|
49
49
|
|
|
50
|
+
// Phase 7: EvoMap Federation + DAO Governance
|
|
51
|
+
import { registerDaoCommand } from "./commands/dao.js";
|
|
52
|
+
|
|
50
53
|
// Phase 8: Blockchain & Enterprise
|
|
51
54
|
import { registerEconomyCommand } from "./commands/economy.js";
|
|
52
55
|
import { registerZkpCommand } from "./commands/zkp.js";
|
|
53
56
|
import { registerBiCommand } from "./commands/bi.js";
|
|
54
57
|
|
|
58
|
+
// Phase 8: Security & Compliance
|
|
59
|
+
import { registerComplianceCommand } from "./commands/compliance.js";
|
|
60
|
+
import { registerDlpCommand } from "./commands/dlp.js";
|
|
61
|
+
import { registerSiemCommand } from "./commands/siem.js";
|
|
62
|
+
import { registerPqcCommand } from "./commands/pqc.js";
|
|
63
|
+
|
|
64
|
+
// Phase 8: Communication Bridges
|
|
65
|
+
import { registerNostrCommand } from "./commands/nostr.js";
|
|
66
|
+
import { registerMatrixCommand } from "./commands/matrix.js";
|
|
67
|
+
import { registerScimCommand } from "./commands/scim.js";
|
|
68
|
+
|
|
69
|
+
// Phase 8: Infrastructure & Hardening
|
|
70
|
+
import { registerTerraformCommand } from "./commands/terraform.js";
|
|
71
|
+
import { registerHardeningCommand } from "./commands/hardening.js";
|
|
72
|
+
|
|
73
|
+
// Phase 8: Social Platform
|
|
74
|
+
import { registerSocialCommand } from "./commands/social.js";
|
|
75
|
+
|
|
55
76
|
// Phase 9: Low-Code & Multi-Agent
|
|
56
77
|
import { registerLowcodeCommand } from "./commands/lowcode.js";
|
|
57
78
|
|
|
79
|
+
// EvoMap: Gene Exchange Protocol
|
|
80
|
+
import { registerEvoMapCommand } from "./commands/evomap.js";
|
|
81
|
+
|
|
82
|
+
// CLI-Anything: Agent-Native Software Integration
|
|
83
|
+
import { registerCliAnythingCommand } from "./commands/cli-anything.js";
|
|
84
|
+
|
|
85
|
+
// WebSocket Server Interface
|
|
86
|
+
import { registerServeCommand } from "./commands/serve.js";
|
|
87
|
+
|
|
58
88
|
export function createProgram() {
|
|
59
89
|
const program = new Command();
|
|
60
90
|
|
|
@@ -131,13 +161,43 @@ export function createProgram() {
|
|
|
131
161
|
registerSandboxCommand(program);
|
|
132
162
|
registerEvolutionCommand(program);
|
|
133
163
|
|
|
164
|
+
// Phase 7: EvoMap Federation + DAO Governance
|
|
165
|
+
registerDaoCommand(program);
|
|
166
|
+
|
|
134
167
|
// Phase 8: Blockchain & Enterprise
|
|
135
168
|
registerEconomyCommand(program);
|
|
136
169
|
registerZkpCommand(program);
|
|
137
170
|
registerBiCommand(program);
|
|
138
171
|
|
|
172
|
+
// Phase 8: Security & Compliance
|
|
173
|
+
registerComplianceCommand(program);
|
|
174
|
+
registerDlpCommand(program);
|
|
175
|
+
registerSiemCommand(program);
|
|
176
|
+
registerPqcCommand(program);
|
|
177
|
+
|
|
178
|
+
// Phase 8: Communication Bridges
|
|
179
|
+
registerNostrCommand(program);
|
|
180
|
+
registerMatrixCommand(program);
|
|
181
|
+
registerScimCommand(program);
|
|
182
|
+
|
|
183
|
+
// Phase 8: Infrastructure & Hardening
|
|
184
|
+
registerTerraformCommand(program);
|
|
185
|
+
registerHardeningCommand(program);
|
|
186
|
+
|
|
187
|
+
// Phase 8: Social Platform
|
|
188
|
+
registerSocialCommand(program);
|
|
189
|
+
|
|
139
190
|
// Phase 9: Low-Code & Multi-Agent
|
|
140
191
|
registerLowcodeCommand(program);
|
|
141
192
|
|
|
193
|
+
// EvoMap: Gene Exchange Protocol
|
|
194
|
+
registerEvoMapCommand(program);
|
|
195
|
+
|
|
196
|
+
// CLI-Anything: Agent-Native Software Integration
|
|
197
|
+
registerCliAnythingCommand(program);
|
|
198
|
+
|
|
199
|
+
// WebSocket Server Interface
|
|
200
|
+
registerServeCommand(program);
|
|
201
|
+
|
|
142
202
|
return program;
|
|
143
203
|
}
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Autonomous Agent — ReAct-style self-directed task execution.
|
|
3
|
+
*
|
|
4
|
+
* Submits a goal → decomposes into sub-steps → runs Reason→Act→Observe loop
|
|
5
|
+
* with self-correction on failure. Single-goal, session-scoped.
|
|
6
|
+
*
|
|
7
|
+
* Lightweight port of desktop-app-vue/src/main/ai-engine/autonomous/autonomous-agent-runner.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { EventEmitter } from "events";
|
|
11
|
+
|
|
12
|
+
// Exported for test injection
|
|
13
|
+
export const _deps = {
|
|
14
|
+
Date,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Goal status
|
|
19
|
+
*/
|
|
20
|
+
export const GoalStatus = {
|
|
21
|
+
PENDING: "pending",
|
|
22
|
+
RUNNING: "running",
|
|
23
|
+
PAUSED: "paused",
|
|
24
|
+
COMPLETED: "completed",
|
|
25
|
+
FAILED: "failed",
|
|
26
|
+
CANCELLED: "cancelled",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Step status
|
|
31
|
+
*/
|
|
32
|
+
export const StepStatus = {
|
|
33
|
+
PENDING: "pending",
|
|
34
|
+
RUNNING: "running",
|
|
35
|
+
COMPLETED: "completed",
|
|
36
|
+
FAILED: "failed",
|
|
37
|
+
SKIPPED: "skipped",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export class CLIAutonomousAgent extends EventEmitter {
|
|
41
|
+
constructor() {
|
|
42
|
+
super();
|
|
43
|
+
this._goals = new Map();
|
|
44
|
+
this._llmChat = null;
|
|
45
|
+
this._toolExecutor = null;
|
|
46
|
+
this._hookManager = null;
|
|
47
|
+
this._initialized = false;
|
|
48
|
+
this._maxIterations = 20;
|
|
49
|
+
this._maxRetries = 3;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Initialize with required dependencies.
|
|
54
|
+
*/
|
|
55
|
+
initialize({ llmChat, toolExecutor, hookManager, maxIterations } = {}) {
|
|
56
|
+
this._llmChat = llmChat || null;
|
|
57
|
+
this._toolExecutor = toolExecutor || null;
|
|
58
|
+
this._hookManager = hookManager || null;
|
|
59
|
+
if (maxIterations) this._maxIterations = maxIterations;
|
|
60
|
+
this._initialized = true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Submit a goal for autonomous execution.
|
|
65
|
+
* @param {string} description - Natural language goal
|
|
66
|
+
* @param {object} [options]
|
|
67
|
+
* @param {number} [options.tokenBudget=50000] - Advisory token budget
|
|
68
|
+
* @returns {{ goalId: string }}
|
|
69
|
+
*/
|
|
70
|
+
async submitGoal(description, { tokenBudget = 50000 } = {}) {
|
|
71
|
+
if (!this._initialized) throw new Error("Agent not initialized");
|
|
72
|
+
if (!description) throw new Error("Goal description required");
|
|
73
|
+
|
|
74
|
+
const goalId = `goal-${_deps.Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
75
|
+
|
|
76
|
+
const goal = {
|
|
77
|
+
id: goalId,
|
|
78
|
+
description,
|
|
79
|
+
status: GoalStatus.PENDING,
|
|
80
|
+
tokenBudget,
|
|
81
|
+
tokensUsed: 0,
|
|
82
|
+
steps: [],
|
|
83
|
+
iterations: 0,
|
|
84
|
+
errors: [],
|
|
85
|
+
result: null,
|
|
86
|
+
createdAt: new Date().toISOString(),
|
|
87
|
+
updatedAt: new Date().toISOString(),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
this._goals.set(goalId, goal);
|
|
91
|
+
this.emit("goal:submitted", { goalId, description });
|
|
92
|
+
|
|
93
|
+
// Start the ReAct loop asynchronously
|
|
94
|
+
this._runReActLoop(goal).catch((err) => {
|
|
95
|
+
goal.status = GoalStatus.FAILED;
|
|
96
|
+
goal.errors.push(err.message);
|
|
97
|
+
this.emit("goal:failed", { goalId, error: err.message });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return { goalId };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Pause a running goal.
|
|
105
|
+
*/
|
|
106
|
+
pauseGoal(goalId) {
|
|
107
|
+
const goal = this._goals.get(goalId);
|
|
108
|
+
if (!goal) return { error: "Goal not found" };
|
|
109
|
+
if (goal.status !== GoalStatus.RUNNING)
|
|
110
|
+
return { error: "Goal is not running" };
|
|
111
|
+
|
|
112
|
+
goal.status = GoalStatus.PAUSED;
|
|
113
|
+
goal.updatedAt = new Date().toISOString();
|
|
114
|
+
this.emit("goal:paused", { goalId });
|
|
115
|
+
return { success: true };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Resume a paused goal.
|
|
120
|
+
*/
|
|
121
|
+
resumeGoal(goalId) {
|
|
122
|
+
const goal = this._goals.get(goalId);
|
|
123
|
+
if (!goal) return { error: "Goal not found" };
|
|
124
|
+
if (goal.status !== GoalStatus.PAUSED)
|
|
125
|
+
return { error: "Goal is not paused" };
|
|
126
|
+
|
|
127
|
+
goal.status = GoalStatus.RUNNING;
|
|
128
|
+
goal.updatedAt = new Date().toISOString();
|
|
129
|
+
this.emit("goal:resumed", { goalId });
|
|
130
|
+
|
|
131
|
+
// Continue the loop
|
|
132
|
+
this._runReActLoop(goal).catch((err) => {
|
|
133
|
+
goal.status = GoalStatus.FAILED;
|
|
134
|
+
goal.errors.push(err.message);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return { success: true };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Cancel a goal.
|
|
142
|
+
*/
|
|
143
|
+
cancelGoal(goalId) {
|
|
144
|
+
const goal = this._goals.get(goalId);
|
|
145
|
+
if (!goal) return { error: "Goal not found" };
|
|
146
|
+
|
|
147
|
+
goal.status = GoalStatus.CANCELLED;
|
|
148
|
+
goal.updatedAt = new Date().toISOString();
|
|
149
|
+
this.emit("goal:cancelled", { goalId });
|
|
150
|
+
return { success: true };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get goal status with steps.
|
|
155
|
+
*/
|
|
156
|
+
getGoalStatus(goalId) {
|
|
157
|
+
const goal = this._goals.get(goalId);
|
|
158
|
+
if (!goal) return null;
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
id: goal.id,
|
|
162
|
+
description: goal.description,
|
|
163
|
+
status: goal.status,
|
|
164
|
+
steps: goal.steps.map((s) => ({
|
|
165
|
+
description: s.description,
|
|
166
|
+
status: s.status,
|
|
167
|
+
tool: s.tool,
|
|
168
|
+
result: s.result ? String(s.result).substring(0, 200) : null,
|
|
169
|
+
error: s.error,
|
|
170
|
+
})),
|
|
171
|
+
iterations: goal.iterations,
|
|
172
|
+
errors: goal.errors,
|
|
173
|
+
result: goal.result,
|
|
174
|
+
tokensUsed: goal.tokensUsed,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* List all goals.
|
|
180
|
+
*/
|
|
181
|
+
listGoals() {
|
|
182
|
+
return [...this._goals.values()].map((g) => ({
|
|
183
|
+
id: g.id,
|
|
184
|
+
description: g.description.substring(0, 80),
|
|
185
|
+
status: g.status,
|
|
186
|
+
steps: g.steps.length,
|
|
187
|
+
iterations: g.iterations,
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ─── ReAct Loop ─────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
async _runReActLoop(goal) {
|
|
194
|
+
goal.status = GoalStatus.RUNNING;
|
|
195
|
+
this.emit("goal:started", { goalId: goal.id });
|
|
196
|
+
|
|
197
|
+
// Decompose goal into initial steps
|
|
198
|
+
if (goal.steps.length === 0) {
|
|
199
|
+
goal.steps = await this._decomposeGoal(goal);
|
|
200
|
+
this.emit("goal:planned", {
|
|
201
|
+
goalId: goal.id,
|
|
202
|
+
stepCount: goal.steps.length,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
while (
|
|
207
|
+
goal.status === GoalStatus.RUNNING &&
|
|
208
|
+
goal.iterations < this._maxIterations
|
|
209
|
+
) {
|
|
210
|
+
goal.iterations++;
|
|
211
|
+
|
|
212
|
+
// Find next pending step
|
|
213
|
+
const nextStep = goal.steps.find((s) => s.status === StepStatus.PENDING);
|
|
214
|
+
if (!nextStep) {
|
|
215
|
+
// All steps done or no pending steps
|
|
216
|
+
const allDone = goal.steps.every(
|
|
217
|
+
(s) =>
|
|
218
|
+
s.status === StepStatus.COMPLETED ||
|
|
219
|
+
s.status === StepStatus.SKIPPED,
|
|
220
|
+
);
|
|
221
|
+
if (allDone) {
|
|
222
|
+
goal.status = GoalStatus.COMPLETED;
|
|
223
|
+
goal.result = this._summarizeResults(goal);
|
|
224
|
+
this.emit("goal:completed", { goalId: goal.id, result: goal.result });
|
|
225
|
+
} else {
|
|
226
|
+
goal.status = GoalStatus.FAILED;
|
|
227
|
+
goal.errors.push("Not all steps completed");
|
|
228
|
+
this.emit("goal:failed", {
|
|
229
|
+
goalId: goal.id,
|
|
230
|
+
error: "Not all steps completed",
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Execute step
|
|
237
|
+
nextStep.status = StepStatus.RUNNING;
|
|
238
|
+
this.emit("step:started", {
|
|
239
|
+
goalId: goal.id,
|
|
240
|
+
step: nextStep.description,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const result = await this._executeStep(nextStep);
|
|
245
|
+
nextStep.status = StepStatus.COMPLETED;
|
|
246
|
+
nextStep.result = result;
|
|
247
|
+
this.emit("step:completed", {
|
|
248
|
+
goalId: goal.id,
|
|
249
|
+
step: nextStep.description,
|
|
250
|
+
});
|
|
251
|
+
} catch (err) {
|
|
252
|
+
nextStep.error = err.message;
|
|
253
|
+
|
|
254
|
+
// Self-correction: try to replan
|
|
255
|
+
if (nextStep.retries < this._maxRetries) {
|
|
256
|
+
nextStep.retries++;
|
|
257
|
+
this.emit("step:retrying", {
|
|
258
|
+
goalId: goal.id,
|
|
259
|
+
step: nextStep.description,
|
|
260
|
+
error: err.message,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const corrected = await this._selfCorrect(goal, err);
|
|
264
|
+
if (corrected) {
|
|
265
|
+
nextStep.status = StepStatus.PENDING; // Retry
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
nextStep.status = StepStatus.FAILED;
|
|
271
|
+
this.emit("step:failed", {
|
|
272
|
+
goalId: goal.id,
|
|
273
|
+
step: nextStep.description,
|
|
274
|
+
error: err.message,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Check if failure is fatal
|
|
278
|
+
if (nextStep.critical !== false) {
|
|
279
|
+
goal.status = GoalStatus.FAILED;
|
|
280
|
+
goal.errors.push(
|
|
281
|
+
`Step failed: ${nextStep.description} — ${err.message}`,
|
|
282
|
+
);
|
|
283
|
+
this.emit("goal:failed", { goalId: goal.id, error: err.message });
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
goal.updatedAt = new Date().toISOString();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (
|
|
292
|
+
goal.iterations >= this._maxIterations &&
|
|
293
|
+
goal.status === GoalStatus.RUNNING
|
|
294
|
+
) {
|
|
295
|
+
goal.status = GoalStatus.FAILED;
|
|
296
|
+
goal.errors.push("Max iterations reached");
|
|
297
|
+
this.emit("goal:failed", {
|
|
298
|
+
goalId: goal.id,
|
|
299
|
+
error: "Max iterations reached",
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Decompose a goal into executable steps using LLM.
|
|
306
|
+
*/
|
|
307
|
+
async _decomposeGoal(goal) {
|
|
308
|
+
if (!this._llmChat) {
|
|
309
|
+
// No LLM — create a single step
|
|
310
|
+
return [
|
|
311
|
+
{
|
|
312
|
+
description: goal.description,
|
|
313
|
+
tool: null,
|
|
314
|
+
params: {},
|
|
315
|
+
status: StepStatus.PENDING,
|
|
316
|
+
retries: 0,
|
|
317
|
+
critical: true,
|
|
318
|
+
result: null,
|
|
319
|
+
error: null,
|
|
320
|
+
},
|
|
321
|
+
];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
const prompt = `Break down this goal into 2-6 concrete, executable steps. Each step should use one tool.
|
|
326
|
+
|
|
327
|
+
Goal: ${goal.description}
|
|
328
|
+
|
|
329
|
+
Available tools: read_file, write_file, edit_file, run_shell, search_files, list_dir, run_skill
|
|
330
|
+
|
|
331
|
+
Return a JSON array of steps, each with: { "description": "...", "tool": "tool_name", "params": {...} }
|
|
332
|
+
Only return the JSON array, no other text.`;
|
|
333
|
+
|
|
334
|
+
const response = await this._llmChat(
|
|
335
|
+
[{ role: "user", content: prompt }],
|
|
336
|
+
{ maxTokens: 1024 },
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
// Parse steps from LLM response
|
|
340
|
+
const parsed = this._parseSteps(response);
|
|
341
|
+
return parsed.map((s) => ({
|
|
342
|
+
description: s.description || "Step",
|
|
343
|
+
tool: s.tool || null,
|
|
344
|
+
params: s.params || {},
|
|
345
|
+
status: StepStatus.PENDING,
|
|
346
|
+
retries: 0,
|
|
347
|
+
critical: s.critical !== false,
|
|
348
|
+
result: null,
|
|
349
|
+
error: null,
|
|
350
|
+
}));
|
|
351
|
+
} catch (_err) {
|
|
352
|
+
// Fallback: single step
|
|
353
|
+
return [
|
|
354
|
+
{
|
|
355
|
+
description: goal.description,
|
|
356
|
+
tool: null,
|
|
357
|
+
params: {},
|
|
358
|
+
status: StepStatus.PENDING,
|
|
359
|
+
retries: 0,
|
|
360
|
+
critical: true,
|
|
361
|
+
result: null,
|
|
362
|
+
error: null,
|
|
363
|
+
},
|
|
364
|
+
];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Execute a single step using the tool executor.
|
|
370
|
+
*/
|
|
371
|
+
async _executeStep(step) {
|
|
372
|
+
if (!step.tool || !this._toolExecutor) {
|
|
373
|
+
// No tool specified or no executor — skip as informational
|
|
374
|
+
return "No tool action required";
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return await this._toolExecutor(step.tool, step.params);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Self-correct after a step failure.
|
|
382
|
+
* Returns true if a correction was applied.
|
|
383
|
+
*/
|
|
384
|
+
async _selfCorrect(goal, error) {
|
|
385
|
+
if (!this._llmChat) return false;
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
const context = goal.steps.map((s) => ({
|
|
389
|
+
description: s.description,
|
|
390
|
+
status: s.status,
|
|
391
|
+
error: s.error,
|
|
392
|
+
}));
|
|
393
|
+
|
|
394
|
+
const prompt = `A step in my plan failed. Help me fix it.
|
|
395
|
+
|
|
396
|
+
Goal: ${goal.description}
|
|
397
|
+
Steps so far: ${JSON.stringify(context)}
|
|
398
|
+
Error: ${error.message}
|
|
399
|
+
|
|
400
|
+
Should I:
|
|
401
|
+
1. Retry the same step with different params
|
|
402
|
+
2. Add a new prerequisite step
|
|
403
|
+
3. Skip this step and continue
|
|
404
|
+
|
|
405
|
+
Reply with a JSON object: { "action": "retry|add_step|skip", "newParams": {...}, "newStep": {...} }`;
|
|
406
|
+
|
|
407
|
+
const response = await this._llmChat(
|
|
408
|
+
[{ role: "user", content: prompt }],
|
|
409
|
+
{ maxTokens: 512 },
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
const correction = this._parseJSON(response);
|
|
413
|
+
if (!correction) return false;
|
|
414
|
+
|
|
415
|
+
if (correction.action === "skip") {
|
|
416
|
+
const failedStep = goal.steps.find(
|
|
417
|
+
(s) => s.status === StepStatus.RUNNING,
|
|
418
|
+
);
|
|
419
|
+
if (failedStep) failedStep.status = StepStatus.SKIPPED;
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (correction.action === "add_step" && correction.newStep) {
|
|
424
|
+
const failedIdx = goal.steps.findIndex(
|
|
425
|
+
(s) => s.status === StepStatus.RUNNING,
|
|
426
|
+
);
|
|
427
|
+
if (failedIdx >= 0) {
|
|
428
|
+
goal.steps.splice(failedIdx, 0, {
|
|
429
|
+
description: correction.newStep.description || "Corrective step",
|
|
430
|
+
tool: correction.newStep.tool || null,
|
|
431
|
+
params: correction.newStep.params || {},
|
|
432
|
+
status: StepStatus.PENDING,
|
|
433
|
+
retries: 0,
|
|
434
|
+
critical: false,
|
|
435
|
+
result: null,
|
|
436
|
+
error: null,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (correction.action === "retry" && correction.newParams) {
|
|
443
|
+
const failedStep = goal.steps.find(
|
|
444
|
+
(s) => s.status === StepStatus.RUNNING,
|
|
445
|
+
);
|
|
446
|
+
if (failedStep) {
|
|
447
|
+
Object.assign(failedStep.params, correction.newParams);
|
|
448
|
+
}
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return false;
|
|
453
|
+
} catch (_err) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
_summarizeResults(goal) {
|
|
459
|
+
const completed = goal.steps.filter(
|
|
460
|
+
(s) => s.status === StepStatus.COMPLETED,
|
|
461
|
+
);
|
|
462
|
+
return `Completed ${completed.length}/${goal.steps.length} steps for: ${goal.description}`;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
_parseSteps(text) {
|
|
466
|
+
try {
|
|
467
|
+
// Extract JSON array from response
|
|
468
|
+
const match = text.match(/\[[\s\S]*\]/);
|
|
469
|
+
if (match) {
|
|
470
|
+
return JSON.parse(match[0]);
|
|
471
|
+
}
|
|
472
|
+
} catch (_err) {
|
|
473
|
+
// Parse failure
|
|
474
|
+
}
|
|
475
|
+
return [{ description: "Execute goal", tool: null, params: {} }];
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
_parseJSON(text) {
|
|
479
|
+
try {
|
|
480
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
481
|
+
if (match) return JSON.parse(match[0]);
|
|
482
|
+
} catch (_err) {
|
|
483
|
+
// Parse failure
|
|
484
|
+
}
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|