feat: add tool framework foundation (types, registry, executor, shell tool, model types, SOUL.md)

- Task 0: SOUL.md + loadSystemPrompt() in daemon
- Task 1: Tool type definitions (Tool, ToolCall, ToolResult, etc.)
- Task 2: ToolRegistry with Anthropic/OpenAI serialization
- Task 3: ToolExecutor with hooks, timeout, truncation
- Task 4: shell.exec builtin tool
- Task 8: Model types updated for tool use (ToolDefinition, ModelToolCall, etc.)
- Task 15: Model index exports for tool types
This commit is contained in:
William Valentin
2026-02-05 17:39:40 -08:00
parent 32dd3ad728
commit b00706325b
13 changed files with 691 additions and 7 deletions
+79
View File
@@ -0,0 +1,79 @@
import { describe, it, expect } from 'vitest';
import { ToolRegistry } from './registry.js';
import type { Tool } from './types.js';
const echoTool: Tool = {
name: 'test.echo',
description: 'Echoes input back',
inputSchema: {
type: 'object',
properties: { text: { type: 'string', description: 'Text to echo' } },
required: ['text'],
},
execute: async (args) => ({ success: true, output: String((args as { text: string }).text) }),
};
const greetTool: Tool = {
name: 'test.greet',
description: 'Greets someone',
inputSchema: {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name'],
},
execute: async (args) => ({ success: true, output: `Hello ${(args as { name: string }).name}` }),
};
describe('ToolRegistry', () => {
it('registers and retrieves tools by name', () => {
const registry = new ToolRegistry();
registry.register(echoTool);
expect(registry.get('test.echo')).toBe(echoTool);
expect(registry.get('nonexistent')).toBeUndefined();
});
it('lists all registered tools', () => {
const registry = new ToolRegistry();
registry.register(echoTool);
registry.register(greetTool);
const tools = registry.list();
expect(tools).toHaveLength(2);
expect(tools.map(t => t.name)).toContain('test.echo');
expect(tools.map(t => t.name)).toContain('test.greet');
});
it('throws on duplicate registration', () => {
const registry = new ToolRegistry();
registry.register(echoTool);
expect(() => registry.register(echoTool)).toThrow('already registered');
});
it('serializes to Anthropic format', () => {
const registry = new ToolRegistry();
registry.register(echoTool);
const anthropicTools = registry.toAnthropicFormat();
expect(anthropicTools).toEqual([{
name: 'test.echo',
description: 'Echoes input back',
input_schema: echoTool.inputSchema,
}]);
});
it('serializes to OpenAI format', () => {
const registry = new ToolRegistry();
registry.register(echoTool);
const openaiTools = registry.toOpenAIFormat();
expect(openaiTools).toEqual([{
type: 'function',
function: {
name: 'test.echo',
description: 'Echoes input back',
parameters: echoTool.inputSchema,
},
}]);
});
});