Download packages/agent-core-v2/test/agent/toolSelect/toolSelect.e2e.test.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 8.74 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/agent-core-v2/test/agent/toolSelect/toolSelect.e2e.test.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/agent-core-v2/test/agent/toolSelect/toolSelect.e2e.test.ts
-
curl -L -o toolSelect.e2e.test.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/agent-core-v2/test/agent/toolSelect/toolSelect.e2e.test.ts
8.74 kB
| import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | |
| import { IAgentContextMemoryService } from '#/agent/contextMemory/contextMemory'; | |
| import { IAgentConversationUndoService } from '#/agent/undo/undo'; | |
| import type { ContextMessage } from '#/agent/contextMemory/types'; | |
| import type { ExecutableTool, ToolExecution } from '#/tool/toolContract'; | |
| import { IAgentToolExecutorService } from '#/agent/toolExecutor/toolExecutor'; | |
| import { IAgentToolRegistryService } from '#/agent/toolRegistry/toolRegistry'; | |
| import { TOOL_SELECT_FLAG_ENV } from '#/agent/toolSelect/flag'; | |
| import { IAgentToolSelectService } from '#/agent/toolSelect/toolSelect'; | |
| import { IAgentToolSelectAnnouncementsService } from '#/agent/toolSelect/toolSelectAnnouncements'; | |
| import { IAgentToolSelectSchemasService } from '#/agent/toolSelect/toolSelectSchemas'; | |
| import { IAgentUserToolService } from '#/agent/userTool/userTool'; | |
| import '#/agent/tools/select-tools/selectToolsTool'; | |
| import { createTestAgent, type TestAgentContext } from '../../harness'; | |
| const MCP_ALPHA = 'mcp__srv__alpha'; | |
| const DASHBOARD_TOOL = 'dashboard_create'; | |
| const DISCLOSURE_CAPABILITIES = { | |
| image_in: false, | |
| video_in: false, | |
| audio_in: false, | |
| thinking: false, | |
| tool_use: true, | |
| max_context_tokens: 128_000, | |
| dynamically_loaded_tools: true, | |
| } as const; | |
| type WireEvent = Extract< | |
| TestAgentContext['allEvents'][number], | |
| { readonly type: '[wire]' } | |
| >; | |
| class StubMcpTool implements ExecutableTool<Record<string, unknown>> { | |
| readonly description: string; | |
| readonly parameters: Record<string, unknown> = { | |
| type: 'object', | |
| properties: { query: { type: 'string' } }, | |
| additionalProperties: false, | |
| }; | |
| calls = 0; | |
| constructor(readonly name: string) { | |
| this.description = `${name} desc`; | |
| } | |
| resolveExecution(): ToolExecution { | |
| return { | |
| description: `stub ${this.name}`, | |
| approvalRule: this.name, | |
| execute: async () => { | |
| this.calls += 1; | |
| return { output: 'mcp ok' }; | |
| }, | |
| }; | |
| } | |
| } | |
| function wireEvents(ctx: TestAgentContext, eventName: string): readonly WireEvent[] { | |
| return ctx.allEvents.filter( | |
| (event): event is WireEvent => event.type === '[wire]' && event.event === eventName, | |
| ); | |
| } | |
| function selectToolsCall(id: string, names: readonly string[]) { | |
| return { | |
| type: 'function' as const, | |
| id, | |
| name: 'select_tools', | |
| arguments: JSON.stringify({ names }), | |
| }; | |
| } | |
| function toolNames(tools: readonly { readonly name: string }[]): string[] { | |
| return tools.map((tool) => tool.name); | |
| } | |
| function historyText(history: readonly ContextMessage[]): string { | |
| return history | |
| .flatMap((message) => message.content) | |
| .map((part) => (part.type === 'text' ? part.text : '')) | |
| .join('\n'); | |
| } | |
| describe('progressive tool disclosure end-to-end', () => { | |
| let ctx: TestAgentContext; | |
| let alpha: StubMcpTool; | |
| let registration: { dispose(): void } | undefined; | |
| beforeEach(async () => { | |
| vi.stubEnv(TOOL_SELECT_FLAG_ENV, '1'); | |
| ctx = createTestAgent(); | |
| ctx.get(IAgentToolSelectService); | |
| ctx.get(IAgentToolSelectAnnouncementsService); | |
| ctx.get(IAgentToolSelectSchemasService); | |
| ctx.get(IAgentToolExecutorService); | |
| ctx.configure({ modelCapabilities: DISCLOSURE_CAPABILITIES }); | |
| await ctx.restorePersisted(); | |
| await ctx.rpc.setPermission({ mode: 'yolo' }); | |
| alpha = new StubMcpTool(MCP_ALPHA); | |
| registration = ctx | |
| .get(IAgentToolRegistryService) | |
| .register(alpha, { source: 'mcp', disclosure: 'deferred' }); | |
| }); | |
| afterEach(async () => { | |
| registration?.dispose(); | |
| vi.unstubAllEnvs(); | |
| await ctx.dispose(); | |
| }); | |
| it('announces the manifest, loads by name, keeps the top-level table byte-stable, and dispatches on the next step', async () => { | |
| ctx.mockNextResponse(selectToolsCall('call_select_1', [MCP_ALPHA])); | |
| ctx.mockNextResponse({ | |
| type: 'function', | |
| id: 'call_alpha_1', | |
| name: MCP_ALPHA, | |
| arguments: JSON.stringify({ query: 'moon' }), | |
| }); | |
| ctx.mockNextResponse({ type: 'text', text: 'done' }); | |
| await ctx.rpc.prompt({ input: [{ type: 'text', text: 'try the srv alpha tool' }] }); | |
| await ctx.untilTurnEnd(); | |
| expect(ctx.llmCalls).toHaveLength(3); | |
| const firstWire = ctx.llmCalls[0]!; | |
| expect(toolNames(firstWire.tools)).not.toContain(MCP_ALPHA); | |
| expect(toolNames(firstWire.tools)).toContain('select_tools'); | |
| const announcementText = firstWire.history | |
| .map((message) => | |
| message.content.map((part) => (part.type === 'text' ? part.text : '')).join(''), | |
| ) | |
| .join('\n'); | |
| expect(announcementText).toContain('<tools_added>'); | |
| expect(announcementText).toContain(MCP_ALPHA); | |
| const requests = wireEvents(ctx, 'llm.request').filter( | |
| (event) => (event.args as { kind?: string }).kind === 'loop', | |
| ); | |
| expect(requests.length).toBeGreaterThan(0); | |
| for (const request of requests) { | |
| expect((request.args as { toolSelect?: boolean }).toolSelect).toBe(true); | |
| } | |
| const secondWire = ctx.llmCalls[1]!; | |
| const schemaMessages = secondWire.history.filter( | |
| (message) => message.tools?.some((tool) => tool.name === MCP_ALPHA), | |
| ); | |
| expect(schemaMessages).toHaveLength(1); | |
| const alphaFromSchema = schemaMessages[0]!.tools!.find((tool) => tool.name === MCP_ALPHA)!; | |
| expect(alphaFromSchema.parameters).toEqual(alpha.parameters); | |
| expect(secondWire.tools).toEqual(firstWire.tools); | |
| expect(wireEvents(ctx, 'llm.tools_snapshot')).toHaveLength(1); | |
| expect(alpha.calls).toBe(1); | |
| }); | |
| it('loads and dispatches a user tool registered through the domain service', async () => { | |
| ctx.get(IAgentUserToolService).register({ | |
| name: DASHBOARD_TOOL, | |
| description: 'Create a dashboard.', | |
| parameters: { | |
| type: 'object', | |
| properties: { title: { type: 'string' } }, | |
| required: ['title'], | |
| additionalProperties: false, | |
| }, | |
| disclosure: 'deferred', | |
| }); | |
| ctx.mockNextResponse(selectToolsCall('call_select_1', [DASHBOARD_TOOL])); | |
| ctx.mockNextResponse({ | |
| type: 'function', | |
| id: 'call_dashboard_1', | |
| name: DASHBOARD_TOOL, | |
| arguments: JSON.stringify({ title: 'Operations' }), | |
| }); | |
| ctx.mockNextResponse({ type: 'text', text: 'done' }); | |
| await ctx.rpc.prompt({ input: [{ type: 'text', text: 'create a dashboard' }] }); | |
| await ctx.untilToolCall({ output: 'dashboard-created' }); | |
| await ctx.untilTurnEnd(); | |
| const firstWire = ctx.llmCalls[0]!; | |
| expect(toolNames(firstWire.tools)).not.toContain(DASHBOARD_TOOL); | |
| expect(historyText(firstWire.history)).toContain(DASHBOARD_TOOL); | |
| const secondWire = ctx.llmCalls[1]!; | |
| const injected = secondWire.history.find((message) => | |
| message.tools?.some((tool) => tool.name === DASHBOARD_TOOL), | |
| ); | |
| expect(injected?.tools?.find((tool) => tool.name === DASHBOARD_TOOL)?.parameters).toEqual({ | |
| type: 'object', | |
| properties: { title: { type: 'string' } }, | |
| required: ['title'], | |
| additionalProperties: false, | |
| }); | |
| expect(secondWire.tools).toEqual(firstWire.tools); | |
| expect(historyText(ctx.get(IAgentContextMemoryService).get())).toContain( | |
| `Loaded: ${DASHBOARD_TOOL}`, | |
| ); | |
| expect(historyText(ctx.get(IAgentContextMemoryService).get())).toContain( | |
| 'dashboard-created', | |
| ); | |
| }); | |
| it('keeps the selected schema across undo and reports it as already available on reselect', async () => { | |
| ctx.get(IAgentContextMemoryService).append({ | |
| role: 'user', | |
| content: [{ type: 'text', text: 'earlier question' }], | |
| toolCalls: [], | |
| origin: { kind: 'user' }, | |
| }); | |
| ctx.mockNextResponse(selectToolsCall('call_select_1', [MCP_ALPHA])); | |
| ctx.mockNextResponse({ type: 'text', text: 'alpha is loaded' }); | |
| await ctx.rpc.prompt({ input: [{ type: 'text', text: 'load alpha' }] }); | |
| await ctx.untilTurnEnd(); | |
| await ctx.get(IAgentConversationUndoService).undo(1); | |
| const afterUndo = ctx.get(IAgentContextMemoryService).get(); | |
| expect(afterUndo.some((message) => message.tools?.some((tool) => tool.name === MCP_ALPHA))).toBe( | |
| true, | |
| ); | |
| ctx.mockNextResponse(selectToolsCall('call_select_2', [MCP_ALPHA])); | |
| ctx.mockNextResponse({ type: 'text', text: 'reloaded' }); | |
| await ctx.rpc.prompt({ input: [{ type: 'text', text: 'load alpha again' }] }); | |
| await ctx.untilTurnEnd(); | |
| const afterReload = ctx.get(IAgentContextMemoryService).get(); | |
| expect( | |
| afterReload.some((message) => message.tools?.some((tool) => tool.name === MCP_ALPHA)), | |
| ).toBe(true); | |
| expect(historyText(afterReload)).toContain('Already available: mcp__srv__alpha'); | |
| expect(historyText(afterReload)).not.toContain('Loaded: mcp__srv__alpha'); | |
| }); | |
| }); | |