fix(agent-loop): scope blocked admission cleanup

This commit is contained in:
_Kerman
2026-07-31 14:35:58 +08:00
parent 12a48558f2
commit c2ff9ddec8
18 changed files with 162 additions and 51 deletions

View File

@@ -209,7 +209,13 @@ export function apply(ctx: Context, config: Config): void {
}
const merged = await runPoint('UserPromptSubmit', '', payload, { agent, plainStdoutAsContext: true, signal })
/* jscpd:ignore-start */
if (merged.decision === 'deny') return { kind: 'block', reason: merged.reason ?? 'blocked by UserPromptSubmit hook' }
if (merged.decision === 'deny') {
return {
kind: 'block',
reason: merged.reason ?? 'blocked by UserPromptSubmit hook',
discardClaimed: true,
}
}
// Context alone is not a veto: DELEGATE so a later prompt-submit listener can
// still block/rewrite, then fold our context onto its decision.
const downstream = await next()

View File

@@ -109,7 +109,11 @@ export function defineCoverageCases(groups: CoverageGroup | readonly CoverageGro
hooks(d, { UserPromptSubmit: [{ hooks: [{ type: 'command', command: sh(d, 'c.sh', '#!/usr/bin/env bash\necho \'{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"bridge ctx"}}\'\n') }] }] })
const adapter = new MockAdapter([textResponse('should not run')])
const ctx = await harness(join(d, 'hooks.json'), adapter)
ctx.on('agent/prompt-submit', async () => ({ kind: 'block' as const, reason: 'policy veto' }))
ctx.on('agent/prompt-submit', async () => ({
kind: 'block' as const,
reason: 'policy veto',
discardClaimed: true,
}))
const agent = ctx.agentLoop.create(SessionId('a1'), { provider: 'mock', model: 'mock' })
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } })); await waitForIdle(ctx, agent)
expect(adapter.requests).toHaveLength(0)