@jclaw/core 0.6.0 → 0.7.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 CHANGED
@@ -3,6 +3,16 @@ import { JClawAgent } from '../runtime/agent.js';
3
3
  import { createSimpleMemoryClient } from '../context/simple-memory-client.js';
4
4
  import { readFile, writeFile } from 'fs/promises';
5
5
  import { existsSync } from 'fs';
6
+ import { fileURLToPath } from 'url';
7
+ import { dirname, join } from 'path';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ // Read version from package.json
13
+ const packageJsonPath = join(__dirname, '../../package.json');
14
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
15
+ const VERSION = packageJson.version;
6
16
 
7
17
  const args = process.argv.slice(2);
8
18
  const command = args[0];
@@ -90,6 +100,8 @@ async function execute(prompt) {
90
100
  console.log(`🔌 API: ${llmConfig.apiBase}\n`);
91
101
 
92
102
  const agent = new JClawAgent({
103
+ name: 'jclaw-cli',
104
+ version: VERSION,
93
105
  enableAutoSkill: true,
94
106
  skillShConfig: { enableCache: true },
95
107
  llm: llmConfig,
@@ -116,7 +128,7 @@ async function chat() {
116
128
  console.log('🧬 JClaw 交互模式 (输入 "exit" 退出)\n');
117
129
  console.log(`📡 模型:${llmConfig.model}\n`);
118
130
 
119
- const agent = new JClawAgent({ enableAutoSkill: true, llm: llmConfig });
131
+ const agent = new JClawAgent({ name: 'jclaw-chat', version: VERSION, enableAutoSkill: true, llm: llmConfig });
120
132
  await agent.start();
121
133
 
122
134
  const readline = await import('readline');
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+ import { JClawAgent } from '../runtime/agent.js';
3
+ import { createSimpleMemoryClient } from '../context/simple-memory-client.js';
4
+ import { readFile, writeFile } from 'fs/promises';
5
+ import { existsSync } from 'fs';
6
+ import { fileURLToPath } from 'url';
7
+ import { dirname, join } from 'path';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ // Read version from package.json
13
+ const packageJsonPath = join(__dirname, '../../package.json');
14
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
15
+ const VERSION = packageJson.version;
16
+
17
+ const args = process.argv.slice(2);
18
+ const command = args[0];
19
+
20
+ function showHelp() {
21
+ console.log(`
22
+ 🧬 JClaw - Universal Self-Evolving Agent Framework
23
+
24
+ 用法:
25
+ jclaw <command> [options]
26
+
27
+ 命令:
28
+ execute <prompt> 执行任务
29
+ chat 交互模式
30
+ init 初始化项目
31
+ help 显示帮助
32
+
33
+ 环境变量:
34
+ LLM_API_KEY API 密钥
35
+ LLM_BASE_URL API 地址
36
+ LLM_MODEL_NAME 模型名称
37
+ `);
38
+ }
39
+
40
+ async function loadLLMConfig() {
41
+ if (existsSync('./jclaw.config.js')) {
42
+ try {
43
+ const config = await import('./jclaw.config.js');
44
+ const { llm, providers } = config.default || config;
45
+ if (llm && llm.provider && providers && providers[llm.provider]) {
46
+ const provider = providers[llm.provider];
47
+ const apiKey = process.env[provider.apiKeyEnv];
48
+ if (!apiKey) {
49
+ console.error(`❌ 请设置 ${provider.apiKeyEnv}`);
50
+ process.exit(1);
51
+ }
52
+ return {
53
+ apiBase: provider.baseURL,
54
+ apiKey: apiKey,
55
+ model: llm.model || provider.models[0]
56
+ };
57
+ }
58
+ if (llm && llm.apiKey) {
59
+ return {
60
+ apiBase: llm.apiBase || 'https://api.openai.com/v1',
61
+ apiKey: llm.apiKey,
62
+ model: llm.model || 'gpt-4'
63
+ };
64
+ }
65
+ } catch (e) {}
66
+ }
67
+ return null;
68
+ }
69
+
70
+ async function getLLMConfig() {
71
+ const configFile = await loadLLMConfig();
72
+ if (configFile) return configFile;
73
+
74
+ if (process.env.LLM_API_KEY) {
75
+ return {
76
+ apiBase: process.env.LLM_BASE_URL || 'https://api.openai.com/v1',
77
+ apiKey: process.env.LLM_API_KEY,
78
+ model: process.env.LLM_MODEL_NAME || 'gpt-4',
79
+ temperature: process.env.LLM_TEMPERATURE ? parseFloat(process.env.LLM_TEMPERATURE) : 1.0
80
+ };
81
+ }
82
+
83
+ if (process.env.OPENAI_API_KEY) {
84
+ return {
85
+ apiBase: process.env.LLM_BASE_URL || 'https://api.openai.com/v1',
86
+ apiKey: process.env.OPENAI_API_KEY,
87
+ model: process.env.LLM_MODEL_NAME || 'gpt-4',
88
+ temperature: process.env.LLM_TEMPERATURE ? parseFloat(process.env.LLM_TEMPERATURE) : 1.0
89
+ };
90
+ }
91
+
92
+ console.error('❌ 请设置 LLM_API_KEY 或 OPENAI_API_KEY');
93
+ process.exit(1);
94
+ }
95
+
96
+ async function execute(prompt) {
97
+ const llmConfig = await getLLMConfig();
98
+ console.log('🧬 启动 JClaw...\n');
99
+ console.log(`📡 模型:${llmConfig.model}`);
100
+ console.log(`🔌 API: ${llmConfig.apiBase}\n`);
101
+
102
+ const agent = new JClawAgent({
103
+ name: 'jclaw-cli',
104
+ version: VERSION,
105
+ enableAutoSkill: true,
106
+ skillShConfig: { enableCache: true },
107
+ llm: llmConfig,
108
+ contextManager: createSimpleMemoryClient({
109
+ enableSynonyms: true,
110
+ enableFuzzyMatch: true
111
+ })
112
+ });
113
+
114
+ await agent.start();
115
+ console.log('✅ JClaw 已启动\n');
116
+ console.log('📝 任务:', prompt, '\n');
117
+
118
+ const result = await agent.execute({ id: 'cli-task', prompt });
119
+
120
+ console.log('\n✅ 结果:\n');
121
+ console.log(result.output || result.error);
122
+ await agent.stop();
123
+ console.log('\n🎉 完成!\n');
124
+ }
125
+
126
+ async function chat() {
127
+ const llmConfig = await getLLMConfig();
128
+ console.log('🧬 JClaw 交互模式 (输入 "exit" 退出)\n');
129
+ console.log(`📡 模型:${llmConfig.model}\n`);
130
+
131
+ const agent = new JClawAgent({ name: 'jclaw-chat', version: VERSION, enableAutoSkill: true, llm: llmConfig });
132
+ await agent.start();
133
+
134
+ const readline = await import('readline');
135
+ const rl = readline.createInterface({
136
+ input: process.stdin,
137
+ output: process.stdout
138
+ });
139
+
140
+ const ask = () => {
141
+ rl.question('你:', async (input) => {
142
+ if (input.toLowerCase() === 'exit') {
143
+ await agent.stop();
144
+ rl.close();
145
+ console.log('\n👋 再见!\n');
146
+ return;
147
+ }
148
+ console.log('\n🤖 JClaw:\n');
149
+ const result = await agent.execute({ id: 'chat', prompt: input });
150
+ console.log(result.output || result.error);
151
+ console.log('');
152
+ ask();
153
+ });
154
+ };
155
+ ask();
156
+ }
157
+
158
+ async function init() {
159
+ console.log('🧬 初始化 JClaw 项目...\n');
160
+ const configTemplate = `export default {
161
+ enableAutoSkill: true,
162
+ llm: { provider: 'openai', model: 'gpt-4' },
163
+ providers: {
164
+ 'openai': {
165
+ baseURL: 'https://api.openai.com/v1',
166
+ apiKeyEnv: 'OPENAI_API_KEY',
167
+ models: ['gpt-4', 'gpt-4o']
168
+ }
169
+ }
170
+ };
171
+ `;
172
+ if (!existsSync('jclaw.config.js')) {
173
+ await writeFile('jclaw.config.js', configTemplate);
174
+ console.log('✅ 创建:jclaw.config.js');
175
+ }
176
+ console.log('\n🎉 初始化完成!\n');
177
+ }
178
+
179
+ switch (command) {
180
+ case 'execute':
181
+ case 'exec':
182
+ case 'e':
183
+ const prompt = args.slice(1).join(' ');
184
+ if (!prompt) {
185
+ console.error('❌ 请提供任务描述');
186
+ process.exit(1);
187
+ }
188
+ execute(prompt);
189
+ break;
190
+ case 'chat':
191
+ case 'c':
192
+ chat();
193
+ break;
194
+ case 'init':
195
+ case 'i':
196
+ init();
197
+ break;
198
+ case 'help':
199
+ case 'h':
200
+ case '--help':
201
+ case '-h':
202
+ showHelp();
203
+ break;
204
+ default:
205
+ if (command) {
206
+ execute(args.join(' '));
207
+ } else {
208
+ showHelp();
209
+ }
210
+ }
211
+
212
+ // Add temperature env var support
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/runtime/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElF,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAOpE,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAGpF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC9C,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9C,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAED,qBAAa,UAAW,YAAW,YAAY;IAC7C,QAAQ,CAAC,aAAa,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAM;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,YAAY,CAAC,CAAe;IACpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,cAAc,CAAC,CAAuB;IAC9C,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,iBAAiB,CAAC,CAAoB;gBAElC,MAAM,GAAE,WAAgB;IAapC,IAAI,OAAO,IAAI,cAAc,CAG5B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;YAQhC,oBAAoB;IA6ClC,SAAS,IAAI,OAAO;IACpB,IAAI,IAAI,IAAI,MAAM,CAA6B;IAC/C,IAAI,OAAO,IAAI,MAAM,CAAgC;CACtD;AAED,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,CAE5D"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/runtime/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElF,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAOpE,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAGpF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC9C,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9C,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAED,qBAAa,UAAW,YAAW,YAAY;IAC7C,QAAQ,CAAC,aAAa,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAM;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,YAAY,CAAC,CAAe;IACpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,cAAc,CAAC,CAAuB;IAC9C,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,iBAAiB,CAAC,CAAoB;gBAElC,MAAM,GAAE,WAAgB;IAapC,IAAI,OAAO,IAAI,cAAc,CAG5B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;YAQhC,oBAAoB;IA6ClC,SAAS,IAAI,OAAO;IACpB,IAAI,IAAI,IAAI,MAAM,CAA6B;IAC/C,IAAI,OAAO,IAAI,MAAM,CAAgC;CACtD;AAED,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,CAE5D"}
@@ -46,7 +46,8 @@ export class JClawAgent {
46
46
  }
47
47
  this.taskExecutor = new TaskExecutor({
48
48
  llmClient: this.llmClient,
49
- context: this.config.contextManager,
49
+ contextManager: this.config.contextManager,
50
+ extensionRegistry: this.extensionRegistry,
50
51
  verbose: this.config.verbose,
51
52
  });
52
53
  if (this.config.enableAutoSkill) {
@@ -1,92 +1,24 @@
1
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
2
+ * Task Executor - Enhanced with Capability Support
8
3
  */
9
4
  import type { Task, TaskResult, ContextManager, Executor } from '../types.js';
10
5
  import { LLMClient } from './llm-client.js';
11
- /**
12
- * Configuration for task executor
13
- */
6
+ import { ExtensionRegistry } from '../extension-system/registry.js';
14
7
  export interface TaskExecutorConfig {
15
- /** LLM client for generating responses */
16
8
  llmClient: LLMClient;
17
- /** Context manager for knowledge retrieval */
18
9
  contextManager?: ContextManager;
19
- /** Command executor for running shell commands */
20
10
  executor?: Executor;
21
- /** System prompt for the agent */
11
+ extensionRegistry?: ExtensionRegistry;
22
12
  systemPrompt?: string;
23
- /** Maximum retries for failed tasks (default: 3) */
24
13
  maxRetries?: number;
25
- /** Enable verbose logging */
26
14
  verbose?: boolean;
27
15
  }
28
- /**
29
- * Task Executor
30
- *
31
- * Orchestrates task execution by:
32
- * - Retrieving relevant context
33
- * - Generating plans with LLM
34
- * - Executing commands when needed
35
- * - Handling errors and retries
36
- *
37
- * @example
38
- * ```typescript
39
- * const executor = new TaskExecutor({
40
- * llmClient: createLLMClient(config),
41
- * contextManager: openVikingClient,
42
- * executor: localExecutor
43
- * });
44
- *
45
- * const result = await executor.execute({
46
- * id: 'task-1',
47
- * prompt: 'List all files in the current directory'
48
- * });
49
- * ```
50
- */
51
16
  export declare class TaskExecutor {
52
17
  private readonly config;
53
- /**
54
- * Create a new task executor instance.
55
- *
56
- * @param config - Configuration options
57
- */
58
18
  constructor(config: TaskExecutorConfig);
59
- /**
60
- * Execute a task and return the result.
61
- *
62
- * @param task - The task to execute
63
- * @returns Promise resolving to task result
64
- */
65
19
  execute(task: Task): Promise<TaskResult>;
66
- /**
67
- * Internal method to execute a single task attempt.
68
- */
69
20
  private executeTask;
70
- /**
71
- * Extract shell commands from LLM response.
72
- *
73
- * Looks for commands in code blocks marked as shell/bash.
74
- */
75
- private extractCommands;
76
- /**
77
- * Log message if verbose mode is enabled.
78
- */
79
- private log;
80
- /**
81
- * Sleep for specified milliseconds.
82
- */
83
- private sleep;
21
+ private buildSystemPrompt;
22
+ private extractCapabilityCall;
84
23
  }
85
- /**
86
- * Create a new task executor instance.
87
- *
88
- * @param config - Configuration options
89
- * @returns New TaskExecutor instance
90
- */
91
- export declare function createTaskExecutor(config: TaskExecutorConfig): TaskExecutor;
92
24
  //# sourceMappingURL=task-executor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/runtime/task-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAoB,MAAM,iBAAiB,CAAC;AAc9D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,SAAS,EAAE,SAAS,CAAC;IACrB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,kDAAkD;IAClD,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGrB;IAEF;;;;OAIG;gBACS,MAAM,EAAE,kBAAkB;IAStC;;;;;OAKG;IACG,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;IAsC9C;;OAEG;YACW,WAAW;IAuEzB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;IACH,OAAO,CAAC,GAAG;IAMX;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAE3E"}
1
+ {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/runtime/task-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAoB,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAKpE,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAIrB;gBAEU,MAAM,EAAE,kBAAkB;IAIhC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;YAsBhC,WAAW;IAkDzB,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,qBAAqB;CAuC/B"}
@@ -1,202 +1,141 @@
1
1
  /**
2
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
3
  */
43
4
  export class TaskExecutor {
44
- config;
45
- /**
46
- * Create a new task executor instance.
47
- *
48
- * @param config - Configuration options
49
- */
50
5
  constructor(config) {
51
6
  this.config = {
52
- systemPrompt: DEFAULT_SYSTEM_PROMPT,
7
+ systemPrompt: `You are JClaw, a self-evolving AI agent.`,
53
8
  maxRetries: 3,
54
9
  verbose: false,
55
10
  ...config,
56
11
  };
57
12
  }
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
- */
13
+
64
14
  async execute(task) {
65
15
  const startTime = Date.now();
66
16
  let attempts = 0;
67
17
  let lastError;
18
+
68
19
  while (attempts < this.config.maxRetries) {
69
20
  attempts++;
70
21
  try {
71
22
  const output = await this.executeTask(task);
72
- const duration = Date.now() - startTime;
73
23
  return {
74
24
  taskId: task.id,
75
25
  success: true,
76
26
  output,
77
- duration,
27
+ duration: Date.now() - startTime,
78
28
  };
79
- }
80
- catch (error) {
29
+ } catch (error) {
81
30
  lastError = error instanceof Error ? error.message : 'Unknown error';
82
- this.log(`Attempt ${attempts} failed: ${lastError}`);
83
- // Wait before retry (exponential backoff)
84
31
  if (attempts < this.config.maxRetries) {
85
32
  await this.sleep(1000 * Math.pow(2, attempts - 1));
86
33
  }
87
34
  }
88
35
  }
89
- const duration = Date.now() - startTime;
36
+
90
37
  return {
91
38
  taskId: task.id,
92
39
  success: false,
93
- error: `Task failed after ${attempts} attempts. Last error: ${lastError}`,
94
- duration,
40
+ error: lastError,
41
+ duration: Date.now() - startTime,
95
42
  };
96
43
  }
97
- /**
98
- * Internal method to execute a single task attempt.
99
- */
44
+
100
45
  async executeTask(task) {
101
- // Build messages array
102
46
  const messages = [
103
- { role: 'system', content: this.config.systemPrompt },
47
+ { role: 'system', content: this.buildSystemPrompt() },
104
48
  ];
105
- // Add context if available
49
+
106
50
  if (this.config.contextManager) {
107
51
  try {
108
52
  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
- });
53
+ if (context) messages.push({ role: 'system', content: 'Context: ' + context });
54
+ } catch {}
55
+ }
56
+
57
+ messages.push({ role: 'user', content: task.prompt });
58
+
59
+ let iterationCount = 0;
60
+ const maxIterations = 5;
61
+
62
+ while (iterationCount < maxIterations) {
63
+ iterationCount++;
64
+ const response = await this.config.llmClient.chat(messages);
65
+
66
+ // Check for capability calls
67
+ const capabilityCall = this.extractCapabilityCall(response.content);
68
+ if (capabilityCall && this.config.extensionRegistry) {
69
+ const registeredCap = this.config.extensionRegistry.getCapability(capabilityCall.name);
70
+ if (registeredCap?.capability?.handler) {
71
+ try {
72
+ const result = await registeredCap.capability.handler(capabilityCall.input);
73
+ messages.push({ role: 'assistant', content: response.content });
74
+ messages.push({ role: 'system', content: 'Result: ' + JSON.stringify(result) });
75
+ continue;
76
+ } catch (error) {
77
+ messages.push({ role: 'assistant', content: response.content });
78
+ messages.push({ role: 'system', content: 'Error: ' + error.message });
79
+ continue;
80
+ }
114
81
  }
115
82
  }
116
- catch {
117
- this.log('Warning: Failed to retrieve context');
83
+
84
+ // Check for commands
85
+ const commands = this.extractCommands(response.content);
86
+ if (commands.length > 0 && this.config.executor) {
87
+ return response.content;
118
88
  }
89
+
90
+ return response.content;
119
91
  }
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
- });
92
+
93
+ return (await this.config.llmClient.chat(messages)).content;
94
+ }
95
+
96
+ buildSystemPrompt() {
97
+ let prompt = this.config.systemPrompt;
98
+ if (this.config.extensionRegistry) {
99
+ const capabilities = this.config.extensionRegistry.getCapabilityNames();
100
+ if (capabilities.length > 0) {
101
+ prompt += '\n\nAvailable Capabilities:\n' + capabilities.map(c => '- ' + c).join('\n');
102
+ prompt += '\n\nTo use: ```capability\n{\n "name": "capability_name",\n "input": {...}\n}\n```';
155
103
  }
156
- const finalResponse = await this.config.llmClient.chat(messages);
157
- return finalResponse.content;
158
104
  }
159
- return response.content;
105
+ return prompt;
106
+ }
107
+
108
+ extractCapabilityCall(content) {
109
+ const match = /```capability\s*\n([\s\S]*?)\n\s*```/.exec(content);
110
+ if (match && match[1]) {
111
+ try {
112
+ const parsed = JSON.parse(match[1].trim());
113
+ if (parsed.name) return { name: parsed.name, input: parsed.input || {} };
114
+ } catch {}
115
+ }
116
+ return null;
160
117
  }
161
- /**
162
- * Extract shell commands from LLM response.
163
- *
164
- * Looks for commands in code blocks marked as shell/bash.
165
- */
118
+
166
119
  extractCommands(content) {
167
120
  const commands = [];
168
- // Match ```shell or ```bash code blocks
169
- const shellBlockRegex = /```(?:shell|bash|sh)\n([\s\S]*?)```/g;
121
+ const regex = /```(?:shell|bash|sh)\n([\s\S]*?)```/g;
170
122
  let match;
171
- while ((match = shellBlockRegex.exec(content)) !== null) {
123
+ while ((match = regex.exec(content)) !== null) {
172
124
  const cmd = match[1]?.trim();
173
- if (cmd && !cmd.startsWith('#')) {
174
- commands.push(cmd);
175
- }
125
+ if (cmd && !cmd.startsWith('#')) commands.push(cmd);
176
126
  }
177
127
  return commands;
178
128
  }
179
- /**
180
- * Log message if verbose mode is enabled.
181
- */
129
+
182
130
  log(message) {
183
- if (this.config.verbose) {
184
- console.log(`[TaskExecutor] ${message}`);
185
- }
131
+ if (this.config.verbose) console.log('[TaskExecutor] ' + message);
186
132
  }
187
- /**
188
- * Sleep for specified milliseconds.
189
- */
133
+
190
134
  sleep(ms) {
191
135
  return new Promise((resolve) => setTimeout(resolve, ms));
192
136
  }
193
137
  }
194
- /**
195
- * Create a new task executor instance.
196
- *
197
- * @param config - Configuration options
198
- * @returns New TaskExecutor instance
199
- */
138
+
200
139
  export function createTaskExecutor(config) {
201
140
  return new TaskExecutor(config);
202
141
  }
@@ -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,35 @@
1
1
  {
2
2
  "name": "@jclaw/core",
3
- "version": "0.6.0",
3
+ "version": "0.7.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
33
  "glob": "^10.3.10"
17
34
  }
18
- }
35
+ }