test: cover project instruction configuration branches
This commit is contained in:
@@ -128,6 +128,16 @@ describe('dsh-agent-core bundle', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('supports direct apply with project instructions disabled and no forwarded agents', async () => {
|
||||||
|
const ctx = new Context()
|
||||||
|
agentCore.apply(ctx, { projectInstructions: false })
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
|
||||||
|
expect(ctx.get('agents')?.list()).toEqual([])
|
||||||
|
expect(ctx.get('systemPrompt')).toBeDefined()
|
||||||
|
await ctx.fiber.dispose()
|
||||||
|
})
|
||||||
|
|
||||||
it('re-exports the loop config schema as its own', () => {
|
it('re-exports the loop config schema as its own', () => {
|
||||||
expect(agentCore.Config).toBeDefined()
|
expect(agentCore.Config).toBeDefined()
|
||||||
expect(agentCore.name).toBe('agent-core')
|
expect(agentCore.name).toBe('agent-core')
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ function ancestorChain(root: string, cwd: string): string[] {
|
|||||||
while (current !== resolvedRoot) {
|
while (current !== resolvedRoot) {
|
||||||
chain.push(current)
|
chain.push(current)
|
||||||
const parent = dirname(current)
|
const parent = dirname(current)
|
||||||
|
/* v8 ignore next -- defensive guard for direct helper misuse; discovery always passes cwd or an ancestor root. */
|
||||||
if (parent === current) break
|
if (parent === current) break
|
||||||
current = parent
|
current = parent
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,6 +195,24 @@ describe('project instruction discovery', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('labels the default DSH home as ~/.dsh when HOME points at the configured default', async () => {
|
||||||
|
const root = await tempRepo()
|
||||||
|
const home = await tempRepo()
|
||||||
|
const previousHome = process.env.HOME
|
||||||
|
try {
|
||||||
|
process.env.HOME = home
|
||||||
|
await write(join(home, '.dsh/AGENTS.md'), 'global default rule')
|
||||||
|
|
||||||
|
const files = await discoverBaselineInstructionFiles({ cwd: root })
|
||||||
|
|
||||||
|
expect(files.map(file => file.displayPath)).toEqual(['~/.dsh/AGENTS.md'])
|
||||||
|
} finally {
|
||||||
|
process.env.HOME = previousHome
|
||||||
|
await rm(root, { recursive: true, force: true })
|
||||||
|
await rm(home, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
it('ignores instruction candidates that are directories', async () => {
|
it('ignores instruction candidates that are directories', async () => {
|
||||||
const root = await tempRepo()
|
const root = await tempRepo()
|
||||||
const home = await tempRepo()
|
const home = await tempRepo()
|
||||||
@@ -280,6 +298,38 @@ describe('project instruction rendering', () => {
|
|||||||
expect(rendered.truncated[0]!.includedBytes).toBeGreaterThan(0)
|
expect(rendered.truncated[0]!.includedBytes).toBeGreaterThan(0)
|
||||||
expect(Buffer.byteLength(rendered.text, 'utf8')).toBeLessThanOrEqual(700)
|
expect(Buffer.byteLength(rendered.text, 'utf8')).toBeLessThanOrEqual(700)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('omits all text when the render budget is disabled', () => {
|
||||||
|
const rendered = renderProjectInstructions([
|
||||||
|
{ absolutePath: '/repo/AGENTS.md', displayPath: 'AGENTS.md', content: 'root rules' },
|
||||||
|
], { maxBytes: 0 })
|
||||||
|
|
||||||
|
expect(rendered).toEqual({
|
||||||
|
text: '',
|
||||||
|
omitted: [{ absolutePath: '/repo/AGENTS.md', displayPath: 'AGENTS.md', content: 'root rules' }],
|
||||||
|
truncated: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to a compact truncation notice when even the empty heading cannot fit', () => {
|
||||||
|
const rendered = renderProjectInstructions([
|
||||||
|
{ absolutePath: '/repo/pkg/AGENTS.md', displayPath: 'pkg/AGENTS.md', content: 'x'.repeat(1000) },
|
||||||
|
], { maxBytes: 100 })
|
||||||
|
|
||||||
|
expect(rendered.text).toBe('<!-- Project instruction budget 100 bytes: truncated pkg/AGENTS.md from 1000 to 0 bytes -->')
|
||||||
|
expect(rendered.truncated).toEqual([{ displayPath: 'pkg/AGENTS.md', originalBytes: 1000, includedBytes: 0 }])
|
||||||
|
expect(Buffer.byteLength(rendered.text, 'utf8')).toBeLessThanOrEqual(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('truncates the compact notice itself when the render budget is smaller than the notice', () => {
|
||||||
|
const rendered = renderProjectInstructions([
|
||||||
|
{ absolutePath: '/repo/pkg/AGENTS.md', displayPath: 'pkg/AGENTS.md', content: 'x'.repeat(1000) },
|
||||||
|
], { maxBytes: 20 })
|
||||||
|
|
||||||
|
expect(rendered.text).toBe('<!-- Project instruc')
|
||||||
|
expect(rendered.truncated).toEqual([{ displayPath: 'pkg/AGENTS.md', originalBytes: 1000, includedBytes: 0 }])
|
||||||
|
expect(Buffer.byteLength(rendered.text, 'utf8')).toBe(20)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('project instruction request injection', () => {
|
describe('project instruction request injection', () => {
|
||||||
|
|||||||
@@ -46,6 +46,18 @@ describe('dsh-acp-agent composition', () => {
|
|||||||
await ctx.fiber.dispose()
|
await ctx.fiber.dispose()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('forwards explicit project-instruction controls to the bundled spine', async () => {
|
||||||
|
const ctx = await mount({
|
||||||
|
model: 'mock',
|
||||||
|
systemPrompt: 'hi',
|
||||||
|
persistenceRoot: '/tmp/dsh-acp-agent-project-instructions',
|
||||||
|
projectInstructions: false,
|
||||||
|
})
|
||||||
|
expect(ctx.get('agents')).toBeDefined()
|
||||||
|
expect(ctx.get('agentLoop')).toBeDefined()
|
||||||
|
await ctx.fiber.dispose()
|
||||||
|
})
|
||||||
|
|
||||||
it('exposes its plugin shape', () => {
|
it('exposes its plugin shape', () => {
|
||||||
expect(acpAgent.name).toBe('acp-agent')
|
expect(acpAgent.name).toBe('acp-agent')
|
||||||
expect(acpAgent.Config).toBeDefined()
|
expect(acpAgent.Config).toBeDefined()
|
||||||
|
|||||||
@@ -51,6 +51,17 @@ describe('dsh-stdio-agent app', () => {
|
|||||||
await ctx.fiber.dispose()
|
await ctx.fiber.dispose()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('forwards explicit project-instruction controls to the bundled spine', async () => {
|
||||||
|
const ctx = await mount({
|
||||||
|
model: 'mock',
|
||||||
|
systemPrompt: 'hi',
|
||||||
|
persistenceRoot: '/tmp/dsh-stdio-agent-spec-project-instructions',
|
||||||
|
projectInstructions: false,
|
||||||
|
})
|
||||||
|
expect(ctx.get('agents')?.get(AgentId('main'))).toBeDefined()
|
||||||
|
await ctx.fiber.dispose()
|
||||||
|
})
|
||||||
|
|
||||||
it('forwards resumeSessionId onto the pre-created agent when set', async () => {
|
it('forwards resumeSessionId onto the pre-created agent when set', async () => {
|
||||||
// A resume id defers agent creation until persistence loads; with no backing
|
// A resume id defers agent creation until persistence loads; with no backing
|
||||||
// session the resume is contained + logged, so no `main` agent registers —
|
// session the resume is contained + logged, so no `main` agent registers —
|
||||||
|
|||||||
Reference in New Issue
Block a user