@push.rocks/smartagent 1.8.0 → 3.0.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/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/index.d.ts +8 -14
- package/dist_ts/index.js +8 -24
- package/dist_ts/plugins.d.ts +8 -9
- package/dist_ts/plugins.js +10 -12
- package/dist_ts/smartagent.classes.agent.d.ts +2 -0
- package/dist_ts/smartagent.classes.agent.js +173 -0
- package/dist_ts/smartagent.classes.toolregistry.d.ts +7 -70
- package/dist_ts/smartagent.classes.toolregistry.js +11 -155
- package/dist_ts/smartagent.interfaces.d.ts +47 -283
- package/dist_ts/smartagent.interfaces.js +6 -7
- package/dist_ts/smartagent.utils.truncation.d.ts +10 -0
- package/dist_ts/smartagent.utils.truncation.js +26 -0
- package/dist_ts_compaction/index.d.ts +1 -0
- package/dist_ts_compaction/index.js +2 -0
- package/dist_ts_compaction/plugins.d.ts +4 -0
- package/dist_ts_compaction/plugins.js +3 -0
- package/dist_ts_compaction/smartagent.compaction.d.ts +10 -0
- package/dist_ts_compaction/smartagent.compaction.js +46 -0
- package/dist_ts_tools/index.d.ts +8 -0
- package/dist_ts_tools/index.js +6 -0
- package/dist_ts_tools/plugins.d.ts +15 -0
- package/dist_ts_tools/plugins.js +19 -0
- package/dist_ts_tools/tool.filesystem.d.ts +6 -0
- package/dist_ts_tools/tool.filesystem.js +102 -0
- package/dist_ts_tools/tool.http.d.ts +2 -0
- package/dist_ts_tools/tool.http.js +65 -0
- package/dist_ts_tools/tool.json.d.ts +2 -0
- package/dist_ts_tools/tool.json.js +47 -0
- package/dist_ts_tools/tool.shell.d.ts +8 -0
- package/dist_ts_tools/tool.shell.js +40 -0
- package/npmextra.json +1 -1
- package/package.json +30 -18
- package/readme.hints.md +38 -84
- package/readme.md +254 -682
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/index.ts +10 -37
- package/ts/plugins.ts +22 -21
- package/ts/smartagent.classes.agent.ts +198 -0
- package/ts/smartagent.classes.toolregistry.ts +11 -179
- package/ts/smartagent.interfaces.ts +51 -363
- package/ts/smartagent.utils.truncation.ts +39 -0
- package/ts_compaction/index.ts +1 -0
- package/ts_compaction/plugins.ts +6 -0
- package/ts_compaction/smartagent.compaction.ts +51 -0
- package/ts_tools/index.ts +8 -0
- package/ts_tools/plugins.ts +30 -0
- package/ts_tools/tool.filesystem.ts +131 -0
- package/ts_tools/tool.http.ts +78 -0
- package/ts_tools/tool.json.ts +53 -0
- package/ts_tools/tool.shell.ts +62 -0
- package/dist_ts/smartagent.classes.driveragent.d.ts +0 -134
- package/dist_ts/smartagent.classes.driveragent.js +0 -671
- package/dist_ts/smartagent.classes.dualagent.d.ts +0 -93
- package/dist_ts/smartagent.classes.dualagent.js +0 -614
- package/dist_ts/smartagent.classes.guardianagent.d.ts +0 -46
- package/dist_ts/smartagent.classes.guardianagent.js +0 -201
- package/dist_ts/smartagent.tools.base.d.ts +0 -52
- package/dist_ts/smartagent.tools.base.js +0 -42
- package/dist_ts/smartagent.tools.browser.d.ts +0 -17
- package/dist_ts/smartagent.tools.browser.js +0 -229
- package/dist_ts/smartagent.tools.deno.d.ts +0 -21
- package/dist_ts/smartagent.tools.deno.js +0 -191
- package/dist_ts/smartagent.tools.expert.d.ts +0 -27
- package/dist_ts/smartagent.tools.expert.js +0 -126
- package/dist_ts/smartagent.tools.filesystem.d.ts +0 -40
- package/dist_ts/smartagent.tools.filesystem.js +0 -801
- package/dist_ts/smartagent.tools.http.d.ts +0 -16
- package/dist_ts/smartagent.tools.http.js +0 -264
- package/dist_ts/smartagent.tools.json.d.ts +0 -24
- package/dist_ts/smartagent.tools.json.js +0 -202
- package/dist_ts/smartagent.tools.search.d.ts +0 -29
- package/dist_ts/smartagent.tools.search.js +0 -215
- package/dist_ts/smartagent.tools.shell.d.ts +0 -17
- package/dist_ts/smartagent.tools.shell.js +0 -202
- package/ts/smartagent.classes.driveragent.ts +0 -775
- package/ts/smartagent.classes.dualagent.ts +0 -692
- package/ts/smartagent.classes.guardianagent.ts +0 -241
- package/ts/smartagent.tools.base.ts +0 -83
- package/ts/smartagent.tools.browser.ts +0 -253
- package/ts/smartagent.tools.deno.ts +0 -230
- package/ts/smartagent.tools.expert.ts +0 -144
- package/ts/smartagent.tools.filesystem.ts +0 -885
- package/ts/smartagent.tools.http.ts +0 -283
- package/ts/smartagent.tools.json.ts +0 -224
- package/ts/smartagent.tools.search.ts +0 -237
- package/ts/smartagent.tools.shell.ts +0 -230
|
@@ -1,692 +0,0 @@
|
|
|
1
|
-
import * as plugins from './plugins.js';
|
|
2
|
-
import * as interfaces from './smartagent.interfaces.js';
|
|
3
|
-
import { BaseToolWrapper } from './smartagent.tools.base.js';
|
|
4
|
-
import { DriverAgent } from './smartagent.classes.driveragent.js';
|
|
5
|
-
import { GuardianAgent } from './smartagent.classes.guardianagent.js';
|
|
6
|
-
import { FilesystemTool } from './smartagent.tools.filesystem.js';
|
|
7
|
-
import { HttpTool } from './smartagent.tools.http.js';
|
|
8
|
-
import { ShellTool } from './smartagent.tools.shell.js';
|
|
9
|
-
import { BrowserTool } from './smartagent.tools.browser.js';
|
|
10
|
-
import { DenoTool } from './smartagent.tools.deno.js';
|
|
11
|
-
import { ToolRegistry } from './smartagent.classes.toolregistry.js';
|
|
12
|
-
import { ToolSearchTool } from './smartagent.tools.search.js';
|
|
13
|
-
import { ExpertTool } from './smartagent.tools.expert.js';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* DualAgentOrchestrator - Coordinates Driver and Guardian agents
|
|
17
|
-
* Manages the complete lifecycle of task execution with tool approval
|
|
18
|
-
*/
|
|
19
|
-
export class DualAgentOrchestrator {
|
|
20
|
-
private options: interfaces.IDualAgentOptions;
|
|
21
|
-
private smartai: plugins.smartai.SmartAi;
|
|
22
|
-
private driverProvider: plugins.smartai.MultiModalModel;
|
|
23
|
-
private guardianProvider: plugins.smartai.MultiModalModel;
|
|
24
|
-
private driver: DriverAgent;
|
|
25
|
-
private guardian: GuardianAgent;
|
|
26
|
-
private registry: ToolRegistry = new ToolRegistry();
|
|
27
|
-
private isRunning = false;
|
|
28
|
-
private conversationHistory: interfaces.IAgentMessage[] = [];
|
|
29
|
-
private ownsSmartAi = true; // true if we created the SmartAi instance, false if it was provided
|
|
30
|
-
|
|
31
|
-
constructor(options: interfaces.IDualAgentOptions) {
|
|
32
|
-
this.options = {
|
|
33
|
-
maxIterations: 20,
|
|
34
|
-
maxConsecutiveRejections: 3,
|
|
35
|
-
defaultProvider: 'openai',
|
|
36
|
-
maxResultChars: 15000,
|
|
37
|
-
maxHistoryMessages: 20,
|
|
38
|
-
...options,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Use existing SmartAi instance if provided, otherwise create a new one
|
|
42
|
-
if (options.smartAiInstance) {
|
|
43
|
-
this.smartai = options.smartAiInstance;
|
|
44
|
-
this.ownsSmartAi = false; // Don't manage lifecycle of provided instance
|
|
45
|
-
} else {
|
|
46
|
-
this.smartai = new plugins.smartai.SmartAi(options);
|
|
47
|
-
this.ownsSmartAi = true;
|
|
48
|
-
}
|
|
49
|
-
// Note: Don't access providers here - they don't exist until start() is called
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Get provider by name
|
|
54
|
-
*/
|
|
55
|
-
private getProviderByName(providerName: plugins.smartai.TProvider): plugins.smartai.MultiModalModel {
|
|
56
|
-
switch (providerName) {
|
|
57
|
-
case 'openai':
|
|
58
|
-
return this.smartai.openaiProvider;
|
|
59
|
-
case 'anthropic':
|
|
60
|
-
return this.smartai.anthropicProvider;
|
|
61
|
-
case 'perplexity':
|
|
62
|
-
return this.smartai.perplexityProvider;
|
|
63
|
-
case 'ollama':
|
|
64
|
-
return this.smartai.ollamaProvider;
|
|
65
|
-
case 'groq':
|
|
66
|
-
return this.smartai.groqProvider;
|
|
67
|
-
case 'xai':
|
|
68
|
-
return this.smartai.xaiProvider;
|
|
69
|
-
case 'exo':
|
|
70
|
-
return this.smartai.exoProvider;
|
|
71
|
-
default:
|
|
72
|
-
return this.smartai.openaiProvider;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Emit a progress event if callback is configured
|
|
78
|
-
*/
|
|
79
|
-
private emitProgress(event: Omit<interfaces.IProgressEvent, 'timestamp' | 'logLevel' | 'logMessage'>): void {
|
|
80
|
-
if (this.options.onProgress) {
|
|
81
|
-
const prefix = this.options.logPrefix ? `${this.options.logPrefix} ` : '';
|
|
82
|
-
const { logLevel, logMessage } = this.formatProgressEvent(event, prefix);
|
|
83
|
-
|
|
84
|
-
this.options.onProgress({
|
|
85
|
-
...event,
|
|
86
|
-
timestamp: new Date(),
|
|
87
|
-
logLevel,
|
|
88
|
-
logMessage,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Format a progress event into a log level and message
|
|
95
|
-
*/
|
|
96
|
-
private formatProgressEvent(
|
|
97
|
-
event: Omit<interfaces.IProgressEvent, 'timestamp' | 'logLevel' | 'logMessage'>,
|
|
98
|
-
prefix: string
|
|
99
|
-
): { logLevel: interfaces.TLogLevel; logMessage: string } {
|
|
100
|
-
switch (event.type) {
|
|
101
|
-
case 'task_started':
|
|
102
|
-
return { logLevel: 'info', logMessage: `${prefix}Task started` };
|
|
103
|
-
case 'iteration_started':
|
|
104
|
-
return { logLevel: 'info', logMessage: `${prefix}Iteration ${event.iteration}/${event.maxIterations}` };
|
|
105
|
-
case 'tool_proposed':
|
|
106
|
-
return { logLevel: 'info', logMessage: `${prefix} → Proposing: ${event.toolName}.${event.action}` };
|
|
107
|
-
case 'guardian_evaluating':
|
|
108
|
-
return { logLevel: 'info', logMessage: `${prefix} ⏳ Guardian evaluating...` };
|
|
109
|
-
case 'tool_approved':
|
|
110
|
-
return { logLevel: 'info', logMessage: `${prefix} ✓ Approved: ${event.toolName}.${event.action}` };
|
|
111
|
-
case 'tool_rejected':
|
|
112
|
-
return { logLevel: 'warn', logMessage: `${prefix} ✗ Rejected: ${event.toolName}.${event.action} - ${event.reason}` };
|
|
113
|
-
case 'tool_executing':
|
|
114
|
-
return { logLevel: 'info', logMessage: `${prefix} ⚡ Executing: ${event.toolName}.${event.action}...` };
|
|
115
|
-
case 'tool_completed':
|
|
116
|
-
return { logLevel: 'info', logMessage: `${prefix} ✓ Completed: ${event.message}` };
|
|
117
|
-
case 'task_completed':
|
|
118
|
-
return { logLevel: 'success', logMessage: `${prefix}Task completed in ${event.iteration} iterations` };
|
|
119
|
-
case 'clarification_needed':
|
|
120
|
-
return { logLevel: 'warn', logMessage: `${prefix}Clarification needed from user` };
|
|
121
|
-
case 'max_iterations':
|
|
122
|
-
return { logLevel: 'error', logMessage: `${prefix}${event.message}` };
|
|
123
|
-
case 'max_rejections':
|
|
124
|
-
return { logLevel: 'error', logMessage: `${prefix}${event.message}` };
|
|
125
|
-
default:
|
|
126
|
-
return { logLevel: 'info', logMessage: `${prefix}${event.type}` };
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Register a custom tool with optional visibility settings
|
|
132
|
-
*/
|
|
133
|
-
public registerTool(
|
|
134
|
-
tool: BaseToolWrapper,
|
|
135
|
-
options?: interfaces.IToolRegistrationOptions
|
|
136
|
-
): void {
|
|
137
|
-
this.registry.register(tool, options);
|
|
138
|
-
|
|
139
|
-
// If initial visibility and agents exist, register with them
|
|
140
|
-
const visibility = options?.visibility ?? 'initial';
|
|
141
|
-
if (visibility === 'initial') {
|
|
142
|
-
if (this.driver) {
|
|
143
|
-
this.driver.registerTool(tool);
|
|
144
|
-
}
|
|
145
|
-
if (this.guardian) {
|
|
146
|
-
this.guardian.registerTool(tool);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Register an expert (subagent) as a tool
|
|
153
|
-
*/
|
|
154
|
-
public registerExpert(config: interfaces.IExpertConfig): void {
|
|
155
|
-
const expert = new ExpertTool(config, this.smartai);
|
|
156
|
-
this.registerTool(expert, {
|
|
157
|
-
visibility: config.visibility,
|
|
158
|
-
tags: config.tags,
|
|
159
|
-
category: config.category ?? 'expert',
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Enable tool search functionality
|
|
165
|
-
* This adds a 'tools' tool that allows the Driver to discover and activate on-demand tools
|
|
166
|
-
*/
|
|
167
|
-
public enableToolSearch(): void {
|
|
168
|
-
const searchTool = new ToolSearchTool(this.registry, (tool) => {
|
|
169
|
-
// Callback when an on-demand tool is activated
|
|
170
|
-
if (this.driver) {
|
|
171
|
-
this.driver.registerTool(tool);
|
|
172
|
-
}
|
|
173
|
-
if (this.guardian) {
|
|
174
|
-
this.guardian.registerTool(tool);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
this.registerTool(searchTool); // Always initial visibility
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Register all standard tools
|
|
182
|
-
*/
|
|
183
|
-
public registerStandardTools(): void {
|
|
184
|
-
const standardTools = [
|
|
185
|
-
new FilesystemTool(),
|
|
186
|
-
new HttpTool(),
|
|
187
|
-
new ShellTool(),
|
|
188
|
-
new BrowserTool(),
|
|
189
|
-
new DenoTool(),
|
|
190
|
-
];
|
|
191
|
-
|
|
192
|
-
for (const tool of standardTools) {
|
|
193
|
-
this.registerTool(tool);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Register a scoped filesystem tool that can only access files within the specified directory
|
|
199
|
-
* @param basePath The directory to scope filesystem operations to
|
|
200
|
-
* @param excludePatterns Optional glob patterns to exclude from listings (e.g., ['.nogit/**', 'node_modules/**'])
|
|
201
|
-
*/
|
|
202
|
-
public registerScopedFilesystemTool(basePath: string, excludePatterns?: string[]): void {
|
|
203
|
-
const scopedTool = new FilesystemTool({ basePath, excludePatterns });
|
|
204
|
-
this.registerTool(scopedTool);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Initialize all tools (eager loading)
|
|
209
|
-
*/
|
|
210
|
-
public async start(): Promise<void> {
|
|
211
|
-
// Start smartai only if we created it (external instances should already be started)
|
|
212
|
-
if (this.ownsSmartAi) {
|
|
213
|
-
await this.smartai.start();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// NOW get providers (after they've been initialized by smartai.start())
|
|
217
|
-
this.driverProvider = this.getProviderByName(this.options.defaultProvider!);
|
|
218
|
-
this.guardianProvider = this.options.guardianProvider
|
|
219
|
-
? this.getProviderByName(this.options.guardianProvider)
|
|
220
|
-
: this.driverProvider;
|
|
221
|
-
|
|
222
|
-
// NOW create agents with initialized providers
|
|
223
|
-
// Set up token callback wrapper if streaming is enabled
|
|
224
|
-
const driverOnToken = this.options.onToken
|
|
225
|
-
? (token: string) => this.options.onToken!(token, 'driver')
|
|
226
|
-
: undefined;
|
|
227
|
-
|
|
228
|
-
this.driver = new DriverAgent(this.driverProvider, {
|
|
229
|
-
systemMessage: this.options.driverSystemMessage,
|
|
230
|
-
maxHistoryMessages: this.options.maxHistoryMessages,
|
|
231
|
-
onToken: driverOnToken,
|
|
232
|
-
});
|
|
233
|
-
this.guardian = new GuardianAgent(this.guardianProvider, this.options.guardianPolicyPrompt);
|
|
234
|
-
|
|
235
|
-
// Register visible tools with agents
|
|
236
|
-
for (const tool of this.registry.getVisibleTools()) {
|
|
237
|
-
this.driver.registerTool(tool);
|
|
238
|
-
this.guardian.registerTool(tool);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Initialize visible tools
|
|
242
|
-
await this.registry.initializeVisibleTools();
|
|
243
|
-
this.isRunning = true;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Cleanup all tools
|
|
248
|
-
*/
|
|
249
|
-
public async stop(): Promise<void> {
|
|
250
|
-
await this.registry.cleanup();
|
|
251
|
-
|
|
252
|
-
// Only stop smartai if we created it (don't stop external instances)
|
|
253
|
-
if (this.ownsSmartAi) {
|
|
254
|
-
await this.smartai.stop();
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
this.isRunning = false;
|
|
258
|
-
if (this.driver) {
|
|
259
|
-
this.driver.reset();
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Run a task through the dual-agent system
|
|
265
|
-
* @param task The task description
|
|
266
|
-
* @param options Optional task run options (e.g., images for vision tasks)
|
|
267
|
-
*/
|
|
268
|
-
public async run(task: string, options?: interfaces.ITaskRunOptions): Promise<interfaces.IDualAgentRunResult> {
|
|
269
|
-
if (!this.isRunning) {
|
|
270
|
-
throw new Error('Orchestrator not started. Call start() first.');
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Use native tool calling if enabled
|
|
274
|
-
const useNativeTools = this.options.useNativeToolCalling === true;
|
|
275
|
-
|
|
276
|
-
this.conversationHistory = [];
|
|
277
|
-
let iterations = 0;
|
|
278
|
-
let consecutiveRejections = 0;
|
|
279
|
-
let completed = false;
|
|
280
|
-
let finalResult: string | null = null;
|
|
281
|
-
|
|
282
|
-
// Track pending native tool calls
|
|
283
|
-
let pendingNativeToolCalls: interfaces.INativeToolCall[] | undefined;
|
|
284
|
-
|
|
285
|
-
// Extract images from options
|
|
286
|
-
const images = options?.images;
|
|
287
|
-
|
|
288
|
-
// Add initial task to history
|
|
289
|
-
this.conversationHistory.push({
|
|
290
|
-
role: 'user',
|
|
291
|
-
content: task,
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
// Start the driver with the task and optional images
|
|
295
|
-
let driverResponse: interfaces.IAgentMessage;
|
|
296
|
-
|
|
297
|
-
if (useNativeTools) {
|
|
298
|
-
// Native tool calling mode
|
|
299
|
-
const result = await this.driver.startTaskWithNativeTools(task, images);
|
|
300
|
-
driverResponse = result.message;
|
|
301
|
-
pendingNativeToolCalls = result.toolCalls;
|
|
302
|
-
} else {
|
|
303
|
-
// XML parsing mode
|
|
304
|
-
driverResponse = await this.driver.startTask(task, images);
|
|
305
|
-
}
|
|
306
|
-
this.conversationHistory.push(driverResponse);
|
|
307
|
-
|
|
308
|
-
// Emit task started event
|
|
309
|
-
this.emitProgress({
|
|
310
|
-
type: 'task_started',
|
|
311
|
-
message: task.length > 100 ? task.substring(0, 100) + '...' : task,
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
while (
|
|
315
|
-
iterations < this.options.maxIterations! &&
|
|
316
|
-
consecutiveRejections < this.options.maxConsecutiveRejections! &&
|
|
317
|
-
!completed
|
|
318
|
-
) {
|
|
319
|
-
iterations++;
|
|
320
|
-
|
|
321
|
-
// Emit iteration started event
|
|
322
|
-
this.emitProgress({
|
|
323
|
-
type: 'iteration_started',
|
|
324
|
-
iteration: iterations,
|
|
325
|
-
maxIterations: this.options.maxIterations,
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Check if task is complete (for native mode, no pending tool calls and has content)
|
|
329
|
-
const isComplete = useNativeTools
|
|
330
|
-
? (!pendingNativeToolCalls || pendingNativeToolCalls.length === 0) && driverResponse.content.length > 0
|
|
331
|
-
: this.driver.isTaskComplete(driverResponse.content);
|
|
332
|
-
|
|
333
|
-
if (isComplete) {
|
|
334
|
-
completed = true;
|
|
335
|
-
finalResult = useNativeTools
|
|
336
|
-
? driverResponse.content
|
|
337
|
-
: (this.driver.extractTaskResult(driverResponse.content) || driverResponse.content);
|
|
338
|
-
|
|
339
|
-
// Emit task completed event
|
|
340
|
-
this.emitProgress({
|
|
341
|
-
type: 'task_completed',
|
|
342
|
-
iteration: iterations,
|
|
343
|
-
message: 'Task completed successfully',
|
|
344
|
-
});
|
|
345
|
-
break;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Check if driver needs clarification
|
|
349
|
-
if (this.driver.needsClarification(driverResponse.content)) {
|
|
350
|
-
// Emit clarification needed event
|
|
351
|
-
this.emitProgress({
|
|
352
|
-
type: 'clarification_needed',
|
|
353
|
-
iteration: iterations,
|
|
354
|
-
message: 'Driver needs clarification from user',
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
// Return with clarification needed status
|
|
358
|
-
return {
|
|
359
|
-
success: false,
|
|
360
|
-
completed: false,
|
|
361
|
-
result: driverResponse.content,
|
|
362
|
-
iterations,
|
|
363
|
-
history: this.conversationHistory,
|
|
364
|
-
status: 'clarification_needed',
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Parse tool call proposals - native mode uses pendingNativeToolCalls, XML mode parses content
|
|
369
|
-
let proposals: interfaces.IToolCallProposal[];
|
|
370
|
-
|
|
371
|
-
if (useNativeTools && pendingNativeToolCalls && pendingNativeToolCalls.length > 0) {
|
|
372
|
-
// Native tool calling mode - convert native tool calls to proposals
|
|
373
|
-
proposals = this.driver.parseNativeToolCalls(pendingNativeToolCalls);
|
|
374
|
-
pendingNativeToolCalls = undefined; // Clear after processing
|
|
375
|
-
} else if (!useNativeTools) {
|
|
376
|
-
// XML parsing mode
|
|
377
|
-
proposals = this.driver.parseToolCallProposals(driverResponse.content);
|
|
378
|
-
} else {
|
|
379
|
-
proposals = [];
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (proposals.length === 0) {
|
|
383
|
-
if (useNativeTools) {
|
|
384
|
-
// Native mode: no tool calls and no content means we should continue
|
|
385
|
-
const result = await this.driver.continueWithNativeTools(
|
|
386
|
-
'Please continue with the task. Use the available tools or provide your final output.'
|
|
387
|
-
);
|
|
388
|
-
driverResponse = result.message;
|
|
389
|
-
pendingNativeToolCalls = result.toolCalls;
|
|
390
|
-
this.conversationHistory.push(driverResponse);
|
|
391
|
-
continue;
|
|
392
|
-
} else {
|
|
393
|
-
// XML mode: remind the model of the exact XML format
|
|
394
|
-
driverResponse = await this.driver.continueWithMessage(
|
|
395
|
-
`No valid tool call was found in your response. To use a tool, you MUST output the exact XML format:
|
|
396
|
-
|
|
397
|
-
<tool_call>
|
|
398
|
-
<tool>tool_name</tool>
|
|
399
|
-
<action>action_name</action>
|
|
400
|
-
<params>{"param1": "value1"}</params>
|
|
401
|
-
</tool_call>
|
|
402
|
-
|
|
403
|
-
For example, to validate JSON:
|
|
404
|
-
<tool_call>
|
|
405
|
-
<tool>json</tool>
|
|
406
|
-
<action>validate</action>
|
|
407
|
-
<params>{"jsonString": "{\\"key\\":\\"value\\"}", "requiredFields": ["key"]}</params>
|
|
408
|
-
</tool_call>
|
|
409
|
-
|
|
410
|
-
Or to complete the task:
|
|
411
|
-
<task_complete>your final JSON output here</task_complete>
|
|
412
|
-
|
|
413
|
-
Please output the exact XML format above.`
|
|
414
|
-
);
|
|
415
|
-
this.conversationHistory.push(driverResponse);
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Process the first proposal (one at a time)
|
|
421
|
-
const proposal = proposals[0];
|
|
422
|
-
|
|
423
|
-
// Emit tool proposed event
|
|
424
|
-
this.emitProgress({
|
|
425
|
-
type: 'tool_proposed',
|
|
426
|
-
iteration: iterations,
|
|
427
|
-
toolName: proposal.toolName,
|
|
428
|
-
action: proposal.action,
|
|
429
|
-
message: `${proposal.toolName}.${proposal.action}`,
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
// Quick validation first
|
|
433
|
-
const quickDecision = this.guardian.quickValidate(proposal);
|
|
434
|
-
let decision: interfaces.IGuardianDecision;
|
|
435
|
-
|
|
436
|
-
if (quickDecision) {
|
|
437
|
-
decision = quickDecision;
|
|
438
|
-
} else {
|
|
439
|
-
// Emit guardian evaluating event
|
|
440
|
-
this.emitProgress({
|
|
441
|
-
type: 'guardian_evaluating',
|
|
442
|
-
iteration: iterations,
|
|
443
|
-
toolName: proposal.toolName,
|
|
444
|
-
action: proposal.action,
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// Full AI evaluation
|
|
448
|
-
decision = await this.guardian.evaluate(proposal, task);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if (decision.decision === 'approve') {
|
|
452
|
-
consecutiveRejections = 0;
|
|
453
|
-
|
|
454
|
-
// Emit tool approved event
|
|
455
|
-
this.emitProgress({
|
|
456
|
-
type: 'tool_approved',
|
|
457
|
-
iteration: iterations,
|
|
458
|
-
toolName: proposal.toolName,
|
|
459
|
-
action: proposal.action,
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
// Execute the tool
|
|
463
|
-
const tool = this.registry.getTool(proposal.toolName);
|
|
464
|
-
if (!tool) {
|
|
465
|
-
const errorMessage = `Tool "${proposal.toolName}" not found.`;
|
|
466
|
-
driverResponse = await this.driver.continueWithMessage(
|
|
467
|
-
`TOOL ERROR: ${errorMessage}\n\nPlease try a different approach.`
|
|
468
|
-
);
|
|
469
|
-
this.conversationHistory.push(driverResponse);
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
try {
|
|
474
|
-
// Emit tool executing event
|
|
475
|
-
this.emitProgress({
|
|
476
|
-
type: 'tool_executing',
|
|
477
|
-
iteration: iterations,
|
|
478
|
-
toolName: proposal.toolName,
|
|
479
|
-
action: proposal.action,
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
const result = await tool.execute(proposal.action, proposal.params);
|
|
483
|
-
|
|
484
|
-
// Emit tool completed event
|
|
485
|
-
this.emitProgress({
|
|
486
|
-
type: 'tool_completed',
|
|
487
|
-
iteration: iterations,
|
|
488
|
-
toolName: proposal.toolName,
|
|
489
|
-
action: proposal.action,
|
|
490
|
-
message: result.success ? 'success' : result.error,
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Build result message (prefer summary if provided, otherwise stringify result)
|
|
494
|
-
let resultMessage: string;
|
|
495
|
-
if (result.success) {
|
|
496
|
-
if (result.summary) {
|
|
497
|
-
// Use tool-provided summary
|
|
498
|
-
resultMessage = `TOOL RESULT (${proposal.toolName}.${proposal.action}):\n${result.summary}`;
|
|
499
|
-
} else {
|
|
500
|
-
// Stringify and potentially truncate
|
|
501
|
-
const resultStr = JSON.stringify(result.result, null, 2);
|
|
502
|
-
const maxChars = this.options.maxResultChars ?? 15000;
|
|
503
|
-
|
|
504
|
-
if (maxChars > 0 && resultStr.length > maxChars) {
|
|
505
|
-
// Truncate the result
|
|
506
|
-
const truncated = resultStr.substring(0, maxChars);
|
|
507
|
-
const omittedTokens = Math.round((resultStr.length - maxChars) / 4);
|
|
508
|
-
resultMessage = `TOOL RESULT (${proposal.toolName}.${proposal.action}):\n${truncated}\n\n[... output truncated, ~${omittedTokens} tokens omitted. Use more specific parameters to reduce output size.]`;
|
|
509
|
-
} else {
|
|
510
|
-
resultMessage = `TOOL RESULT (${proposal.toolName}.${proposal.action}):\n${resultStr}`;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
} else {
|
|
514
|
-
resultMessage = `TOOL ERROR (${proposal.toolName}.${proposal.action}):\n${result.error}`;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
this.conversationHistory.push({
|
|
518
|
-
role: 'system',
|
|
519
|
-
content: resultMessage,
|
|
520
|
-
toolCall: proposal,
|
|
521
|
-
toolResult: result,
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
// Continue with appropriate method based on mode
|
|
525
|
-
if (useNativeTools) {
|
|
526
|
-
const toolNameForHistory = `${proposal.toolName}_${proposal.action}`;
|
|
527
|
-
const continueResult = await this.driver.continueWithNativeTools(resultMessage, toolNameForHistory);
|
|
528
|
-
driverResponse = continueResult.message;
|
|
529
|
-
pendingNativeToolCalls = continueResult.toolCalls;
|
|
530
|
-
} else {
|
|
531
|
-
driverResponse = await this.driver.continueWithMessage(resultMessage);
|
|
532
|
-
}
|
|
533
|
-
this.conversationHistory.push(driverResponse);
|
|
534
|
-
} catch (error) {
|
|
535
|
-
const errorMessage = `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
536
|
-
if (useNativeTools) {
|
|
537
|
-
const toolNameForHistory = `${proposal.toolName}_${proposal.action}`;
|
|
538
|
-
const continueResult = await this.driver.continueWithNativeTools(
|
|
539
|
-
`TOOL ERROR: ${errorMessage}\n\nPlease try a different approach.`,
|
|
540
|
-
toolNameForHistory
|
|
541
|
-
);
|
|
542
|
-
driverResponse = continueResult.message;
|
|
543
|
-
pendingNativeToolCalls = continueResult.toolCalls;
|
|
544
|
-
} else {
|
|
545
|
-
driverResponse = await this.driver.continueWithMessage(
|
|
546
|
-
`TOOL ERROR: ${errorMessage}\n\nPlease try a different approach.`
|
|
547
|
-
);
|
|
548
|
-
}
|
|
549
|
-
this.conversationHistory.push(driverResponse);
|
|
550
|
-
}
|
|
551
|
-
} else {
|
|
552
|
-
// Rejected
|
|
553
|
-
consecutiveRejections++;
|
|
554
|
-
|
|
555
|
-
// Emit tool rejected event
|
|
556
|
-
this.emitProgress({
|
|
557
|
-
type: 'tool_rejected',
|
|
558
|
-
iteration: iterations,
|
|
559
|
-
toolName: proposal.toolName,
|
|
560
|
-
action: proposal.action,
|
|
561
|
-
reason: decision.reason,
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
// Build rejection feedback
|
|
565
|
-
let feedback = `TOOL CALL REJECTED by Guardian:\n`;
|
|
566
|
-
feedback += `- Reason: ${decision.reason}\n`;
|
|
567
|
-
|
|
568
|
-
if (decision.concerns && decision.concerns.length > 0) {
|
|
569
|
-
feedback += `- Concerns:\n${decision.concerns.map(c => ` - ${c}`).join('\n')}\n`;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
if (decision.suggestions) {
|
|
573
|
-
feedback += `- Suggestions: ${decision.suggestions}\n`;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
feedback += `\nPlease adapt your approach based on this feedback.`;
|
|
577
|
-
|
|
578
|
-
this.conversationHistory.push({
|
|
579
|
-
role: 'system',
|
|
580
|
-
content: feedback,
|
|
581
|
-
toolCall: proposal,
|
|
582
|
-
guardianDecision: decision,
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
// Continue with appropriate method based on mode
|
|
586
|
-
if (useNativeTools) {
|
|
587
|
-
const continueResult = await this.driver.continueWithNativeTools(feedback);
|
|
588
|
-
driverResponse = continueResult.message;
|
|
589
|
-
pendingNativeToolCalls = continueResult.toolCalls;
|
|
590
|
-
} else {
|
|
591
|
-
driverResponse = await this.driver.continueWithMessage(feedback);
|
|
592
|
-
}
|
|
593
|
-
this.conversationHistory.push(driverResponse);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// Determine final status
|
|
598
|
-
let status: interfaces.TDualAgentRunStatus = 'completed';
|
|
599
|
-
if (!completed) {
|
|
600
|
-
if (iterations >= this.options.maxIterations!) {
|
|
601
|
-
status = 'max_iterations_reached';
|
|
602
|
-
// Emit max iterations event
|
|
603
|
-
this.emitProgress({
|
|
604
|
-
type: 'max_iterations',
|
|
605
|
-
iteration: iterations,
|
|
606
|
-
maxIterations: this.options.maxIterations,
|
|
607
|
-
message: `Maximum iterations (${this.options.maxIterations}) reached`,
|
|
608
|
-
});
|
|
609
|
-
} else if (consecutiveRejections >= this.options.maxConsecutiveRejections!) {
|
|
610
|
-
status = 'max_rejections_reached';
|
|
611
|
-
// Emit max rejections event
|
|
612
|
-
this.emitProgress({
|
|
613
|
-
type: 'max_rejections',
|
|
614
|
-
iteration: iterations,
|
|
615
|
-
message: `Maximum consecutive rejections (${this.options.maxConsecutiveRejections}) reached`,
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
return {
|
|
621
|
-
success: completed,
|
|
622
|
-
completed,
|
|
623
|
-
result: finalResult || driverResponse.content,
|
|
624
|
-
iterations,
|
|
625
|
-
history: this.conversationHistory,
|
|
626
|
-
status,
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/**
|
|
631
|
-
* Continue an existing task with user input
|
|
632
|
-
*/
|
|
633
|
-
public async continueTask(userInput: string): Promise<interfaces.IDualAgentRunResult> {
|
|
634
|
-
if (!this.isRunning) {
|
|
635
|
-
throw new Error('Orchestrator not started. Call start() first.');
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
this.conversationHistory.push({
|
|
639
|
-
role: 'user',
|
|
640
|
-
content: userInput,
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
const driverResponse = await this.driver.continueWithMessage(userInput);
|
|
644
|
-
this.conversationHistory.push(driverResponse);
|
|
645
|
-
|
|
646
|
-
// Continue the run loop
|
|
647
|
-
// For simplicity, we return the current state - full continuation would need refactoring
|
|
648
|
-
return {
|
|
649
|
-
success: false,
|
|
650
|
-
completed: false,
|
|
651
|
-
result: driverResponse.content,
|
|
652
|
-
iterations: 1,
|
|
653
|
-
history: this.conversationHistory,
|
|
654
|
-
status: 'in_progress',
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/**
|
|
659
|
-
* Get the conversation history
|
|
660
|
-
*/
|
|
661
|
-
public getHistory(): interfaces.IAgentMessage[] {
|
|
662
|
-
return [...this.conversationHistory];
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/**
|
|
666
|
-
* Update the guardian policy
|
|
667
|
-
*/
|
|
668
|
-
public setGuardianPolicy(policyPrompt: string): void {
|
|
669
|
-
this.guardian.setPolicy(policyPrompt);
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
/**
|
|
673
|
-
* Check if orchestrator is running
|
|
674
|
-
*/
|
|
675
|
-
public isActive(): boolean {
|
|
676
|
-
return this.isRunning;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* Get registered tool names
|
|
681
|
-
*/
|
|
682
|
-
public getToolNames(): string[] {
|
|
683
|
-
return this.registry.getAllMetadata().map((m) => m.name);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* Get the tool registry for advanced operations
|
|
688
|
-
*/
|
|
689
|
-
public getRegistry(): ToolRegistry {
|
|
690
|
-
return this.registry;
|
|
691
|
-
}
|
|
692
|
-
}
|