feat: add customTools support to AgentConfig for orchestrator-level tool injection
Users can now pass custom ToolDefinition objects via AgentConfig.customTools, which are registered alongside built-in tools in all orchestrator paths (runAgent, runTeam, runTasks). Custom tools bypass allowlist/preset filtering but can still be blocked by disallowedTools. Ref #108
This commit is contained in:
parent
017e0f42f6
commit
38a88df144
|
|
@ -212,6 +212,11 @@ function resolveTokenBudget(primary?: number, fallback?: number): number | undef
|
|||
function buildAgent(config: AgentConfig): Agent {
|
||||
const registry = new ToolRegistry()
|
||||
registerBuiltInTools(registry)
|
||||
if (config.customTools) {
|
||||
for (const tool of config.customTools) {
|
||||
registry.register(tool, { runtimeAdded: true })
|
||||
}
|
||||
}
|
||||
const executor = new ToolExecutor(registry)
|
||||
return new Agent(config, registry, executor)
|
||||
}
|
||||
|
|
|
|||
10
src/types.ts
10
src/types.ts
|
|
@ -223,6 +223,16 @@ export interface AgentConfig {
|
|||
/** API key override; falls back to the provider's standard env var. */
|
||||
readonly apiKey?: string
|
||||
readonly systemPrompt?: string
|
||||
/**
|
||||
* Custom tool definitions to register alongside built-in tools.
|
||||
* Created via `defineTool()`. Custom tools bypass `tools` (allowlist)
|
||||
* and `toolPreset` filtering, but can still be blocked by `disallowedTools`.
|
||||
*
|
||||
* Tool names must not collide with built-in tool names; a duplicate name
|
||||
* will throw at registration time.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
readonly customTools?: readonly ToolDefinition<any>[]
|
||||
/** Names of tools (from the tool registry) available to this agent. */
|
||||
readonly tools?: readonly string[]
|
||||
/** Names of tools explicitly disallowed for this agent. */
|
||||
|
|
|
|||
|
|
@ -155,6 +155,56 @@ describe('OpenMultiAgent', () => {
|
|||
expect(oma.getStatus().completedTasks).toBe(1)
|
||||
})
|
||||
|
||||
it('registers customTools so they are available to the LLM', async () => {
|
||||
mockAdapterResponses = ['used custom tool']
|
||||
|
||||
const { z } = await import('zod')
|
||||
const { defineTool } = await import('../src/tool/framework.js')
|
||||
|
||||
const myTool = defineTool({
|
||||
name: 'my_custom_tool',
|
||||
description: 'A custom tool for testing',
|
||||
inputSchema: z.object({ query: z.string() }),
|
||||
execute: async ({ query }) => ({ data: query }),
|
||||
})
|
||||
|
||||
const oma = new OpenMultiAgent({ defaultModel: 'mock-model' })
|
||||
await oma.runAgent(
|
||||
{ ...agentConfig('solo'), customTools: [myTool] },
|
||||
'Use the custom tool',
|
||||
)
|
||||
|
||||
const toolNames = capturedChatOptions[0]?.tools?.map(t => t.name) ?? []
|
||||
expect(toolNames).toContain('my_custom_tool')
|
||||
})
|
||||
|
||||
it('customTools bypass tools allowlist and toolPreset filtering', async () => {
|
||||
mockAdapterResponses = ['done']
|
||||
|
||||
const { z } = await import('zod')
|
||||
const { defineTool } = await import('../src/tool/framework.js')
|
||||
|
||||
const myTool = defineTool({
|
||||
name: 'my_custom_tool',
|
||||
description: 'A custom tool for testing',
|
||||
inputSchema: z.object({ query: z.string() }),
|
||||
execute: async ({ query }) => ({ data: query }),
|
||||
})
|
||||
|
||||
const oma = new OpenMultiAgent({ defaultModel: 'mock-model' })
|
||||
|
||||
// toolPreset 'readonly' only allows file_read, grep, glob — custom tool should still appear
|
||||
await oma.runAgent(
|
||||
{ ...agentConfig('solo'), customTools: [myTool], toolPreset: 'readonly' },
|
||||
'test',
|
||||
)
|
||||
|
||||
const toolNames = capturedChatOptions[0]?.tools?.map(t => t.name) ?? []
|
||||
expect(toolNames).toContain('my_custom_tool')
|
||||
// built-in tools outside the preset should be filtered
|
||||
expect(toolNames).not.toContain('bash')
|
||||
})
|
||||
|
||||
it('fires onProgress events', async () => {
|
||||
mockAdapterResponses = ['done']
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue