@jclaw/core 0.6.1 → 0.8.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/cli/jclaw.js.backup +212 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +5 -4
- package/dist/context/memsearch-ts-client.d.ts +37 -0
- package/dist/context/memsearch-ts-client.d.ts.map +1 -0
- package/dist/context/memsearch-ts-client.js +121 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/planning/index.d.ts +8 -0
- package/dist/planning/index.d.ts.map +1 -0
- package/dist/planning/index.js +6 -0
- package/dist/planning/planner.d.ts +23 -0
- package/dist/planning/planner.d.ts.map +1 -0
- package/dist/planning/planner.js +184 -0
- package/dist/planning/types.d.ts +34 -0
- package/dist/planning/types.d.ts.map +1 -0
- package/dist/planning/types.js +6 -0
- package/dist/runtime/agent.d.ts +3 -4
- package/dist/runtime/agent.d.ts.map +1 -1
- package/dist/runtime/agent.js +76 -45
- package/dist/runtime/task-executor.d.ts +5 -69
- package/dist/runtime/task-executor.d.ts.map +1 -1
- package/dist/runtime/task-executor.js +67 -151
- package/dist/runtime/task-executor.js.backup +295 -0
- package/package.json +26 -8
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Executor
|
|
3
|
+
*
|
|
4
|
+
* Manages task execution workflow including context retrieval,
|
|
5
|
+
* LLM interaction, and command execution.
|
|
6
|
+
*
|
|
7
|
+
* @module @jclaw/core/runtime/task-executor
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* System prompt for the agent
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_SYSTEM_PROMPT = `You are JClaw, a self-evolving AI agent.
|
|
13
|
+
You help users complete tasks by:
|
|
14
|
+
1. Understanding their request
|
|
15
|
+
2. Planning the necessary steps
|
|
16
|
+
3. Executing commands when needed
|
|
17
|
+
4. Reporting results clearly
|
|
18
|
+
|
|
19
|
+
Always think step by step and explain your reasoning.`;
|
|
20
|
+
/**
|
|
21
|
+
* Task Executor
|
|
22
|
+
*
|
|
23
|
+
* Orchestrates task execution by:
|
|
24
|
+
* - Retrieving relevant context
|
|
25
|
+
* - Generating plans with LLM
|
|
26
|
+
* - Executing commands when needed
|
|
27
|
+
* - Handling errors and retries
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const executor = new TaskExecutor({
|
|
32
|
+
* llmClient: createLLMClient(config),
|
|
33
|
+
* contextManager: openVikingClient,
|
|
34
|
+
* executor: localExecutor
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* const result = await executor.execute({
|
|
38
|
+
* id: 'task-1',
|
|
39
|
+
* prompt: 'List all files in the current directory'
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export class TaskExecutor {
|
|
44
|
+
config;
|
|
45
|
+
/**
|
|
46
|
+
* Create a new task executor instance.
|
|
47
|
+
*
|
|
48
|
+
* @param config - Configuration options
|
|
49
|
+
*/
|
|
50
|
+
constructor(config) {
|
|
51
|
+
this.config = {
|
|
52
|
+
systemPrompt: DEFAULT_SYSTEM_PROMPT,
|
|
53
|
+
maxRetries: 3,
|
|
54
|
+
verbose: false,
|
|
55
|
+
...config,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Execute a task and return the result.
|
|
60
|
+
*
|
|
61
|
+
* @param task - The task to execute
|
|
62
|
+
* @returns Promise resolving to task result
|
|
63
|
+
*/
|
|
64
|
+
async execute(task) {
|
|
65
|
+
const startTime = Date.now();
|
|
66
|
+
let attempts = 0;
|
|
67
|
+
let lastError;
|
|
68
|
+
while (attempts < this.config.maxRetries) {
|
|
69
|
+
attempts++;
|
|
70
|
+
try {
|
|
71
|
+
const output = await this.executeTask(task);
|
|
72
|
+
const duration = Date.now() - startTime;
|
|
73
|
+
return {
|
|
74
|
+
taskId: task.id,
|
|
75
|
+
success: true,
|
|
76
|
+
output,
|
|
77
|
+
duration,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
lastError = error instanceof Error ? error.message : 'Unknown error';
|
|
82
|
+
this.log(`Attempt ${attempts} failed: ${lastError}`);
|
|
83
|
+
// Wait before retry (exponential backoff)
|
|
84
|
+
if (attempts < this.config.maxRetries) {
|
|
85
|
+
await this.sleep(1000 * Math.pow(2, attempts - 1));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const duration = Date.now() - startTime;
|
|
90
|
+
return {
|
|
91
|
+
taskId: task.id,
|
|
92
|
+
success: false,
|
|
93
|
+
error: `Task failed after ${attempts} attempts. Last error: ${lastError}`,
|
|
94
|
+
duration,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Internal method to execute a single task attempt.
|
|
99
|
+
*/
|
|
100
|
+
async executeTask(task) {
|
|
101
|
+
// Build messages array
|
|
102
|
+
const messages = [
|
|
103
|
+
{ role: 'system', content: this.config.systemPrompt },
|
|
104
|
+
];
|
|
105
|
+
// Add context if available
|
|
106
|
+
if (this.config.contextManager) {
|
|
107
|
+
try {
|
|
108
|
+
const context = await this.config.contextManager.query(task.prompt, { topK: 5 });
|
|
109
|
+
if (context) {
|
|
110
|
+
messages.push({
|
|
111
|
+
role: 'system',
|
|
112
|
+
content: `Relevant context:\n${context}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
this.log('Warning: Failed to retrieve context');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Add user prompt
|
|
121
|
+
messages.push({ role: 'user', content: task.prompt });
|
|
122
|
+
// Add task context if provided
|
|
123
|
+
if (task.context) {
|
|
124
|
+
messages.push({
|
|
125
|
+
role: 'user',
|
|
126
|
+
content: `Additional context: ${JSON.stringify(task.context, null, 2)}`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// Get LLM response
|
|
130
|
+
const response = await this.config.llmClient.chat(messages);
|
|
131
|
+
// Check if we need to execute commands
|
|
132
|
+
const commands = this.extractCommands(response.content);
|
|
133
|
+
if (commands.length > 0 && this.config.executor) {
|
|
134
|
+
for (const cmd of commands) {
|
|
135
|
+
this.log(`Executing command: ${cmd}`);
|
|
136
|
+
const result = await this.config.executor.execute(cmd, {
|
|
137
|
+
mode: task.executionMode,
|
|
138
|
+
});
|
|
139
|
+
if (result.exitCode !== 0) {
|
|
140
|
+
// Feed error back to LLM
|
|
141
|
+
messages.push({ role: 'assistant', content: response.content });
|
|
142
|
+
messages.push({
|
|
143
|
+
role: 'user',
|
|
144
|
+
content: `Command failed with exit code ${result.exitCode}:\n${result.stderr}`,
|
|
145
|
+
});
|
|
146
|
+
const retryResponse = await this.config.llmClient.chat(messages);
|
|
147
|
+
return retryResponse.content;
|
|
148
|
+
}
|
|
149
|
+
// Feed output back to LLM
|
|
150
|
+
messages.push({ role: 'assistant', content: response.content });
|
|
151
|
+
messages.push({
|
|
152
|
+
role: 'user',
|
|
153
|
+
content: `Command output:\n${result.stdout}`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const finalResponse = await this.config.llmClient.chat(messages);
|
|
157
|
+
return finalResponse.content;
|
|
158
|
+
}
|
|
159
|
+
return response.content;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Extract shell commands from LLM response.
|
|
163
|
+
*
|
|
164
|
+
* Looks for commands in code blocks marked as shell/bash.
|
|
165
|
+
*/
|
|
166
|
+
extractCommands(content) {
|
|
167
|
+
const commands = [];
|
|
168
|
+
// Match ```shell or ```bash code blocks
|
|
169
|
+
const shellBlockRegex = /```(?:shell|bash|sh)\n([\s\S]*?)```/g;
|
|
170
|
+
let match;
|
|
171
|
+
while ((match = shellBlockRegex.exec(content)) !== null) {
|
|
172
|
+
const cmd = match[1]?.trim();
|
|
173
|
+
if (cmd && !cmd.startsWith('#')) {
|
|
174
|
+
commands.push(cmd);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return commands;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Log message if verbose mode is enabled.
|
|
181
|
+
*/
|
|
182
|
+
log(message) {
|
|
183
|
+
if (this.config.verbose) {
|
|
184
|
+
console.log(`[TaskExecutor] ${message}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Sleep for specified milliseconds.
|
|
189
|
+
*/
|
|
190
|
+
sleep(ms) {
|
|
191
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Build system prompt with available capabilities.
|
|
195
|
+
*/
|
|
196
|
+
buildSystemPrompt() {
|
|
197
|
+
let prompt = this.config.systemPrompt;
|
|
198
|
+
// Add available capabilities if registry exists
|
|
199
|
+
if (this.config.extensionRegistry) {
|
|
200
|
+
const capabilities = this.config.extensionRegistry.getCapabilityNames();
|
|
201
|
+
if (capabilities.length > 0) {
|
|
202
|
+
prompt += '\n\n## Available Capabilities\n\nYou have access to the following capabilities:\n';
|
|
203
|
+
capabilities.forEach(cap => {
|
|
204
|
+
prompt += `- \`${cap}\` - Use this capability when appropriate\n`;
|
|
205
|
+
});
|
|
206
|
+
prompt += '\n### How to Use a Capability\n\n';
|
|
207
|
+
prompt += 'To use a capability, respond with a code block in this format:\n';
|
|
208
|
+
prompt += '```capability\n{\n "name": "capability_name",\n "input": { ... }\n}\n```\n\n';
|
|
209
|
+
prompt += 'The capability will be executed and the result will be provided to you.\n';
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return prompt;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Extract capability call from LLM response.
|
|
216
|
+
*/
|
|
217
|
+
extractCapabilityCall(content) {
|
|
218
|
+
// Match ```capability { ... } ``` blocks
|
|
219
|
+
const capabilityRegex = /```capability\s*\n([\s\S]*?)\n\s*```/;
|
|
220
|
+
const match = capabilityRegex.exec(content);
|
|
221
|
+
if (match && match[1]) {
|
|
222
|
+
try {
|
|
223
|
+
const parsed = JSON.parse(match[1].trim());
|
|
224
|
+
if (parsed.name && typeof parsed.name === 'string') {
|
|
225
|
+
return {
|
|
226
|
+
name: parsed.name,
|
|
227
|
+
input: parsed.input || {}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
this.log(`Failed to parse capability call: ${e}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Extract shell commands from LLM response.
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Create a new task executor instance.
|
|
243
|
+
*
|
|
244
|
+
* @param config - Configuration options
|
|
245
|
+
* @returns New TaskExecutor instance
|
|
246
|
+
*/
|
|
247
|
+
export function createTaskExecutor(config) {
|
|
248
|
+
return new TaskExecutor(config);
|
|
249
|
+
}
|
|
250
|
+
buildSystemPrompt();
|
|
251
|
+
string;
|
|
252
|
+
{
|
|
253
|
+
let prompt = this.config.systemPrompt;
|
|
254
|
+
// Add available capabilities if registry exists
|
|
255
|
+
if (this.config.extensionRegistry) {
|
|
256
|
+
const capabilities = this.config.extensionRegistry.getCapabilityNames();
|
|
257
|
+
if (capabilities.length > 0) {
|
|
258
|
+
prompt += '\n\n## Available Capabilities\n\nYou have access to the following capabilities:\n';
|
|
259
|
+
capabilities.forEach(cap => {
|
|
260
|
+
prompt += `- \`${cap}\` - Use this capability when appropriate\n`;
|
|
261
|
+
});
|
|
262
|
+
prompt += '\n### How to Use a Capability\n\n';
|
|
263
|
+
prompt += 'To use a capability, respond with a code block in this format:\n';
|
|
264
|
+
prompt += '```capability\n{\n "name": "capability_name",\n "input": { ... }\n}\n```\n\n';
|
|
265
|
+
prompt += 'The capability will be executed and the result will be provided to you.\n';
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return prompt;
|
|
269
|
+
}
|
|
270
|
+
extractCapabilityCall(content, string);
|
|
271
|
+
{
|
|
272
|
+
name: string;
|
|
273
|
+
input: unknown;
|
|
274
|
+
}
|
|
275
|
+
| null;
|
|
276
|
+
{
|
|
277
|
+
// Match ```capability { ... } ``` blocks
|
|
278
|
+
const capabilityRegex = /```capability\s*\n([\s\S]*?)\n\s*```/;
|
|
279
|
+
const match = capabilityRegex.exec(content);
|
|
280
|
+
if (match && match[1]) {
|
|
281
|
+
try {
|
|
282
|
+
const parsed = JSON.parse(match[1].trim());
|
|
283
|
+
if (parsed.name && typeof parsed.name === 'string') {
|
|
284
|
+
return {
|
|
285
|
+
name: parsed.name,
|
|
286
|
+
input: parsed.input || {}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
this.log(`Failed to parse capability call: ${e}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jclaw/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Universal self-evolving Agent with improved AutoSkill retry logic",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
-
"bin": {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
8
|
+
"bin": {
|
|
9
|
+
"jclaw": "./dist/cli/jclaw.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"ai",
|
|
26
|
+
"agent",
|
|
27
|
+
"cli",
|
|
28
|
+
"self-evolving"
|
|
29
|
+
],
|
|
13
30
|
"author": "JClaw Team",
|
|
14
31
|
"license": "MIT",
|
|
15
32
|
"dependencies": {
|
|
16
|
-
"glob": "^10.3.10"
|
|
33
|
+
"glob": "^10.3.10",
|
|
34
|
+
"memsearch-core": "^1.0.5"
|
|
17
35
|
}
|
|
18
|
-
}
|
|
36
|
+
}
|