import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { rgPath } from '@vscode/ripgrep' import { expect, it } from 'vitest' import { FileOutputBacking, outputFixtureRoot } from '../../../tests/support/paged-output-backing' import { ModelToolProvider, type ModelToolResult } from './model-tool-provider' it('pages native rg output through the production provider and releases it', async () => { await mkdir(outputFixtureRoot, { recursive: true }) const root = await mkdtemp(join(tmpdir(), 'goodbuddy-rg-provider-')) const backingStore = new FileOutputBacking() const provider = new ModelToolProvider(root, [], undefined, undefined, false, { ripgrepExecutablePath: rgPath, outputStore: { backingStore } }) const context = { conversationId: 'rg-pages', runtimeTarget: 'model' as const, } const signal = new AbortController().signal const parse = (result: ModelToolResult) => { expect(result.contextBytes).toBeLessThanOrEqual(256 * 1024) const part = result.parts[0]! if (part.type !== 'text') throw new Error('Expected text') return JSON.parse(part.text) } try { const text = 'target 你好\\"'.repeat(30_000) + 'tail\n' await writeFile(join(root, 'large.txt'), text) expect(await provider.listTools(context, signal)).toEqual(expect.arrayContaining([ expect.objectContaining({ name: 'workspace_rg', inputSchema: expect.objectContaining({ required: ['args'] }) }), expect.objectContaining({ name: 'output_read' }) ])) const result = parse(await provider.callTool('workspace_rg', { args: ['--no-line-number', 'target', 'large.txt'] }, signal, context)) let output = result.stdout const reference = result.stdoutReference let cursor = reference.nextCursor while (cursor < reference.totalBytes) { const page = parse(await provider.callTool('output_read', { handle: reference.handle, cursor }, signal, context)) output += page.content expect(page.nextCursor).toBeGreaterThan(cursor) cursor = page.nextCursor } expect(output).toBe(text) const error = parse(await provider.callTool('workspace_rg', { args: ['[', '.'] }, signal, context)) expect(error).toMatchObject({ exitCode: 2, stderr: expect.stringContaining('regex parse error') }) const preprocessed = parse(await provider.callTool('workspace_rg', { args: ['--pre=goodbuddy-nonexistent-preprocessor', 'target', 'large.txt'] }, signal, context)) expect(preprocessed.exitCode).toBe(2) expect(preprocessed.stderr).toContain('goodbuddy-nonexistent-preprocessor') await provider.releaseConversation(context.conversationId) await expect(provider.callTool('output_read', { handle: reference.handle }, signal, context)).rejects.toThrow('不存在') } finally { await provider.dispose() await backingStore.dispose() await rm(root, { recursive: true, force: true }) } })