refactor agent pre-step inbox lifecycle

This commit is contained in:
_Kerman
2026-07-31 19:21:16 +08:00
parent c2ff9ddec8
commit fcc2b5e282
267 changed files with 2052 additions and 1546 deletions
+11 -19
View File
@@ -15,7 +15,7 @@
import { readFileSync } from 'node:fs'
import type { Context } from 'cordis'
import z from 'schemastery'
import type { Agent, PromptDecision } from '@deepseek-ai/dsh-agent'
import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, MessageSource } from '@deepseek-ai/dsh-llm'
import type { UserMessage } from '@deepseek-ai/dsh-session'
@@ -182,11 +182,6 @@ export function apply(ctx: Context, config: Config): void {
return [ours, ...theirs ?? []]
}
/** Append hook context to an admitted inbox batch. */
function appendPromptContext(theirs: UserMessage[], ours: UserMessage): UserMessage[] {
return [...theirs, ours]
}
// SessionStart injects plain stdout when its detached hook resolves; a slow
// hook may miss the first request.
// TODO(session-start-gating): add a startup gate before promising first-turn delivery.
@@ -200,30 +195,27 @@ export function apply(ctx: Context, config: Config): void {
/* jscpd:ignore-end */
})
// UserPromptSubmit → PromptDecision. Codex supports block, not allow or ask.
ctx.on('agent/prompt-submit', async (agent, messages, signal, next): Promise<PromptDecision> => {
// UserPromptSubmit → PreStepDecision. Codex supports reject, not rewrite or ask.
ctx.on('agent/pre-step', async (agent, messages, { turn, signal }, next): Promise<PreStepDecision> => {
if (messages.length === 0) return next()
const payload = {
...base(ctx, agent, 'UserPromptSubmit', model),
turn_id: String(lastTurn(agent) + 1),
turn_id: String(turn),
prompt: blocksToText(messages.flatMap(message => message.content)),
}
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',
discardClaimed: true,
}
return { kind: 'reject' }
}
// Context alone is not a veto: DELEGATE so a later prompt-submit listener can
// still block/rewrite, then fold our context onto its decision.
// Context alone is not a veto: DELEGATE so a later pre-step listener can
// still reject/rewrite, then fold our context onto its decision.
const downstream = await next()
const ours = contextFrom(merged)
if (!ours || downstream.kind !== 'allow') return downstream
if (!ours || downstream.kind !== 'enter') return downstream
return {
kind: 'allow',
messages: appendPromptContext(downstream.messages, ours),
kind: 'enter',
messages: [...downstream.messages, ours],
}
})