@tyvm/knowhow 0.0.20 → 0.0.22
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/package.json +3 -1
- package/src/agents/base/base.ts +16 -77
- package/src/agents/tools/executeScript/README.md +78 -0
- package/src/agents/tools/executeScript/definition.ts +73 -0
- package/src/agents/tools/executeScript/examples/quick-test.ts +80 -0
- package/src/agents/tools/executeScript/examples/serialization-test.ts +309 -0
- package/src/agents/tools/executeScript/examples/test-runner.ts +204 -0
- package/src/agents/tools/executeScript/index.ts +74 -0
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/list.ts +2 -1
- package/src/cli.ts +2 -6
- package/src/clients/index.ts +23 -9
- package/src/services/Tools.ts +150 -9
- package/src/services/script-execution/SandboxContext.ts +278 -0
- package/src/services/script-execution/ScriptExecutor.ts +337 -0
- package/src/services/script-execution/ScriptPolicy.ts +236 -0
- package/src/services/script-execution/ScriptTracer.ts +249 -0
- package/src/services/script-execution/types.ts +134 -0
- package/ts_build/src/agents/base/base.js +2 -53
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/executeScript/definition.d.ts +2 -0
- package/ts_build/src/agents/tools/executeScript/definition.js +70 -0
- package/ts_build/src/agents/tools/executeScript/definition.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +3 -0
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +68 -0
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +15 -0
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +267 -0
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/simple-example.d.ts +20 -0
- package/ts_build/src/agents/tools/executeScript/examples/simple-example.js +35 -0
- package/ts_build/src/agents/tools/executeScript/examples/simple-example.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +4 -0
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +202 -0
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/handler.d.ts +27 -0
- package/ts_build/src/agents/tools/executeScript/handler.js +64 -0
- package/ts_build/src/agents/tools/executeScript/handler.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/index.d.ts +27 -0
- package/ts_build/src/agents/tools/executeScript/index.js +64 -0
- package/ts_build/src/agents/tools/executeScript/index.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript.d.ts +29 -0
- package/ts_build/src/agents/tools/executeScript.js +124 -0
- package/ts_build/src/agents/tools/executeScript.js.map +1 -0
- package/ts_build/src/agents/tools/index.d.ts +1 -0
- package/ts_build/src/agents/tools/index.js +1 -0
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +2 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/cli.js +2 -6
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +9 -2
- package/ts_build/src/clients/index.js +17 -4
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +11 -1
- package/ts_build/src/services/Tools.js +94 -3
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/script-execution/SandboxContext.d.ts +34 -0
- package/ts_build/src/services/script-execution/SandboxContext.js +188 -0
- package/ts_build/src/services/script-execution/SandboxContext.js.map +1 -0
- package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +17 -0
- package/ts_build/src/services/script-execution/ScriptExecutor.js +207 -0
- package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -0
- package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +27 -0
- package/ts_build/src/services/script-execution/ScriptPolicy.js +150 -0
- package/ts_build/src/services/script-execution/ScriptPolicy.js.map +1 -0
- package/ts_build/src/services/script-execution/ScriptTracer.d.ts +19 -0
- package/ts_build/src/services/script-execution/ScriptTracer.js +186 -0
- package/ts_build/src/services/script-execution/ScriptTracer.js.map +1 -0
- package/ts_build/src/services/script-execution/types.d.ts +108 -0
- package/ts_build/src/services/script-execution/types.js +3 -0
- package/ts_build/src/services/script-execution/types.js.map +1 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ResourceQuotas,
|
|
3
|
+
SecurityPolicy,
|
|
4
|
+
QuotaUsage,
|
|
5
|
+
PolicyViolation
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Enforces security policies and resource quotas for script execution
|
|
10
|
+
*/
|
|
11
|
+
export class ScriptPolicyEnforcer {
|
|
12
|
+
private usage: QuotaUsage;
|
|
13
|
+
private violations: PolicyViolation[] = [];
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
private quotas: ResourceQuotas,
|
|
17
|
+
private policy: SecurityPolicy
|
|
18
|
+
) {
|
|
19
|
+
this.usage = {
|
|
20
|
+
toolCalls: 0,
|
|
21
|
+
tokens: 0,
|
|
22
|
+
executionTimeMs: 0,
|
|
23
|
+
costUsd: 0
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a tool call is allowed
|
|
29
|
+
*/
|
|
30
|
+
checkToolCall(toolName: string): boolean {
|
|
31
|
+
// Check if tool is in denylist
|
|
32
|
+
if (this.policy.denylistedTools && this.policy.denylistedTools.includes(toolName)) {
|
|
33
|
+
this.recordViolation('tool_denied', `Tool '${toolName}' is in denylist`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check if tool is in allowlist (if allowlist is defined and not empty)
|
|
38
|
+
if (this.policy.allowlistedTools && this.policy.allowlistedTools.length > 0 &&
|
|
39
|
+
!this.policy.allowlistedTools.includes(toolName)) {
|
|
40
|
+
this.recordViolation('tool_not_allowed', `Tool '${toolName}' is not in allowlist`);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check quota
|
|
45
|
+
if (this.usage.toolCalls >= this.quotas.maxToolCalls) {
|
|
46
|
+
this.recordViolation('quota_exceeded', 'Maximum tool calls exceeded');
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Record a tool call
|
|
55
|
+
*/
|
|
56
|
+
recordToolCall(): void {
|
|
57
|
+
this.usage.toolCalls++;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if token usage is allowed
|
|
62
|
+
*/
|
|
63
|
+
checkTokenUsage(tokens: number): boolean {
|
|
64
|
+
if (this.usage.tokens + tokens > this.quotas.maxTokens) {
|
|
65
|
+
this.recordViolation('quota_exceeded', 'Maximum tokens would be exceeded');
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Record token usage
|
|
73
|
+
*/
|
|
74
|
+
recordTokenUsage(tokens: number): void {
|
|
75
|
+
this.usage.tokens += tokens;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if execution time limit is exceeded
|
|
80
|
+
*/
|
|
81
|
+
checkExecutionTime(currentTimeMs: number): boolean {
|
|
82
|
+
if (currentTimeMs > this.quotas.maxExecutionTimeMs) {
|
|
83
|
+
this.recordViolation('quota_exceeded', 'Maximum execution time exceeded');
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
this.usage.executionTimeMs = currentTimeMs;
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if cost limit is exceeded
|
|
92
|
+
*/
|
|
93
|
+
checkCost(additionalCost: number): boolean {
|
|
94
|
+
if (this.usage.costUsd + additionalCost > this.quotas.maxCostUsd) {
|
|
95
|
+
this.recordViolation('quota_exceeded', 'Maximum cost would be exceeded');
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Record cost usage
|
|
103
|
+
*/
|
|
104
|
+
recordCost(cost: number): void {
|
|
105
|
+
this.usage.costUsd += cost;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get current usage
|
|
110
|
+
*/
|
|
111
|
+
getUsage(): QuotaUsage {
|
|
112
|
+
return { ...this.usage };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get current quotas
|
|
117
|
+
*/
|
|
118
|
+
getQuotas(): ResourceQuotas {
|
|
119
|
+
return { ...this.quotas };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get all policy violations
|
|
124
|
+
*/
|
|
125
|
+
getViolations(): PolicyViolation[] {
|
|
126
|
+
return [...this.violations];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if there are any violations
|
|
131
|
+
*/
|
|
132
|
+
hasViolations(): boolean {
|
|
133
|
+
return this.violations.length > 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get the most recent violation
|
|
138
|
+
*/
|
|
139
|
+
getLastViolation(): PolicyViolation | undefined {
|
|
140
|
+
return this.violations[this.violations.length - 1];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Reset usage counters
|
|
145
|
+
*/
|
|
146
|
+
resetUsage(): void {
|
|
147
|
+
this.usage = {
|
|
148
|
+
toolCalls: 0,
|
|
149
|
+
tokens: 0,
|
|
150
|
+
executionTimeMs: 0,
|
|
151
|
+
costUsd: 0
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Reset violations
|
|
157
|
+
*/
|
|
158
|
+
resetViolations(): void {
|
|
159
|
+
this.violations = [];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Validate script content for security issues
|
|
164
|
+
*/
|
|
165
|
+
validateScript(scriptContent: string): { valid: boolean; issues: string[] } {
|
|
166
|
+
const issues: string[] = [];
|
|
167
|
+
|
|
168
|
+
// Check for dangerous patterns
|
|
169
|
+
const dangerousPatterns = [
|
|
170
|
+
/require\s*\(/gi, // Node.js require
|
|
171
|
+
/import\s+.*\s+from/gi, // ES6 imports (should be handled by bundler)
|
|
172
|
+
/process\./gi, // Process access
|
|
173
|
+
/global\./gi, // Global object access
|
|
174
|
+
/eval\s*\(/gi, // eval calls
|
|
175
|
+
/Function\s*\(/gi, // Function constructor
|
|
176
|
+
/setTimeout/gi, // setTimeout
|
|
177
|
+
/setInterval/gi, // setInterval
|
|
178
|
+
/fetch\s*\(/gi, // Direct fetch calls
|
|
179
|
+
/XMLHttpRequest/gi, // XHR
|
|
180
|
+
/WebSocket/gi, // WebSocket
|
|
181
|
+
/location\./gi, // Location object
|
|
182
|
+
/document\./gi, // Document object
|
|
183
|
+
/window\./gi, // Window object
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
for (const pattern of dangerousPatterns) {
|
|
187
|
+
if (pattern.test(scriptContent)) {
|
|
188
|
+
issues.push(`Potentially dangerous pattern detected: ${pattern.source}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check script length
|
|
193
|
+
if (scriptContent.length > this.policy.maxScriptLength) {
|
|
194
|
+
issues.push(`Script too long: ${scriptContent.length} > ${this.policy.maxScriptLength}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Check for excessive complexity (rough heuristic)
|
|
198
|
+
const complexityIndicators = [
|
|
199
|
+
/for\s*\(/gi,
|
|
200
|
+
/while\s*\(/gi,
|
|
201
|
+
/function\s+\w+/gi,
|
|
202
|
+
/=>\s*{/gi,
|
|
203
|
+
/if\s*\(/gi,
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
let complexityScore = 0;
|
|
207
|
+
for (const indicator of complexityIndicators) {
|
|
208
|
+
const matches = scriptContent.match(indicator);
|
|
209
|
+
complexityScore += matches ? matches.length : 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (complexityScore > 50) {
|
|
213
|
+
issues.push(`Script complexity too high: ${complexityScore} constructs detected`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
valid: issues.length === 0,
|
|
218
|
+
issues
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Record a policy violation
|
|
224
|
+
*/
|
|
225
|
+
private recordViolation(type: 'quota_exceeded' | 'tool_denied' | 'tool_not_allowed' | 'script_validation', message: string): void {
|
|
226
|
+
const violation: PolicyViolation = {
|
|
227
|
+
id: `violation-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
228
|
+
type,
|
|
229
|
+
message,
|
|
230
|
+
timestamp: Date.now(),
|
|
231
|
+
usage: { ...this.usage }
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
this.violations.push(violation);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { TraceEvent, TraceMetrics, ExecutionTrace, QuotaUsage } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles tracing and monitoring of script execution
|
|
5
|
+
*/
|
|
6
|
+
export class ScriptTracer {
|
|
7
|
+
private events: TraceEvent[] = [];
|
|
8
|
+
private metrics: TraceMetrics;
|
|
9
|
+
private startTime: number;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.startTime = Date.now();
|
|
13
|
+
this.metrics = {
|
|
14
|
+
executionTimeMs: 0,
|
|
15
|
+
toolCallCount: 0,
|
|
16
|
+
llmCallCount: 0,
|
|
17
|
+
tokenUsage: {
|
|
18
|
+
prompt: 0,
|
|
19
|
+
completion: 0,
|
|
20
|
+
total: 0,
|
|
21
|
+
},
|
|
22
|
+
memoryUsage: {
|
|
23
|
+
heapUsed: 0,
|
|
24
|
+
heapTotal: 0,
|
|
25
|
+
},
|
|
26
|
+
costUsd: 0,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Emit a trace event
|
|
32
|
+
*/
|
|
33
|
+
emitEvent(type: string, data: any): void {
|
|
34
|
+
const event: TraceEvent = {
|
|
35
|
+
id: `evt-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
36
|
+
type,
|
|
37
|
+
timestamp: Date.now(),
|
|
38
|
+
data: this.sanitizeEventData(data),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
this.events.push(event);
|
|
42
|
+
this.updateMetrics(event);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Record cost for tracking
|
|
47
|
+
*/
|
|
48
|
+
recordCost(costUsd: number): void {
|
|
49
|
+
this.metrics.costUsd += costUsd;
|
|
50
|
+
this.emitEvent("cost_recorded", {
|
|
51
|
+
costUsd,
|
|
52
|
+
totalCost: this.metrics.costUsd,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current quota usage
|
|
58
|
+
*/
|
|
59
|
+
getCurrentQuota(): QuotaUsage {
|
|
60
|
+
return {
|
|
61
|
+
toolCalls: this.metrics.toolCallCount,
|
|
62
|
+
tokens: this.metrics.tokenUsage.total,
|
|
63
|
+
executionTimeMs: Date.now() - this.startTime,
|
|
64
|
+
costUsd: this.metrics.costUsd,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get all trace events
|
|
70
|
+
*/
|
|
71
|
+
getEvents(): TraceEvent[] {
|
|
72
|
+
return [...this.events];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get current metrics
|
|
77
|
+
*/
|
|
78
|
+
getMetrics(): TraceMetrics {
|
|
79
|
+
return {
|
|
80
|
+
...this.metrics,
|
|
81
|
+
executionTimeMs: Date.now() - this.startTime,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generate execution trace
|
|
87
|
+
*/
|
|
88
|
+
getTrace(): ExecutionTrace {
|
|
89
|
+
return {
|
|
90
|
+
id: `trace-${Date.now()}`,
|
|
91
|
+
startTime: this.startTime,
|
|
92
|
+
endTime: Date.now(),
|
|
93
|
+
events: this.getEvents(),
|
|
94
|
+
metrics: this.getMetrics(),
|
|
95
|
+
success: !this.events.some((e) => e.type.includes("error")),
|
|
96
|
+
error: this.getLastError(),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Clear all events and reset metrics
|
|
102
|
+
*/
|
|
103
|
+
reset(): void {
|
|
104
|
+
this.events = [];
|
|
105
|
+
this.startTime = Date.now();
|
|
106
|
+
this.metrics = {
|
|
107
|
+
executionTimeMs: 0,
|
|
108
|
+
toolCallCount: 0,
|
|
109
|
+
llmCallCount: 0,
|
|
110
|
+
tokenUsage: {
|
|
111
|
+
prompt: 0,
|
|
112
|
+
completion: 0,
|
|
113
|
+
total: 0,
|
|
114
|
+
},
|
|
115
|
+
memoryUsage: {
|
|
116
|
+
heapUsed: 0,
|
|
117
|
+
heapTotal: 0,
|
|
118
|
+
},
|
|
119
|
+
costUsd: 0,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Update metrics based on events
|
|
125
|
+
*/
|
|
126
|
+
private updateMetrics(event: TraceEvent): void {
|
|
127
|
+
switch (event.type) {
|
|
128
|
+
case "tool_call_start":
|
|
129
|
+
this.metrics.toolCallCount++;
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
case "llm_call_start":
|
|
133
|
+
this.metrics.llmCallCount++;
|
|
134
|
+
break;
|
|
135
|
+
|
|
136
|
+
case "llm_call_success":
|
|
137
|
+
if (event.data && event.data.usage) {
|
|
138
|
+
const usage = event.data.usage;
|
|
139
|
+
this.metrics.tokenUsage.prompt += usage.prompt_tokens || 0;
|
|
140
|
+
this.metrics.tokenUsage.completion += usage.completion_tokens || 0;
|
|
141
|
+
this.metrics.tokenUsage.total += usage.total_tokens || 0;
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Update memory usage if available
|
|
147
|
+
if (typeof process !== "undefined" && process.memoryUsage) {
|
|
148
|
+
const memUsage = process.memoryUsage();
|
|
149
|
+
this.metrics.memoryUsage.heapUsed = memUsage.heapUsed;
|
|
150
|
+
this.metrics.memoryUsage.heapTotal = memUsage.heapTotal;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get the last error from events
|
|
156
|
+
*/
|
|
157
|
+
private getLastError(): string | undefined {
|
|
158
|
+
const errorEvents = this.events
|
|
159
|
+
.filter((e) => e.type.includes("error"))
|
|
160
|
+
.reverse();
|
|
161
|
+
|
|
162
|
+
if (errorEvents.length > 0) {
|
|
163
|
+
const lastError = errorEvents[0];
|
|
164
|
+
return (
|
|
165
|
+
lastError.data?.error || lastError.data?.message || "Unknown error"
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Sanitize event data to prevent sensitive information leaks
|
|
174
|
+
*/
|
|
175
|
+
private sanitizeEventData(data: any): any {
|
|
176
|
+
if (data === null || data === undefined) {
|
|
177
|
+
return data;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (typeof data === "string") {
|
|
181
|
+
// Truncate very long strings
|
|
182
|
+
return data.length > 1000
|
|
183
|
+
? data.substring(0, 1000) + "...[TRUNCATED]"
|
|
184
|
+
: data;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (Array.isArray(data)) {
|
|
188
|
+
return data.map((item) => this.sanitizeEventData(item));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (typeof data === "object") {
|
|
192
|
+
const sanitized: any = {};
|
|
193
|
+
for (const [key, value] of Object.entries(data)) {
|
|
194
|
+
// Redact sensitive keys
|
|
195
|
+
if (this.isSensitiveKey(key)) {
|
|
196
|
+
sanitized[key] = "[REDACTED]";
|
|
197
|
+
} else if (key === "parameters" && typeof value === "object") {
|
|
198
|
+
// Special handling for tool parameters
|
|
199
|
+
sanitized[key] = this.sanitizeParameters(value);
|
|
200
|
+
} else {
|
|
201
|
+
sanitized[key] = this.sanitizeEventData(value);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return sanitized;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return data;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Check if a key contains sensitive information
|
|
212
|
+
*/
|
|
213
|
+
private isSensitiveKey(key: string): boolean {
|
|
214
|
+
const lowerKey = key.toLowerCase();
|
|
215
|
+
const sensitivePatterns = [
|
|
216
|
+
"password",
|
|
217
|
+
"secret",
|
|
218
|
+
"token",
|
|
219
|
+
"key",
|
|
220
|
+
"auth",
|
|
221
|
+
"credential",
|
|
222
|
+
"private",
|
|
223
|
+
"confidential",
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
return sensitivePatterns.some((pattern) => lowerKey.includes(pattern));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Sanitize tool parameters
|
|
231
|
+
*/
|
|
232
|
+
private sanitizeParameters(params: any): any {
|
|
233
|
+
if (!params || typeof params !== "object") {
|
|
234
|
+
return params;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const sanitized: any = {};
|
|
238
|
+
for (const [key, value] of Object.entries(params)) {
|
|
239
|
+
if (this.isSensitiveKey(key)) {
|
|
240
|
+
sanitized[key] = "[REDACTED]";
|
|
241
|
+
} else if (typeof value === "string" && value.length > 500) {
|
|
242
|
+
sanitized[key] = value.substring(0, 500) + "...[TRUNCATED]";
|
|
243
|
+
} else {
|
|
244
|
+
sanitized[key] = this.sanitizeEventData(value);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return sanitized;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Message } from '../../clients/types';
|
|
2
|
+
|
|
3
|
+
// Basic trace event interface
|
|
4
|
+
export interface TraceEvent {
|
|
5
|
+
id: string;
|
|
6
|
+
type: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
data: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Trace metrics for performance monitoring
|
|
12
|
+
export interface TraceMetrics {
|
|
13
|
+
executionTimeMs: number;
|
|
14
|
+
toolCallCount: number;
|
|
15
|
+
llmCallCount: number;
|
|
16
|
+
tokenUsage: {
|
|
17
|
+
prompt: number;
|
|
18
|
+
completion: number;
|
|
19
|
+
total: number;
|
|
20
|
+
};
|
|
21
|
+
memoryUsage: {
|
|
22
|
+
heapUsed: number;
|
|
23
|
+
heapTotal: number;
|
|
24
|
+
};
|
|
25
|
+
costUsd: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Complete execution trace
|
|
29
|
+
export interface ExecutionTrace {
|
|
30
|
+
id: string;
|
|
31
|
+
startTime: number;
|
|
32
|
+
endTime: number;
|
|
33
|
+
events: TraceEvent[];
|
|
34
|
+
metrics: TraceMetrics;
|
|
35
|
+
success: boolean;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Resource quotas for script execution
|
|
40
|
+
export interface ResourceQuotas {
|
|
41
|
+
maxToolCalls: number;
|
|
42
|
+
maxTokens: number;
|
|
43
|
+
maxExecutionTimeMs: number;
|
|
44
|
+
maxCostUsd: number;
|
|
45
|
+
maxMemoryMb: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Security policy configuration
|
|
49
|
+
export interface SecurityPolicy {
|
|
50
|
+
allowlistedTools: string[];
|
|
51
|
+
denylistedTools: string[];
|
|
52
|
+
maxScriptLength: number;
|
|
53
|
+
allowNetworkAccess: boolean;
|
|
54
|
+
allowFileSystemAccess: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Current quota usage tracking
|
|
58
|
+
export interface QuotaUsage {
|
|
59
|
+
toolCalls: number;
|
|
60
|
+
tokens: number;
|
|
61
|
+
executionTimeMs: number;
|
|
62
|
+
costUsd: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Policy violation record
|
|
66
|
+
export interface PolicyViolation {
|
|
67
|
+
id: string;
|
|
68
|
+
type: 'quota_exceeded' | 'tool_denied' | 'tool_not_allowed' | 'script_validation';
|
|
69
|
+
message: string;
|
|
70
|
+
timestamp: number;
|
|
71
|
+
usage: QuotaUsage;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Script execution request
|
|
75
|
+
export interface ExecutionRequest {
|
|
76
|
+
script: string;
|
|
77
|
+
context?: Record<string, any>;
|
|
78
|
+
quotas?: Partial<ResourceQuotas>;
|
|
79
|
+
policy?: Partial<SecurityPolicy>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Script execution result
|
|
83
|
+
export interface ExecutionResult {
|
|
84
|
+
success: boolean;
|
|
85
|
+
error: string | null;
|
|
86
|
+
result: any;
|
|
87
|
+
trace: ExecutionTrace;
|
|
88
|
+
artifacts: Artifact[];
|
|
89
|
+
consoleOutput: string[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Artifact created by script
|
|
93
|
+
export interface Artifact {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
type: 'text' | 'json' | 'csv' | 'html' | 'markdown';
|
|
97
|
+
content: string;
|
|
98
|
+
createdAt: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Tool execution result
|
|
102
|
+
export interface ToolResult {
|
|
103
|
+
success: boolean;
|
|
104
|
+
result?: any;
|
|
105
|
+
error?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Tool call record
|
|
109
|
+
export interface ToolCall {
|
|
110
|
+
id: string;
|
|
111
|
+
name: string;
|
|
112
|
+
args: Record<string, any>;
|
|
113
|
+
result?: any;
|
|
114
|
+
error?: string;
|
|
115
|
+
timestamp: number;
|
|
116
|
+
duration?: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// LLM call record
|
|
120
|
+
export interface LLMCall {
|
|
121
|
+
id: string;
|
|
122
|
+
model: string;
|
|
123
|
+
messages: Message[];
|
|
124
|
+
response?: any;
|
|
125
|
+
error?: string;
|
|
126
|
+
timestamp: number;
|
|
127
|
+
duration?: number;
|
|
128
|
+
tokenUsage?: {
|
|
129
|
+
prompt: number;
|
|
130
|
+
completion: number;
|
|
131
|
+
total: number;
|
|
132
|
+
};
|
|
133
|
+
cost?: number;
|
|
134
|
+
}
|
|
@@ -141,62 +141,11 @@ class BaseAgent {
|
|
|
141
141
|
return this.summaries;
|
|
142
142
|
}
|
|
143
143
|
async processToolMessages(toolCall) {
|
|
144
|
-
const
|
|
145
|
-
const functionToCall = this.tools.getFunction(functionName);
|
|
146
|
-
console.log(toolCall);
|
|
147
|
-
const functionArgs = JSON.parse(this.formatAiResponse(toolCall.function.arguments));
|
|
148
|
-
const toJsonIfObject = (arg) => {
|
|
149
|
-
if (typeof arg === "object") {
|
|
150
|
-
return JSON.stringify(arg, null, 2);
|
|
151
|
-
}
|
|
152
|
-
return arg;
|
|
153
|
-
};
|
|
154
|
-
const toolDefinition = this.tools.getTool(functionName);
|
|
155
|
-
const properties = toolDefinition?.function?.parameters?.properties || {};
|
|
156
|
-
const isPositional = toolDefinition?.function?.parameters?.positional || false;
|
|
157
|
-
const fnArgs = isPositional
|
|
158
|
-
? Object.keys(properties).map((p) => functionArgs[p])
|
|
159
|
-
: functionArgs;
|
|
160
|
-
console.log(`Calling function ${functionName} with args:`, JSON.stringify(fnArgs, null, 2));
|
|
161
|
-
if (!functionToCall) {
|
|
162
|
-
const options = this.getEnabledToolNames().join(", ");
|
|
163
|
-
const error = `Function ${functionName} not found, options are ${options}`;
|
|
164
|
-
console.log(error);
|
|
165
|
-
return [
|
|
166
|
-
{
|
|
167
|
-
tool_call_id: toolCall.id,
|
|
168
|
-
role: "tool",
|
|
169
|
-
name: "error",
|
|
170
|
-
content: error,
|
|
171
|
-
},
|
|
172
|
-
];
|
|
173
|
-
}
|
|
174
|
-
const functionResponse = await Promise.resolve(isPositional ? functionToCall(...fnArgs) : functionToCall(fnArgs)).catch((e) => "ERROR: " + e.message);
|
|
144
|
+
const { functionResp, toolMessages } = await this.tools.callTool(toolCall, this.getEnabledToolNames());
|
|
175
145
|
this.agentEvents.emit(this.eventTypes.toolUsed, {
|
|
176
146
|
toolCall,
|
|
177
|
-
|
|
147
|
+
functionResp,
|
|
178
148
|
});
|
|
179
|
-
let toolMessages = [];
|
|
180
|
-
if (functionName === "multi_tool_use.parallel") {
|
|
181
|
-
const args = fnArgs[0];
|
|
182
|
-
toolMessages = args.map((call, index) => {
|
|
183
|
-
return {
|
|
184
|
-
tool_call_id: toolCall.id + "_" + index,
|
|
185
|
-
role: "tool",
|
|
186
|
-
name: call.recipient_name.split(".").pop(),
|
|
187
|
-
content: toJsonIfObject(functionResponse[index]) || "Done",
|
|
188
|
-
};
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
toolMessages = [
|
|
192
|
-
{
|
|
193
|
-
tool_call_id: toolCall.id,
|
|
194
|
-
role: "tool",
|
|
195
|
-
name: functionName,
|
|
196
|
-
content: toJsonIfObject(functionResponse) || "Done",
|
|
197
|
-
},
|
|
198
|
-
];
|
|
199
|
-
console.log(toolMessages);
|
|
200
149
|
return toolMessages;
|
|
201
150
|
}
|
|
202
151
|
logMessages(messages) {
|