@push.rocks/smartagent 1.0.2
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_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +10 -0
- package/dist_ts/index.js +18 -0
- package/dist_ts/plugins.d.ts +6 -0
- package/dist_ts/plugins.js +8 -0
- package/dist_ts/smartagent.classes.driveragent.d.ts +70 -0
- package/dist_ts/smartagent.classes.driveragent.js +274 -0
- package/dist_ts/smartagent.classes.dualagent.d.ts +62 -0
- package/dist_ts/smartagent.classes.dualagent.js +298 -0
- package/dist_ts/smartagent.classes.guardianagent.d.ts +46 -0
- package/dist_ts/smartagent.classes.guardianagent.js +201 -0
- package/dist_ts/smartagent.classes.smartagent.d.ts +123 -0
- package/dist_ts/smartagent.classes.smartagent.js +274 -0
- package/dist_ts/smartagent.interfaces.d.ts +165 -0
- package/dist_ts/smartagent.interfaces.js +8 -0
- package/dist_ts/smartagent.tools.base.d.ts +46 -0
- package/dist_ts/smartagent.tools.base.js +45 -0
- package/dist_ts/smartagent.tools.browser.d.ts +16 -0
- package/dist_ts/smartagent.tools.browser.js +177 -0
- package/dist_ts/smartagent.tools.filesystem.d.ts +16 -0
- package/dist_ts/smartagent.tools.filesystem.js +352 -0
- package/dist_ts/smartagent.tools.http.d.ts +15 -0
- package/dist_ts/smartagent.tools.http.js +187 -0
- package/dist_ts/smartagent.tools.shell.d.ts +16 -0
- package/dist_ts/smartagent.tools.shell.js +155 -0
- package/npmextra.json +18 -0
- package/package.json +50 -0
- package/readme.hints.md +16 -0
- package/readme.md +299 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +29 -0
- package/ts/plugins.ts +14 -0
- package/ts/smartagent.classes.driveragent.ts +321 -0
- package/ts/smartagent.classes.dualagent.ts +350 -0
- package/ts/smartagent.classes.guardianagent.ts +241 -0
- package/ts/smartagent.interfaces.ts +210 -0
- package/ts/smartagent.tools.base.ts +80 -0
- package/ts/smartagent.tools.browser.ts +200 -0
- package/ts/smartagent.tools.filesystem.ts +379 -0
- package/ts/smartagent.tools.http.ts +205 -0
- package/ts/smartagent.tools.shell.ts +182 -0
package/readme.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# @push.rocks/smartagent
|
|
2
|
+
A dual-agent agentic framework with Driver and Guardian agents for safe, policy-controlled AI task execution.
|
|
3
|
+
|
|
4
|
+
## Install
|
|
5
|
+
```bash
|
|
6
|
+
npm install @push.rocks/smartagent
|
|
7
|
+
# or
|
|
8
|
+
pnpm install @push.rocks/smartagent
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
SmartAgent implements a dual-agent architecture:
|
|
14
|
+
|
|
15
|
+
- **Driver Agent**: Executes tasks, reasons about goals, and proposes tool calls
|
|
16
|
+
- **Guardian Agent**: Evaluates tool call proposals against a policy prompt, approving or rejecting with feedback
|
|
17
|
+
|
|
18
|
+
This design ensures safe tool use through AI-based policy evaluation rather than rigid programmatic rules.
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
User Task + Guardian Policy Prompt
|
|
24
|
+
|
|
|
25
|
+
+---------------------------------------+
|
|
26
|
+
| DualAgentOrchestrator |
|
|
27
|
+
| |
|
|
28
|
+
| +--------+ +------------+ |
|
|
29
|
+
| | Driver |-------> | Guardian | |
|
|
30
|
+
| | Agent | tool | Agent | |
|
|
31
|
+
| | | call | | |
|
|
32
|
+
| | Reason |<--------| Evaluate | |
|
|
33
|
+
| | + Plan | approve | against | |
|
|
34
|
+
| +--------+ /reject | policy | |
|
|
35
|
+
| | +feedback+-----------+ |
|
|
36
|
+
| v (if approved) |
|
|
37
|
+
| +-----------------------------------+|
|
|
38
|
+
| | Standard Tools ||
|
|
39
|
+
| | Filesystem | HTTP | Shell | Browser|
|
|
40
|
+
| +-----------------------------------+|
|
|
41
|
+
+---------------------------------------+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { DualAgentOrchestrator } from '@push.rocks/smartagent';
|
|
48
|
+
|
|
49
|
+
// Create orchestrator with Guardian policy
|
|
50
|
+
const orchestrator = new DualAgentOrchestrator({
|
|
51
|
+
openaiToken: 'sk-...',
|
|
52
|
+
defaultProvider: 'openai',
|
|
53
|
+
guardianPolicyPrompt: `
|
|
54
|
+
FILE SYSTEM POLICY:
|
|
55
|
+
- ONLY allow reading/writing within /tmp or the current working directory
|
|
56
|
+
- REJECT operations on system directories or sensitive files
|
|
57
|
+
|
|
58
|
+
SHELL POLICY:
|
|
59
|
+
- Allow read-only commands (ls, cat, grep, echo)
|
|
60
|
+
- REJECT destructive commands (rm, mv, chmod) without explicit justification
|
|
61
|
+
|
|
62
|
+
FLAG any attempt to expose secrets or credentials.
|
|
63
|
+
`,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Register standard tools
|
|
67
|
+
orchestrator.registerStandardTools();
|
|
68
|
+
|
|
69
|
+
// Start the orchestrator (initializes all tools)
|
|
70
|
+
await orchestrator.start();
|
|
71
|
+
|
|
72
|
+
// Run a task
|
|
73
|
+
const result = await orchestrator.run('List all TypeScript files in the current directory');
|
|
74
|
+
|
|
75
|
+
console.log('Success:', result.success);
|
|
76
|
+
console.log('Result:', result.result);
|
|
77
|
+
console.log('Iterations:', result.iterations);
|
|
78
|
+
|
|
79
|
+
// Cleanup
|
|
80
|
+
await orchestrator.stop();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Standard Tools
|
|
84
|
+
|
|
85
|
+
### FilesystemTool
|
|
86
|
+
File and directory operations using `@push.rocks/smartfs`.
|
|
87
|
+
|
|
88
|
+
**Actions**: `read`, `write`, `append`, `list`, `delete`, `exists`, `stat`, `copy`, `move`, `mkdir`
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Example tool call by Driver
|
|
92
|
+
<tool_call>
|
|
93
|
+
<tool>filesystem</tool>
|
|
94
|
+
<action>read</action>
|
|
95
|
+
<params>{"path": "/tmp/config.json"}</params>
|
|
96
|
+
<reasoning>Need to read the configuration file to understand the settings</reasoning>
|
|
97
|
+
</tool_call>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### HttpTool
|
|
101
|
+
HTTP requests using `@push.rocks/smartrequest`.
|
|
102
|
+
|
|
103
|
+
**Actions**: `get`, `post`, `put`, `patch`, `delete`
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
<tool_call>
|
|
107
|
+
<tool>http</tool>
|
|
108
|
+
<action>get</action>
|
|
109
|
+
<params>{"url": "https://api.example.com/data"}</params>
|
|
110
|
+
<reasoning>Fetching data from the API endpoint</reasoning>
|
|
111
|
+
</tool_call>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ShellTool
|
|
115
|
+
Secure shell command execution using `@push.rocks/smartshell` with `execSpawn` (no shell injection).
|
|
116
|
+
|
|
117
|
+
**Actions**: `execute`, `which`
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
<tool_call>
|
|
121
|
+
<tool>shell</tool>
|
|
122
|
+
<action>execute</action>
|
|
123
|
+
<params>{"command": "ls", "args": ["-la", "/tmp"]}</params>
|
|
124
|
+
<reasoning>Listing directory contents to find relevant files</reasoning>
|
|
125
|
+
</tool_call>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### BrowserTool
|
|
129
|
+
Web page interaction using `@push.rocks/smartbrowser` (Puppeteer-based).
|
|
130
|
+
|
|
131
|
+
**Actions**: `screenshot`, `pdf`, `evaluate`, `getPageContent`
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
<tool_call>
|
|
135
|
+
<tool>browser</tool>
|
|
136
|
+
<action>getPageContent</action>
|
|
137
|
+
<params>{"url": "https://example.com"}</params>
|
|
138
|
+
<reasoning>Extracting text content from the webpage</reasoning>
|
|
139
|
+
</tool_call>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Guardian Policy Examples
|
|
143
|
+
|
|
144
|
+
### Strict Security Policy
|
|
145
|
+
```typescript
|
|
146
|
+
const securityPolicy = `
|
|
147
|
+
SECURITY POLICY:
|
|
148
|
+
1. REJECT any file operations outside /home/user/workspace
|
|
149
|
+
2. REJECT any shell commands that could modify system state
|
|
150
|
+
3. REJECT any HTTP requests to internal/private IP ranges
|
|
151
|
+
4. REJECT any attempts to read environment variables or credentials
|
|
152
|
+
5. FLAG and REJECT obfuscated code execution
|
|
153
|
+
|
|
154
|
+
When rejecting, always explain:
|
|
155
|
+
- What policy was violated
|
|
156
|
+
- What would be a safer alternative
|
|
157
|
+
`;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Development Environment Policy
|
|
161
|
+
```typescript
|
|
162
|
+
const devPolicy = `
|
|
163
|
+
DEVELOPMENT POLICY:
|
|
164
|
+
- Allow file operations only within the project directory
|
|
165
|
+
- Allow npm/pnpm commands for package management
|
|
166
|
+
- Allow git commands for version control
|
|
167
|
+
- Allow HTTP requests to public APIs only
|
|
168
|
+
- REJECT direct database modifications
|
|
169
|
+
- REJECT commands that could affect other users
|
|
170
|
+
|
|
171
|
+
Always verify:
|
|
172
|
+
- File paths are relative or within project bounds
|
|
173
|
+
- Commands don't have dangerous flags (--force, -rf)
|
|
174
|
+
`;
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Configuration Options
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
interface IDualAgentOptions {
|
|
181
|
+
// Provider tokens (from @push.rocks/smartai)
|
|
182
|
+
openaiToken?: string;
|
|
183
|
+
anthropicToken?: string;
|
|
184
|
+
perplexityToken?: string;
|
|
185
|
+
groqToken?: string;
|
|
186
|
+
xaiToken?: string;
|
|
187
|
+
|
|
188
|
+
// Provider selection
|
|
189
|
+
defaultProvider?: TProvider; // For both Driver and Guardian
|
|
190
|
+
guardianProvider?: TProvider; // Optional: separate provider for Guardian
|
|
191
|
+
|
|
192
|
+
// Agent configuration
|
|
193
|
+
driverSystemMessage?: string; // Custom system message for Driver
|
|
194
|
+
guardianPolicyPrompt: string; // REQUIRED: Policy for Guardian to enforce
|
|
195
|
+
|
|
196
|
+
// Limits
|
|
197
|
+
maxIterations?: number; // Max task iterations (default: 20)
|
|
198
|
+
maxConsecutiveRejections?: number; // Abort after N rejections (default: 3)
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Result Interface
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
interface IDualAgentRunResult {
|
|
206
|
+
success: boolean; // Whether task completed successfully
|
|
207
|
+
completed: boolean; // Task completion status
|
|
208
|
+
result: string; // Final result or response
|
|
209
|
+
iterations: number; // Number of iterations taken
|
|
210
|
+
history: IAgentMessage[]; // Full conversation history
|
|
211
|
+
status: TDualAgentRunStatus; // 'completed' | 'max_iterations_reached' | etc.
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Custom Tools
|
|
216
|
+
|
|
217
|
+
Create custom tools by extending `BaseToolWrapper`:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { BaseToolWrapper, IToolAction, IToolExecutionResult } from '@push.rocks/smartagent';
|
|
221
|
+
|
|
222
|
+
class MyCustomTool extends BaseToolWrapper {
|
|
223
|
+
public name = 'custom';
|
|
224
|
+
public description = 'My custom tool for specific operations';
|
|
225
|
+
|
|
226
|
+
public actions: IToolAction[] = [
|
|
227
|
+
{
|
|
228
|
+
name: 'myAction',
|
|
229
|
+
description: 'Performs a custom action',
|
|
230
|
+
parameters: {
|
|
231
|
+
type: 'object',
|
|
232
|
+
properties: {
|
|
233
|
+
input: { type: 'string', description: 'Input for the action' },
|
|
234
|
+
},
|
|
235
|
+
required: ['input'],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
public async initialize(): Promise<void> {
|
|
241
|
+
this.isInitialized = true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
public async cleanup(): Promise<void> {
|
|
245
|
+
this.isInitialized = false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public async execute(action: string, params: Record<string, unknown>): Promise<IToolExecutionResult> {
|
|
249
|
+
this.validateAction(action);
|
|
250
|
+
this.ensureInitialized();
|
|
251
|
+
|
|
252
|
+
if (action === 'myAction') {
|
|
253
|
+
return {
|
|
254
|
+
success: true,
|
|
255
|
+
result: { processed: params.input },
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { success: false, error: 'Unknown action' };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
public getCallSummary(action: string, params: Record<string, unknown>): string {
|
|
263
|
+
return `Custom action "${action}" with input "${params.input}"`;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Register custom tool
|
|
268
|
+
orchestrator.registerTool(new MyCustomTool());
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Supported Providers
|
|
272
|
+
|
|
273
|
+
| Provider | Driver | Guardian |
|
|
274
|
+
|----------|:------:|:--------:|
|
|
275
|
+
| OpenAI | Yes | Yes |
|
|
276
|
+
| Anthropic | Yes | Yes |
|
|
277
|
+
| Perplexity | Yes | Yes |
|
|
278
|
+
| Groq | Yes | Yes |
|
|
279
|
+
| Ollama | Yes | Yes |
|
|
280
|
+
| XAI | Yes | Yes |
|
|
281
|
+
|
|
282
|
+
## License and Legal Information
|
|
283
|
+
|
|
284
|
+
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
285
|
+
|
|
286
|
+
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
287
|
+
|
|
288
|
+
### Trademarks
|
|
289
|
+
|
|
290
|
+
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
|
291
|
+
|
|
292
|
+
### Company Information
|
|
293
|
+
|
|
294
|
+
Task Venture Capital GmbH
|
|
295
|
+
Registered at District court Bremen HRB 35230 HB, Germany
|
|
296
|
+
|
|
297
|
+
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
|
298
|
+
|
|
299
|
+
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
package/ts/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
// Export the dual-agent orchestrator (main entry point)
|
|
4
|
+
export { DualAgentOrchestrator } from './smartagent.classes.dualagent.js';
|
|
5
|
+
|
|
6
|
+
// Export individual agents
|
|
7
|
+
export { DriverAgent } from './smartagent.classes.driveragent.js';
|
|
8
|
+
export { GuardianAgent } from './smartagent.classes.guardianagent.js';
|
|
9
|
+
|
|
10
|
+
// Export base tool class for custom tool creation
|
|
11
|
+
export { BaseToolWrapper } from './smartagent.tools.base.js';
|
|
12
|
+
|
|
13
|
+
// Export standard tools
|
|
14
|
+
export { FilesystemTool } from './smartagent.tools.filesystem.js';
|
|
15
|
+
export { HttpTool } from './smartagent.tools.http.js';
|
|
16
|
+
export { ShellTool } from './smartagent.tools.shell.js';
|
|
17
|
+
export { BrowserTool } from './smartagent.tools.browser.js';
|
|
18
|
+
|
|
19
|
+
// Export all interfaces
|
|
20
|
+
export * from './smartagent.interfaces.js';
|
|
21
|
+
|
|
22
|
+
// Re-export useful types from smartai
|
|
23
|
+
export {
|
|
24
|
+
type ISmartAiOptions,
|
|
25
|
+
type TProvider,
|
|
26
|
+
type ChatMessage,
|
|
27
|
+
type ChatOptions,
|
|
28
|
+
type ChatResponse,
|
|
29
|
+
} from '@push.rocks/smartai';
|
package/ts/plugins.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @push.rocks scope
|
|
2
|
+
import * as smartai from '@push.rocks/smartai';
|
|
3
|
+
import * as smartfs from '@push.rocks/smartfs';
|
|
4
|
+
import * as smartrequest from '@push.rocks/smartrequest';
|
|
5
|
+
import * as smartbrowser from '@push.rocks/smartbrowser';
|
|
6
|
+
import * as smartshell from '@push.rocks/smartshell';
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
smartai,
|
|
10
|
+
smartfs,
|
|
11
|
+
smartrequest,
|
|
12
|
+
smartbrowser,
|
|
13
|
+
smartshell,
|
|
14
|
+
};
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as interfaces from './smartagent.interfaces.js';
|
|
3
|
+
import type { BaseToolWrapper } from './smartagent.tools.base.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* DriverAgent - Executes tasks by reasoning and proposing tool calls
|
|
7
|
+
* Works in conjunction with GuardianAgent for approval
|
|
8
|
+
*/
|
|
9
|
+
export class DriverAgent {
|
|
10
|
+
private provider: plugins.smartai.MultiModalModel;
|
|
11
|
+
private systemMessage: string;
|
|
12
|
+
private messageHistory: plugins.smartai.ChatMessage[] = [];
|
|
13
|
+
private tools: Map<string, BaseToolWrapper> = new Map();
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
provider: plugins.smartai.MultiModalModel,
|
|
17
|
+
systemMessage?: string
|
|
18
|
+
) {
|
|
19
|
+
this.provider = provider;
|
|
20
|
+
this.systemMessage = systemMessage || this.getDefaultSystemMessage();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register a tool for use by the driver
|
|
25
|
+
*/
|
|
26
|
+
public registerTool(tool: BaseToolWrapper): void {
|
|
27
|
+
this.tools.set(tool.name, tool);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get all registered tools
|
|
32
|
+
*/
|
|
33
|
+
public getTools(): Map<string, BaseToolWrapper> {
|
|
34
|
+
return this.tools;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Initialize a new conversation for a task
|
|
39
|
+
*/
|
|
40
|
+
public async startTask(task: string): Promise<interfaces.IAgentMessage> {
|
|
41
|
+
// Reset message history
|
|
42
|
+
this.messageHistory = [];
|
|
43
|
+
|
|
44
|
+
// Build the user message
|
|
45
|
+
const userMessage = `TASK: ${task}\n\nAnalyze this task and determine what actions are needed. If you need to use a tool, provide a tool call proposal.`;
|
|
46
|
+
|
|
47
|
+
// Add to history
|
|
48
|
+
this.messageHistory.push({
|
|
49
|
+
role: 'user',
|
|
50
|
+
content: userMessage,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Build tool descriptions for the system message
|
|
54
|
+
const toolDescriptions = this.buildToolDescriptions();
|
|
55
|
+
const fullSystemMessage = `${this.systemMessage}\n\n## Available Tools\n${toolDescriptions}`;
|
|
56
|
+
|
|
57
|
+
// Get response from provider
|
|
58
|
+
const response = await this.provider.chat({
|
|
59
|
+
systemMessage: fullSystemMessage,
|
|
60
|
+
userMessage: userMessage,
|
|
61
|
+
messageHistory: [],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Add assistant response to history
|
|
65
|
+
this.messageHistory.push({
|
|
66
|
+
role: 'assistant',
|
|
67
|
+
content: response.message,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
role: 'assistant',
|
|
72
|
+
content: response.message,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Continue the conversation with feedback or results
|
|
78
|
+
*/
|
|
79
|
+
public async continueWithMessage(message: string): Promise<interfaces.IAgentMessage> {
|
|
80
|
+
// Add the new message to history
|
|
81
|
+
this.messageHistory.push({
|
|
82
|
+
role: 'user',
|
|
83
|
+
content: message,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Build tool descriptions for the system message
|
|
87
|
+
const toolDescriptions = this.buildToolDescriptions();
|
|
88
|
+
const fullSystemMessage = `${this.systemMessage}\n\n## Available Tools\n${toolDescriptions}`;
|
|
89
|
+
|
|
90
|
+
// Get response from provider (pass all but last user message as history)
|
|
91
|
+
const historyForChat = this.messageHistory.slice(0, -1);
|
|
92
|
+
|
|
93
|
+
const response = await this.provider.chat({
|
|
94
|
+
systemMessage: fullSystemMessage,
|
|
95
|
+
userMessage: message,
|
|
96
|
+
messageHistory: historyForChat,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Add assistant response to history
|
|
100
|
+
this.messageHistory.push({
|
|
101
|
+
role: 'assistant',
|
|
102
|
+
content: response.message,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
role: 'assistant',
|
|
107
|
+
content: response.message,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Parse tool call proposals from assistant response
|
|
113
|
+
*/
|
|
114
|
+
public parseToolCallProposals(response: string): interfaces.IToolCallProposal[] {
|
|
115
|
+
const proposals: interfaces.IToolCallProposal[] = [];
|
|
116
|
+
|
|
117
|
+
// Match <tool_call>...</tool_call> blocks
|
|
118
|
+
const toolCallRegex = /<tool_call>([\s\S]*?)<\/tool_call>/g;
|
|
119
|
+
let match;
|
|
120
|
+
|
|
121
|
+
while ((match = toolCallRegex.exec(response)) !== null) {
|
|
122
|
+
const content = match[1];
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const proposal = this.parseToolCallContent(content);
|
|
126
|
+
if (proposal) {
|
|
127
|
+
proposals.push(proposal);
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
// Skip malformed tool calls
|
|
131
|
+
console.warn('Failed to parse tool call:', error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return proposals;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Parse the content inside a tool_call block
|
|
140
|
+
*/
|
|
141
|
+
private parseToolCallContent(content: string): interfaces.IToolCallProposal | null {
|
|
142
|
+
// Extract tool name
|
|
143
|
+
const toolMatch = content.match(/<tool>(.*?)<\/tool>/s);
|
|
144
|
+
if (!toolMatch) return null;
|
|
145
|
+
const toolName = toolMatch[1].trim();
|
|
146
|
+
|
|
147
|
+
// Extract action
|
|
148
|
+
const actionMatch = content.match(/<action>(.*?)<\/action>/s);
|
|
149
|
+
if (!actionMatch) return null;
|
|
150
|
+
const action = actionMatch[1].trim();
|
|
151
|
+
|
|
152
|
+
// Extract params (JSON)
|
|
153
|
+
const paramsMatch = content.match(/<params>([\s\S]*?)<\/params>/);
|
|
154
|
+
let params: Record<string, unknown> = {};
|
|
155
|
+
if (paramsMatch) {
|
|
156
|
+
try {
|
|
157
|
+
params = JSON.parse(paramsMatch[1].trim());
|
|
158
|
+
} catch {
|
|
159
|
+
// Try to extract individual parameters if JSON fails
|
|
160
|
+
params = this.extractParamsFromXml(paramsMatch[1]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Extract reasoning (optional)
|
|
165
|
+
const reasoningMatch = content.match(/<reasoning>([\s\S]*?)<\/reasoning>/);
|
|
166
|
+
const reasoning = reasoningMatch ? reasoningMatch[1].trim() : undefined;
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
proposalId: this.generateProposalId(),
|
|
170
|
+
toolName,
|
|
171
|
+
action,
|
|
172
|
+
params,
|
|
173
|
+
reasoning,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Extract parameters from XML-like format when JSON parsing fails
|
|
179
|
+
*/
|
|
180
|
+
private extractParamsFromXml(content: string): Record<string, unknown> {
|
|
181
|
+
const params: Record<string, unknown> = {};
|
|
182
|
+
const paramRegex = /<(\w+)>([\s\S]*?)<\/\1>/g;
|
|
183
|
+
let match;
|
|
184
|
+
|
|
185
|
+
while ((match = paramRegex.exec(content)) !== null) {
|
|
186
|
+
const key = match[1];
|
|
187
|
+
let value: unknown = match[2].trim();
|
|
188
|
+
|
|
189
|
+
// Try to parse as JSON for arrays/objects
|
|
190
|
+
try {
|
|
191
|
+
value = JSON.parse(value as string);
|
|
192
|
+
} catch {
|
|
193
|
+
// Keep as string if not valid JSON
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
params[key] = value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return params;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Check if the response indicates task completion
|
|
204
|
+
*/
|
|
205
|
+
public isTaskComplete(response: string): boolean {
|
|
206
|
+
// Check for explicit completion markers
|
|
207
|
+
const completionMarkers = [
|
|
208
|
+
'<task_complete>',
|
|
209
|
+
'<task_completed>',
|
|
210
|
+
'TASK COMPLETE',
|
|
211
|
+
'Task completed successfully',
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
const lowerResponse = response.toLowerCase();
|
|
215
|
+
return completionMarkers.some(marker =>
|
|
216
|
+
lowerResponse.includes(marker.toLowerCase())
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if the response needs clarification or user input
|
|
222
|
+
*/
|
|
223
|
+
public needsClarification(response: string): boolean {
|
|
224
|
+
const clarificationMarkers = [
|
|
225
|
+
'<needs_clarification>',
|
|
226
|
+
'<question>',
|
|
227
|
+
'please clarify',
|
|
228
|
+
'could you specify',
|
|
229
|
+
'what do you mean by',
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
const lowerResponse = response.toLowerCase();
|
|
233
|
+
return clarificationMarkers.some(marker =>
|
|
234
|
+
lowerResponse.includes(marker.toLowerCase())
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Extract the final result from a completed task
|
|
240
|
+
*/
|
|
241
|
+
public extractTaskResult(response: string): string | null {
|
|
242
|
+
// Try to extract from result tags
|
|
243
|
+
const resultMatch = response.match(/<task_result>([\s\S]*?)<\/task_result>/);
|
|
244
|
+
if (resultMatch) {
|
|
245
|
+
return resultMatch[1].trim();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const completeMatch = response.match(/<task_complete>([\s\S]*?)<\/task_complete>/);
|
|
249
|
+
if (completeMatch) {
|
|
250
|
+
return completeMatch[1].trim();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Build tool descriptions for the system message
|
|
258
|
+
*/
|
|
259
|
+
private buildToolDescriptions(): string {
|
|
260
|
+
const descriptions: string[] = [];
|
|
261
|
+
|
|
262
|
+
for (const tool of this.tools.values()) {
|
|
263
|
+
descriptions.push(tool.getFullDescription());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return descriptions.join('\n\n');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Generate a unique proposal ID
|
|
271
|
+
*/
|
|
272
|
+
private generateProposalId(): string {
|
|
273
|
+
return `prop_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get the default system message for the driver
|
|
278
|
+
*/
|
|
279
|
+
private getDefaultSystemMessage(): string {
|
|
280
|
+
return `You are an AI assistant that executes tasks by using available tools.
|
|
281
|
+
|
|
282
|
+
## Your Role
|
|
283
|
+
You analyze tasks, break them down into steps, and use tools to accomplish goals.
|
|
284
|
+
|
|
285
|
+
## Tool Usage Format
|
|
286
|
+
When you need to use a tool, output a tool call proposal in this format:
|
|
287
|
+
|
|
288
|
+
<tool_call>
|
|
289
|
+
<tool>tool_name</tool>
|
|
290
|
+
<action>action_name</action>
|
|
291
|
+
<params>
|
|
292
|
+
{"param1": "value1", "param2": "value2"}
|
|
293
|
+
</params>
|
|
294
|
+
<reasoning>Brief explanation of why this action is needed</reasoning>
|
|
295
|
+
</tool_call>
|
|
296
|
+
|
|
297
|
+
## Guidelines
|
|
298
|
+
1. Think step by step about what needs to be done
|
|
299
|
+
2. Use only the tools that are available to you
|
|
300
|
+
3. Provide clear reasoning for each tool call
|
|
301
|
+
4. If a tool call is rejected, adapt your approach based on the feedback
|
|
302
|
+
5. When the task is complete, indicate this clearly:
|
|
303
|
+
|
|
304
|
+
<task_complete>
|
|
305
|
+
Brief summary of what was accomplished
|
|
306
|
+
</task_complete>
|
|
307
|
+
|
|
308
|
+
## Important
|
|
309
|
+
- Only propose ONE tool call at a time
|
|
310
|
+
- Wait for the result before proposing the next action
|
|
311
|
+
- If you encounter an error, analyze it and try an alternative approach
|
|
312
|
+
- If you need clarification, ask using <needs_clarification>your question</needs_clarification>`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Reset the conversation state
|
|
317
|
+
*/
|
|
318
|
+
public reset(): void {
|
|
319
|
+
this.messageHistory = [];
|
|
320
|
+
}
|
|
321
|
+
}
|