@game_ryo/lsji 0.1.0 → 0.3.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/package.json +15 -7
- package/src/cli.js +395 -62
- package/src/execution/budget/circuit-breaker.js +245 -0
- package/src/execution/budget/cost-tracker.js +387 -0
- package/src/execution/budget/index.js +63 -0
- package/src/execution/budget/token-counter.js +159 -0
- package/src/execution/engine.js +428 -0
- package/src/execution/hitl/approval-gate.js +210 -0
- package/src/execution/hitl/index.js +12 -0
- package/src/execution/hitl/notifier.js +151 -0
- package/src/execution/hitl/store.js +311 -0
- package/src/execution/idempotency.js +312 -0
- package/src/execution/index.js +14 -0
- package/src/index.js +80 -4
- package/src/llm/index.js +21 -0
- package/src/llm/llm-agent.js +357 -0
- package/src/llm/memory/conversation.js +271 -0
- package/src/llm/memory/episodic.js +312 -0
- package/src/llm/memory/index.js +12 -0
- package/src/llm/memory/semantic.js +324 -0
- package/src/llm/plugins/index.js +202 -0
- package/src/llm/prompt-manager.js +332 -0
- package/src/llm/providers/anthropic.js +250 -0
- package/src/llm/providers/base.js +116 -0
- package/src/llm/providers/local.js +163 -0
- package/src/llm/providers/openai.js +212 -0
- package/src/llm/tools/registry.js +342 -0
- package/src/server/index.js +416 -0
- package/src/server/ui/index.html +16 -0
- package/src/server/ui/package.json +19 -0
- package/src/server/ui/src/main.jsx +10 -0
- package/src/server/ui/src/styles.css +260 -0
- package/src/server/ui/vite.config.js +27 -0
- package/docs/README.md +0 -43
- package/docs/blog/2019-05-28-first-blog-post.mdx +0 -12
- package/docs/blog/2019-05-29-long-blog-post.mdx +0 -44
- package/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -24
- package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs/blog/2021-08-26-welcome/index.mdx +0 -29
- package/docs/blog/authors.yml +0 -25
- package/docs/blog/tags.yml +0 -19
- package/docs/docs/api/agent.md +0 -151
- package/docs/docs/api/env.md +0 -133
- package/docs/docs/api/environments.md +0 -102
- package/docs/docs/api/qlearning.md +0 -138
- package/docs/docs/api/storage.md +0 -168
- package/docs/docs/architecture.md +0 -155
- package/docs/docs/cli.md +0 -210
- package/docs/docs/contributing.md +0 -162
- package/docs/docs/core-concepts.md +0 -152
- package/docs/docs/examples/advanced-training.md +0 -244
- package/docs/docs/examples/custom-environment.md +0 -198
- package/docs/docs/examples/custom-storage.md +0 -251
- package/docs/docs/getting-started.md +0 -91
- package/docs/docusaurus.config.ts +0 -149
- package/docs/package-lock.json +0 -19522
- package/docs/package.json +0 -49
- package/docs/sidebars.ts +0 -33
- package/docs/src/components/HomepageFeatures/index.tsx +0 -71
- package/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- package/docs/src/css/custom.css +0 -79
- package/docs/src/pages/index.module.css +0 -23
- package/docs/src/pages/index.tsx +0 -44
- package/docs/src/pages/markdown-page.mdx +0 -7
- package/docs/static/.nojekyll +0 -0
- package/docs/static/img/docusaurus-social-card.jpg +0 -0
- package/docs/static/img/docusaurus.png +0 -0
- package/docs/static/img/favicon.ico +0 -0
- package/docs/static/img/logo.png +0 -0
- package/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- package/docs/static/img/undraw_docusaurus_react.svg +0 -170
- package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- package/docs/tsconfig.json +0 -12
- package/legacy/worker.js +0 -166
- package/legacy/wrangler.toml +0 -11
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Provider Implementation
|
|
3
|
+
*
|
|
4
|
+
* Supports OpenAI API (GPT-4, GPT-3.5, etc.)
|
|
5
|
+
* Compatible with OpenAI-compatible endpoints (e.g., Azure, local proxies)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { LLMProvider } from './base.js';
|
|
9
|
+
|
|
10
|
+
// OpenAI pricing (USD per 1M tokens) - as of 2026
|
|
11
|
+
const PRICING = {
|
|
12
|
+
'gpt-4o': { input: 5.00, output: 15.00 },
|
|
13
|
+
'gpt-4o-mini': { input: 0.15, output: 0.60 },
|
|
14
|
+
'gpt-4-turbo': { input: 10.00, output: 30.00 },
|
|
15
|
+
'gpt-4': { input: 30.00, output: 60.00 },
|
|
16
|
+
'gpt-3.5-turbo': { input: 0.50, output: 1.50 },
|
|
17
|
+
'o1-preview': { input: 15.00, output: 60.00 },
|
|
18
|
+
'o1-mini': { input: 3.00, output: 12.00 },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class OpenAIProvider extends LLMProvider {
|
|
22
|
+
constructor(config = {}) {
|
|
23
|
+
super({
|
|
24
|
+
model: config.model || 'gpt-4o-mini',
|
|
25
|
+
apiKey: config.apiKey || process.env.OPENAI_API_KEY,
|
|
26
|
+
baseUrl: config.baseUrl || 'https://api.openai.com/v1',
|
|
27
|
+
defaultParams: config.defaultParams || {},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.pricing = PRICING[this.model] || PRICING['gpt-4o-mini'];
|
|
31
|
+
this.client = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lazy-load OpenAI client
|
|
36
|
+
*/
|
|
37
|
+
async getClient() {
|
|
38
|
+
if (!this.client) {
|
|
39
|
+
const { default: OpenAI } = await import('openai');
|
|
40
|
+
this.client = new OpenAI({
|
|
41
|
+
apiKey: this.apiKey,
|
|
42
|
+
baseURL: this.baseUrl,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return this.client;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate completion from OpenAI
|
|
50
|
+
*/
|
|
51
|
+
async generate(messages, options = {}) {
|
|
52
|
+
const client = await this.getClient();
|
|
53
|
+
|
|
54
|
+
const params = {
|
|
55
|
+
model: this.model,
|
|
56
|
+
messages: this.formatMessages(messages),
|
|
57
|
+
temperature: options.temperature ?? this.defaultParams.temperature ?? 0.7,
|
|
58
|
+
max_tokens: options.maxTokens ?? this.defaultParams.maxTokens ?? 4096,
|
|
59
|
+
...this.defaultParams,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (options.tools && options.tools.length > 0) {
|
|
63
|
+
params.tools = this.formatTools(options.tools);
|
|
64
|
+
params.tool_choice = options.toolChoice || 'auto';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const response = await client.chat.completions.create(params);
|
|
68
|
+
|
|
69
|
+
return this.parseResponse(response);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate streaming completion
|
|
74
|
+
*/
|
|
75
|
+
async *generateStream(messages, options = {}) {
|
|
76
|
+
const client = await this.getClient();
|
|
77
|
+
|
|
78
|
+
const params = {
|
|
79
|
+
model: this.model,
|
|
80
|
+
messages: this.formatMessages(messages),
|
|
81
|
+
temperature: options.temperature ?? this.defaultParams.temperature ?? 0.7,
|
|
82
|
+
max_tokens: options.maxTokens ?? this.defaultParams.maxTokens ?? 4096,
|
|
83
|
+
stream: true,
|
|
84
|
+
...this.defaultParams,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (options.tools && options.tools.length > 0) {
|
|
88
|
+
params.tools = this.formatTools(options.tools);
|
|
89
|
+
params.tool_choice = options.toolChoice || 'auto';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const stream = await client.chat.completions.create(params);
|
|
93
|
+
|
|
94
|
+
for await (const chunk of stream) {
|
|
95
|
+
yield this.parseStreamChunk(chunk);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Format messages for OpenAI API
|
|
101
|
+
*/
|
|
102
|
+
formatMessages(messages) {
|
|
103
|
+
return messages.map(msg => ({
|
|
104
|
+
role: msg.role,
|
|
105
|
+
content: msg.content,
|
|
106
|
+
name: msg.name,
|
|
107
|
+
tool_call_id: msg.tool_call_id,
|
|
108
|
+
tool_calls: msg.tool_calls,
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format tools for OpenAI API
|
|
114
|
+
*/
|
|
115
|
+
formatTools(tools) {
|
|
116
|
+
return tools.map(tool => ({
|
|
117
|
+
type: 'function',
|
|
118
|
+
function: {
|
|
119
|
+
name: tool.name,
|
|
120
|
+
description: tool.description,
|
|
121
|
+
parameters: tool.parameters,
|
|
122
|
+
},
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Parse OpenAI response
|
|
128
|
+
*/
|
|
129
|
+
parseResponse(response) {
|
|
130
|
+
const choice = response.choices[0];
|
|
131
|
+
const message = choice.message;
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
content: message.content || '',
|
|
135
|
+
toolCalls: message.tool_calls || [],
|
|
136
|
+
usage: {
|
|
137
|
+
inputTokens: response.usage?.prompt_tokens || 0,
|
|
138
|
+
outputTokens: response.usage?.completion_tokens || 0,
|
|
139
|
+
totalTokens: response.usage?.total_tokens || 0,
|
|
140
|
+
},
|
|
141
|
+
finishReason: choice.finish_reason,
|
|
142
|
+
model: response.model,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Parse streaming chunk
|
|
148
|
+
*/
|
|
149
|
+
parseStreamChunk(chunk) {
|
|
150
|
+
const choice = chunk.choices[0];
|
|
151
|
+
const delta = choice.delta;
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
content: delta.content || '',
|
|
155
|
+
toolCalls: delta.tool_calls || [],
|
|
156
|
+
usage: chunk.usage ? {
|
|
157
|
+
inputTokens: chunk.usage.prompt_tokens || 0,
|
|
158
|
+
outputTokens: chunk.usage.completion_tokens || 0,
|
|
159
|
+
totalTokens: chunk.usage.total_tokens || 0,
|
|
160
|
+
} : null,
|
|
161
|
+
finishReason: choice.finish_reason,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Calculate cost for usage
|
|
167
|
+
*/
|
|
168
|
+
calculateCost(usage) {
|
|
169
|
+
const inputCost = (usage.inputTokens / 1_000_000) * this.pricing.input;
|
|
170
|
+
const outputCost = (usage.outputTokens / 1_000_000) * this.pricing.output;
|
|
171
|
+
return inputCost + outputCost;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get pricing info for current model
|
|
176
|
+
*/
|
|
177
|
+
getPricing() {
|
|
178
|
+
return { ...this.pricing };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Check if provider supports function calling
|
|
183
|
+
*/
|
|
184
|
+
supportsTools() {
|
|
185
|
+
// Most OpenAI models support function calling
|
|
186
|
+
return !this.model.includes('o1-mini') && !this.model.includes('o1-preview');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Check if provider supports streaming
|
|
191
|
+
*/
|
|
192
|
+
supportsStreaming() {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Validate configuration
|
|
198
|
+
*/
|
|
199
|
+
async validate() {
|
|
200
|
+
if (!this.apiKey) {
|
|
201
|
+
return { valid: false, error: 'OpenAI API key is required' };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const client = await this.getClient();
|
|
206
|
+
await client.models.list();
|
|
207
|
+
return { valid: true };
|
|
208
|
+
} catch (e) {
|
|
209
|
+
return { valid: false, error: e.message };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages tool/function definitions for LLM agents.
|
|
5
|
+
* Provides registration, lookup, and execution with idempotency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { IdempotencyStore } from '../../execution/idempotency.js';
|
|
9
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tool definition
|
|
13
|
+
* @typedef {Object} ToolDefinition
|
|
14
|
+
* @property {string} name - Unique tool name
|
|
15
|
+
* @property {string} description - Human-readable description
|
|
16
|
+
* @property {Object} parameters - JSON Schema for parameters
|
|
17
|
+
* @property {Function} execute - Async function(params, context) -> result
|
|
18
|
+
* @property {boolean} [requiresApproval] - Whether HITL approval needed
|
|
19
|
+
* @property {boolean} [idempotent] - Whether to use idempotency
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Tool Registry - Manages available tools
|
|
24
|
+
*/
|
|
25
|
+
export class ToolRegistry {
|
|
26
|
+
constructor({ idempotencyStore, approvalGate } = {}) {
|
|
27
|
+
this.tools = new Map();
|
|
28
|
+
this.idempotencyStore = idempotencyStore;
|
|
29
|
+
this.approvalGate = approvalGate;
|
|
30
|
+
this.categories = new Map();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Register a tool
|
|
35
|
+
*/
|
|
36
|
+
register(definition) {
|
|
37
|
+
if (!definition.name) {
|
|
38
|
+
throw new Error('Tool must have a name');
|
|
39
|
+
}
|
|
40
|
+
if (!definition.execute || typeof definition.execute !== 'function') {
|
|
41
|
+
throw new Error('Tool must have an execute function');
|
|
42
|
+
}
|
|
43
|
+
if (!definition.parameters) {
|
|
44
|
+
throw new Error('Tool must have parameters schema');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.tools.set(definition.name, definition);
|
|
48
|
+
|
|
49
|
+
// Categorize
|
|
50
|
+
const category = definition.category || 'general';
|
|
51
|
+
if (!this.categories.has(category)) {
|
|
52
|
+
this.categories.set(category, []);
|
|
53
|
+
}
|
|
54
|
+
this.categories.get(category).push(definition.name);
|
|
55
|
+
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Register multiple tools
|
|
61
|
+
*/
|
|
62
|
+
registerMany(definitions) {
|
|
63
|
+
for (const def of definitions) {
|
|
64
|
+
this.register(def);
|
|
65
|
+
}
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get a tool by name
|
|
71
|
+
*/
|
|
72
|
+
get(name) {
|
|
73
|
+
return this.tools.get(name);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if tool exists
|
|
78
|
+
*/
|
|
79
|
+
has(name) {
|
|
80
|
+
return this.tools.has(name);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get all tools
|
|
85
|
+
*/
|
|
86
|
+
getAll() {
|
|
87
|
+
return Array.from(this.tools.values());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get tools by category
|
|
92
|
+
*/
|
|
93
|
+
getByCategory(category) {
|
|
94
|
+
const names = this.categories.get(category) || [];
|
|
95
|
+
return names.map(n => this.tools.get(n)).filter(Boolean);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get tool definitions for LLM (OpenAI/Anthropic format)
|
|
100
|
+
*/
|
|
101
|
+
getDefinitions(category = null) {
|
|
102
|
+
const tools = category ? this.getByCategory(category) : this.getAll();
|
|
103
|
+
return tools.map(tool => ({
|
|
104
|
+
type: 'function',
|
|
105
|
+
function: {
|
|
106
|
+
name: tool.name,
|
|
107
|
+
description: tool.description,
|
|
108
|
+
parameters: tool.parameters,
|
|
109
|
+
},
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Execute a tool with idempotency and approval
|
|
115
|
+
*/
|
|
116
|
+
async execute(name, params, context = {}) {
|
|
117
|
+
const tool = this.tools.get(name);
|
|
118
|
+
if (!tool) {
|
|
119
|
+
throw new Error(`Tool not found: ${name}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check approval requirement
|
|
123
|
+
if (tool.requiresApproval && this.approvalGate) {
|
|
124
|
+
const approval = await this.approvalGate.requestApproval({
|
|
125
|
+
action: name,
|
|
126
|
+
context: { params, ...context },
|
|
127
|
+
requester: context.requester || 'agent',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (approval.status !== 'approved') {
|
|
131
|
+
throw new Error(`Tool ${name} was ${approval.status}: ${approval.reason}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Execute with idempotency if enabled
|
|
136
|
+
if (tool.idempotent && this.idempotencyStore) {
|
|
137
|
+
const key = `${name}_${uuidv4()}`;
|
|
138
|
+
return this.idempotencyStore.execute(key, name, params, () => tool.execute(params, context));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return tool.execute(params, context);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Execute multiple tools in parallel
|
|
146
|
+
*/
|
|
147
|
+
async executeAll(calls, context = {}) {
|
|
148
|
+
return Promise.all(calls.map(({ name, params }) => this.execute(name, params, context)));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create tool registry with built-in tools
|
|
154
|
+
*/
|
|
155
|
+
export function createToolRegistry(config = {}) {
|
|
156
|
+
const registry = new ToolRegistry(config);
|
|
157
|
+
|
|
158
|
+
// Register built-in tools
|
|
159
|
+
registry.registerMany(getBuiltinTools(config));
|
|
160
|
+
|
|
161
|
+
return registry;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get built-in tool definitions
|
|
166
|
+
*/
|
|
167
|
+
function getBuiltinTools(config = {}) {
|
|
168
|
+
const tools = [];
|
|
169
|
+
|
|
170
|
+
// Web search tool
|
|
171
|
+
tools.push({
|
|
172
|
+
name: 'web_search',
|
|
173
|
+
description: 'Search the web for current information',
|
|
174
|
+
category: 'research',
|
|
175
|
+
parameters: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
properties: {
|
|
178
|
+
query: { type: 'string', description: 'Search query' },
|
|
179
|
+
maxResults: { type: 'number', description: 'Maximum results', default: 5 },
|
|
180
|
+
},
|
|
181
|
+
required: ['query'],
|
|
182
|
+
},
|
|
183
|
+
requiresApproval: false,
|
|
184
|
+
idempotent: true,
|
|
185
|
+
async execute({ query, maxResults = 5 }, context) {
|
|
186
|
+
// This would integrate with actual search API
|
|
187
|
+
return { query, results: [], note: 'Web search not configured' };
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// File read tool
|
|
192
|
+
tools.push({
|
|
193
|
+
name: 'file_read',
|
|
194
|
+
description: 'Read a file from the filesystem',
|
|
195
|
+
category: 'filesystem',
|
|
196
|
+
parameters: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
path: { type: 'string', description: 'File path' },
|
|
200
|
+
encoding: { type: 'string', description: 'Encoding', default: 'utf-8' },
|
|
201
|
+
},
|
|
202
|
+
required: ['path'],
|
|
203
|
+
},
|
|
204
|
+
requiresApproval: false,
|
|
205
|
+
idempotent: true,
|
|
206
|
+
async execute({ path, encoding = 'utf-8' }, context) {
|
|
207
|
+
const fs = await import('fs/promises');
|
|
208
|
+
return fs.readFile(path, encoding);
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// File write tool
|
|
213
|
+
tools.push({
|
|
214
|
+
name: 'file_write',
|
|
215
|
+
description: 'Write a file to the filesystem',
|
|
216
|
+
category: 'filesystem',
|
|
217
|
+
parameters: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
path: { type: 'string', description: 'File path' },
|
|
221
|
+
content: { type: 'string', description: 'File content' },
|
|
222
|
+
encoding: { type: 'string', description: 'Encoding', default: 'utf-8' },
|
|
223
|
+
},
|
|
224
|
+
required: ['path', 'content'],
|
|
225
|
+
},
|
|
226
|
+
requiresApproval: true, // Requires HITL approval
|
|
227
|
+
idempotent: true,
|
|
228
|
+
async execute({ path, content, encoding = 'utf-8' }, context) {
|
|
229
|
+
const fs = await import('fs/promises');
|
|
230
|
+
await fs.writeFile(path, content, encoding);
|
|
231
|
+
return { path, bytesWritten: Buffer.byteLength(content, encoding) };
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Code execution tool
|
|
236
|
+
tools.push({
|
|
237
|
+
name: 'code_exec',
|
|
238
|
+
description: 'Execute code in a sandboxed environment',
|
|
239
|
+
category: 'code',
|
|
240
|
+
parameters: {
|
|
241
|
+
type: 'object',
|
|
242
|
+
properties: {
|
|
243
|
+
code: { type: 'string', description: 'Code to execute' },
|
|
244
|
+
language: { type: 'string', description: 'Language', default: 'javascript' },
|
|
245
|
+
timeout: { type: 'number', description: 'Timeout in ms', default: 30000 },
|
|
246
|
+
},
|
|
247
|
+
required: ['code'],
|
|
248
|
+
},
|
|
249
|
+
requiresApproval: true, // Requires HITL approval
|
|
250
|
+
idempotent: false,
|
|
251
|
+
async execute({ code, language = 'javascript', timeout = 30000 }, context) {
|
|
252
|
+
// This would integrate with a sandbox like Pyodide, QuickJS, etc.
|
|
253
|
+
return { output: 'Code execution not configured', language };
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// API call tool
|
|
258
|
+
tools.push({
|
|
259
|
+
name: 'api_call',
|
|
260
|
+
description: 'Make an HTTP API call',
|
|
261
|
+
category: 'network',
|
|
262
|
+
parameters: {
|
|
263
|
+
type: 'object',
|
|
264
|
+
properties: {
|
|
265
|
+
url: { type: 'string', description: 'API URL' },
|
|
266
|
+
method: { type: 'string', description: 'HTTP method', default: 'GET' },
|
|
267
|
+
headers: { type: 'object', description: 'Headers' },
|
|
268
|
+
body: { type: 'object', description: 'Request body' },
|
|
269
|
+
timeout: { type: 'number', description: 'Timeout in ms', default: 30000 },
|
|
270
|
+
},
|
|
271
|
+
required: ['url'],
|
|
272
|
+
},
|
|
273
|
+
requiresApproval: true, // Requires HITL approval
|
|
274
|
+
idempotent: true,
|
|
275
|
+
async execute({ url, method = 'GET', headers = {}, body, timeout = 30000 }, context) {
|
|
276
|
+
const controller = new AbortController();
|
|
277
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const response = await fetch(url, {
|
|
281
|
+
method,
|
|
282
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
283
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
284
|
+
signal: controller.signal,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
clearTimeout(timeoutId);
|
|
288
|
+
const data = await response.json();
|
|
289
|
+
return { status: response.status, data };
|
|
290
|
+
} catch (error) {
|
|
291
|
+
clearTimeout(timeoutId);
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Email send tool
|
|
298
|
+
tools.push({
|
|
299
|
+
name: 'send_email',
|
|
300
|
+
description: 'Send an email',
|
|
301
|
+
category: 'communication',
|
|
302
|
+
parameters: {
|
|
303
|
+
type: 'object',
|
|
304
|
+
properties: {
|
|
305
|
+
to: { type: 'string', description: 'Recipient email' },
|
|
306
|
+
subject: { type: 'string', description: 'Email subject' },
|
|
307
|
+
body: { type: 'string', description: 'Email body' },
|
|
308
|
+
html: { type: 'string', description: 'HTML body' },
|
|
309
|
+
},
|
|
310
|
+
required: ['to', 'subject', 'body'],
|
|
311
|
+
},
|
|
312
|
+
requiresApproval: true, // Requires HITL approval
|
|
313
|
+
idempotent: true,
|
|
314
|
+
async execute({ to, subject, body, html }, context) {
|
|
315
|
+
// This would integrate with actual email service
|
|
316
|
+
return { to, subject, sent: false, note: 'Email service not configured' };
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Database query tool
|
|
321
|
+
tools.push({
|
|
322
|
+
name: 'db_query',
|
|
323
|
+
description: 'Execute a database query',
|
|
324
|
+
category: 'data',
|
|
325
|
+
parameters: {
|
|
326
|
+
type: 'object',
|
|
327
|
+
properties: {
|
|
328
|
+
query: { type: 'string', description: 'SQL query' },
|
|
329
|
+
params: { type: 'array', description: 'Query parameters' },
|
|
330
|
+
},
|
|
331
|
+
required: ['query'],
|
|
332
|
+
},
|
|
333
|
+
requiresApproval: true, // Requires HITL approval
|
|
334
|
+
idempotent: false,
|
|
335
|
+
async execute({ query, params = [] }, context) {
|
|
336
|
+
// This would use the existing storage
|
|
337
|
+
return { query, results: [], note: 'DB query not configured' };
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return tools;
|
|
342
|
+
}
|