refactor(agent-loop): simplify message machine
This commit is contained in:
@@ -30,7 +30,7 @@ const result = (overrides: Record<string, unknown> = {}) => ({
|
||||
})
|
||||
|
||||
function startTurn(session: Session, turn = 1): void {
|
||||
session.append('turn/start', { turn, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
session.append('turn/start', { turn })
|
||||
}
|
||||
|
||||
describe('hook-protocol invariants', () => {
|
||||
@@ -49,7 +49,7 @@ describe('hook-protocol invariants', () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
const session = ctx.sessions.create()
|
||||
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
session.append('turn/start', { turn: 1 })
|
||||
session.append('hook/invoked', invoked())
|
||||
await ctx.plugin(InvariantService)
|
||||
await ctx.plugin(HookInvariant)
|
||||
@@ -63,7 +63,7 @@ describe('hook-protocol invariants', () => {
|
||||
expect(() => {
|
||||
ctx.emit('session/event', session, {
|
||||
type: 'turn/start', seq: 0, time: 0,
|
||||
data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } },
|
||||
data: { turn: 1 },
|
||||
})
|
||||
ctx.emit('session/event', session, {
|
||||
type: 'hook/invoked', seq: 1, time: 1, data: invoked(),
|
||||
|
||||
@@ -197,6 +197,11 @@ 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 context 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.
|
||||
@@ -213,8 +218,9 @@ export function apply(ctx: Context, config: Config): void {
|
||||
|
||||
// --- UserPromptSubmit → PromptDecision. The prompt text is the payload; no
|
||||
// matcher subject (CC ignores matchers for this event). ---
|
||||
ctx.on('agent/prompt-submit', async (agent, message, signal, next): Promise<PromptDecision> => {
|
||||
const merged = await runPoint('UserPromptSubmit', '', promptPayload(ctx, agent, message.content), { agent, signal })
|
||||
ctx.on('agent/prompt-submit', async (agent, messages, signal, next): Promise<PromptDecision> => {
|
||||
const content = messages.flatMap(message => message.content)
|
||||
const merged = await runPoint('UserPromptSubmit', '', promptPayload(ctx, agent, content), { agent, signal })
|
||||
if (merged.decision === 'deny') {
|
||||
return { kind: 'block', reason: merged.reason ?? 'blocked by UserPromptSubmit hook' }
|
||||
}
|
||||
@@ -225,8 +231,7 @@ export function apply(ctx: Context, config: Config): void {
|
||||
if (!ours || downstream.kind !== 'allow') return downstream
|
||||
return {
|
||||
kind: 'allow',
|
||||
...downstream.content !== undefined ? { content: downstream.content } : {},
|
||||
additionalContexts: prependContext(ours, downstream.additionalContexts),
|
||||
messages: appendPromptContext(downstream.messages, ours),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -182,6 +182,11 @@ 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.
|
||||
@@ -196,11 +201,11 @@ export function apply(ctx: Context, config: Config): void {
|
||||
})
|
||||
|
||||
// UserPromptSubmit → PromptDecision. Codex supports block, not allow or ask.
|
||||
ctx.on('agent/prompt-submit', async (agent, message, signal, next): Promise<PromptDecision> => {
|
||||
ctx.on('agent/prompt-submit', async (agent, messages, signal, next): Promise<PromptDecision> => {
|
||||
const payload = {
|
||||
...base(ctx, agent, 'UserPromptSubmit', model),
|
||||
turn_id: String(lastTurn(agent) + 1),
|
||||
prompt: blocksToText(message.content),
|
||||
prompt: blocksToText(messages.flatMap(message => message.content)),
|
||||
}
|
||||
const merged = await runPoint('UserPromptSubmit', '', payload, { agent, plainStdoutAsContext: true, signal })
|
||||
/* jscpd:ignore-start */
|
||||
@@ -212,8 +217,7 @@ export function apply(ctx: Context, config: Config): void {
|
||||
if (!ours || downstream.kind !== 'allow') return downstream
|
||||
return {
|
||||
kind: 'allow',
|
||||
...downstream.content !== undefined ? { content: downstream.content } : {},
|
||||
additionalContexts: prependContext(ours, downstream.additionalContexts),
|
||||
messages: appendPromptContext(downstream.messages, ours),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user