mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(core): enforce agent-scoped ownership boundaries
This commit is contained in:
@@ -119,37 +119,30 @@ describe('bash tool through the agent loop', () => {
|
||||
it('background: start → poll → completion notice lands as context/message', async () => {
|
||||
const adapter = new MockAdapter([
|
||||
toolCallResponse('call-1', 'bash', { command: 'echo bg-ok', description: 'test command', run_in_background: true }),
|
||||
toolCallResponse('call-2', 'bash_output', {}, undefined),
|
||||
// Each harness owns a fresh BashLocal service, whose first task id is
|
||||
// deterministically bash-1. Keep the scripted call faithful to what the
|
||||
// model sent; tool arguments are immutable once execution policy begins.
|
||||
toolCallResponse('call-2', 'bash_output', { task_id: 'bash-1' }, undefined),
|
||||
textResponse('Background task finished.'),
|
||||
])
|
||||
// The second tool call needs the REAL task id from the first result;
|
||||
// a tools/pre-execute listener rewrites the scripted arguments. (This uses
|
||||
// the low-level capability to mutate `exec` before dispatch — the
|
||||
// unadvertised mechanism behind a future first-class input-rewrite decision;
|
||||
// here it is a test shim to thread the generated id, not a product feature.)
|
||||
let taskId = ''
|
||||
|
||||
const ctx = await harness(adapter)
|
||||
const agent = ctx.agentLoop.create(AgentId('it-bg'), { model: 'mock' })
|
||||
|
||||
// Intercept the first tool result to capture the generated task id, then
|
||||
// rewrite the second scripted call's arguments to use it.
|
||||
// Capture the generated id so the deterministic fixture is checked against
|
||||
// the real executor instead of silently assuming it.
|
||||
ctx.on('session/event', (_session, event) => {
|
||||
if (event.type === 'tool/result' && taskId === '') {
|
||||
const match = /task (bash-\d+)/.exec(resultText(event))
|
||||
if (match) taskId = match[1]!
|
||||
}
|
||||
})
|
||||
ctx.on('tools/pre-execute', async (exec, next) => {
|
||||
if (exec.name === 'bash_output') {
|
||||
exec.arguments = { task_id: taskId }
|
||||
}
|
||||
return next()
|
||||
})
|
||||
|
||||
agent.send([{ type: 'text', text: 'run echo bg-ok in the background' }])
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
expect(taskId).toBe('bash-1')
|
||||
|
||||
// Wait for the background task itself (completion may race turn end).
|
||||
const task = ctx.bash.get(BashTaskId(taskId))
|
||||
if (!task) throw new Error(`task ${taskId} not registered`)
|
||||
|
||||
@@ -242,7 +242,6 @@ describe('bash tool', () => {
|
||||
[{ command: ' ', description: 'd' }, /invalid command/],
|
||||
[{ command: 'x', description: ' ' }, /invalid description/],
|
||||
[{ command: 'x', description: 'd', timeoutMs: -1 }, /invalid timeoutMs/],
|
||||
[{ command: 'x', description: 'd', timeoutMs: Number.NaN }, /invalid timeoutMs/],
|
||||
])('rejects value-invalid args %j', async (args, pattern) => {
|
||||
const ctx = await setup()
|
||||
const result = await call(ctx, 'bash', args)
|
||||
@@ -250,6 +249,15 @@ describe('bash tool', () => {
|
||||
expect(text(result)).toMatch(pattern)
|
||||
})
|
||||
|
||||
it('rejects a non-JSON numeric argument before tool-specific validation', async () => {
|
||||
const ctx = await setup()
|
||||
const result = await call(ctx, 'bash', {
|
||||
command: 'x', description: 'd', timeoutMs: Number.NaN,
|
||||
})
|
||||
expect(result.isError).toBe(true)
|
||||
expect(text(result)).toContain('tool execution arguments must be losslessly JSON-serializable')
|
||||
})
|
||||
|
||||
it('registers all three schemas in the system prompt assembly', async () => {
|
||||
const ctx = await setup()
|
||||
const names = ctx.tools.schemas().map(schema => schema.name)
|
||||
|
||||
Reference in New Issue
Block a user