fix(tools): isolate tool schemas and waterfall failures
This commit is contained in:
@@ -173,7 +173,10 @@ export class ToolRegistry extends Service {
|
||||
schemas(): ToolSchema[] {
|
||||
// Rest-destructure to drop `execute`; the unused binding is the idiom.
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unused-vars
|
||||
return [...this.store.values()].map(({ execute, ...schema }) => schema)
|
||||
return [...this.store.values()].map(({ execute, ...schema }) => ({
|
||||
...schema,
|
||||
parameters: structuredClone(schema.parameters),
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,6 +204,14 @@ export class ToolRegistry extends Service {
|
||||
...info ? { error: info } : {},
|
||||
}
|
||||
}
|
||||
}).catch((error: unknown): ToolExecutionResult => {
|
||||
const info = errorInfo(error)
|
||||
return {
|
||||
callId: exec.callId,
|
||||
content: [{ type: 'text', text: `Error: ${errorMessage(error)}` }],
|
||||
isError: true,
|
||||
...info ? { error: info } : {},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,38 @@ describe('ToolRegistry', () => {
|
||||
expect(order).toEqual(['first:before', 'second:before', 'second:after', 'first:after'])
|
||||
})
|
||||
|
||||
it('returns an isError result when a tools/execute listener throws', async () => {
|
||||
const ctx = await setup()
|
||||
ctx.tools.register(echoTool)
|
||||
ctx.on('tools/execute', async () => {
|
||||
throw new Error('permission hook broke')
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
|
||||
expect(result).toEqual({
|
||||
callId: CallId('c1'),
|
||||
content: [{ type: 'text', text: 'Error: permission hook broke' }],
|
||||
isError: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('schemas() snapshots tool schemas instead of exposing registry objects', async () => {
|
||||
const ctx = await setup()
|
||||
ctx.tools.register(echoTool)
|
||||
|
||||
const first = ctx.tools.schemas()
|
||||
const firstParameters = first[0]!.parameters as { properties: Record<string, unknown> }
|
||||
firstParameters.properties['mutated'] = { type: 'string' }
|
||||
first[0]!.description = 'mutated'
|
||||
|
||||
expect(ctx.tools.schemas()).toEqual([{
|
||||
name: 'echo',
|
||||
description: 'echo arguments back',
|
||||
parameters: { type: 'object', properties: { text: { type: 'string' } } },
|
||||
}])
|
||||
})
|
||||
|
||||
it('rejects duplicate names and unregisters on fiber dispose (HMR safety)', async () => {
|
||||
const ctx = await setup()
|
||||
ctx.tools.register(echoTool)
|
||||
|
||||
Reference in New Issue
Block a user