fix(tui): honor file reference boundaries

This commit is contained in:
Yichen Jiang
2026-07-23 19:21:02 +08:00
parent c37892f753
commit 01dcc7920c
4 changed files with 60 additions and 13 deletions

View File

@@ -105,6 +105,24 @@ describe('WorkspaceFileSearch', () => {
])
expect(await files.list('~/.dsh-file-autocomplete-missing/', signal)).toEqual([])
expect(await files.list('../', signal)).toEqual([])
expect(await files.list('README.md/', signal)).toEqual([])
})
it('does not traverse directory symlinks during direct completion', async () => {
const root = await workspace()
const outside = await mkdtemp(join(tmpdir(), 'dsh-file-autocomplete-outside-'))
roots.push(outside)
await writeFile(join(outside, 'outside-secret.txt'), 'secret')
await symlink(
outside,
join(root, 'escape'),
process.platform === 'win32' ? 'junction' : 'dir',
)
const files = search(root)
const signal = new AbortController().signal
expect(await files.list('escape/', signal)).toEqual([])
expect(await files.list('escape/outside', signal)).toEqual([])
})
it('ranks basename and subsequence fuzzy matches across the bounded workspace index', async () => {

View File

@@ -1153,22 +1153,34 @@ describe('pi-tui chat lifecycle and transcript', () => {
})
it('shows file-reference guidance only while read is visible to the agent', async () => {
const tools: Record<string, ToolDefinition> = {}
const result = await setup({ tools })
const read: ToolDefinition = {
name: 'read',
description: 'Read a file.',
parameters: {},
execute: () => Promise.resolve([]),
}
let visibility: 'none' | 'global' | 'agent' = 'none'
const result = await setup({
async configureContext(ctx) {
ctx.provide('tools', {
get(name: string, scope?: Agent) {
if (name !== 'read' || visibility === 'none') return undefined
return (scope === undefined) === (visibility === 'global') ? read : undefined
},
} as never)
},
})
const fileReferenceText = async (): Promise<string | undefined> => {
const assembly = await result.ctx.systemPrompt.assemble(assembleContextFor(result.agent))
return assembly.sections.find(section => section.name === 'ui:tui-file-reference')?.text
}
try {
expect(await fileReferenceText()).toBe('')
tools.read = {
name: 'read',
description: 'Read a file.',
parameters: {},
execute: () => Promise.resolve([]),
}
visibility = 'global'
expect(await fileReferenceText()).toBe('')
visibility = 'agent'
expect(await fileReferenceText()).toBe(FILE_REFERENCE_PROMPT)
delete tools.read
visibility = 'none'
expect(await fileReferenceText()).toBe('')
} finally {
await dispose(result)