@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.
@@ -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.6.1",
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": { "jclaw": "./dist/cli/jclaw.js" },
9
- "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
10
- "files": ["dist", "README.md"],
11
- "scripts": {"build": "tsc"},
12
- "keywords": ["ai", "agent", "cli", "self-evolving"],
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
+ }