refactor(agent): unify agent-scoped event signatures as payload objects

All agent/* and agent-loop/config-start-failed events take one payload
object carrying the agent subject; waterfall/serial payloads require a
signal and keep next as the final argument. PreStepContext and
RequestFailureContext are unfolded into payloads and retired.
goal/changed follows the same shape so agentEvents keeps its listener
error containment. ReactLoopAgent builds its scope carrier once in the
constructor. Regenerates scope resolvers, tool-cordis api catalog, and
docs catalogs; updates all affected listeners, tests, and the
core-data-structures docs (en + zh).
This commit is contained in:
_Kerman
2026-08-06 12:13:14 +08:00
parent bb53e25ed0
commit ccebba2349
91 changed files with 574 additions and 618 deletions

View File

@@ -185,7 +185,7 @@ export function apply(ctx: Context, config: Config): void {
// 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.
ctx.on('agent/session-start', (agent, source) => {
ctx.on('agent/session-start', ({ agent, source }) => {
detached.track(runPoint('SessionStart', source, { ...base(ctx, agent, 'SessionStart', model), source }, { agent, plainStdoutAsContext: true, signal: detached.signal })
.then((merged) => {
const context = contextFrom(merged)
@@ -196,7 +196,7 @@ export function apply(ctx: Context, config: Config): void {
})
// UserPromptSubmit → PreStepDecision. Codex supports reject, not rewrite or ask.
ctx.on('agent/pre-step', async (agent, messages, { turn, signal }, next): Promise<PreStepDecision> => {
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),
@@ -257,7 +257,7 @@ export function apply(ctx: Context, config: Config): void {
// TODO(stop-loop-guard): Codex supplies `stop_hook_active` so a Stop hook can
// avoid continuing the same turn indefinitely. It is always false here, so an
// unconditionally blocking hook force-continues every step until it self-limits.
ctx.on('agent/turn-stopping', async (agent, turn, signal): Promise<void> => {
ctx.on('agent/turn-stopping', async ({ agent, turn, signal }): Promise<void> => {
const merged = await runPoint('Stop', '', { ...turnBase(ctx, agent, 'Stop', model), stop_hook_active: false, last_assistant_message: null }, { agent, turn, signal })
/* jscpd:ignore-end */
if (merged.decision === 'deny') {

View File

@@ -128,7 +128,7 @@ 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":"from-bridge"}}\'\n') }] }] })
const adapter = new MockAdapter([textResponse('ok')])
const ctx = await harness(join(d, 'hooks.json'), adapter)
ctx.on('agent/pre-step', async (_agent, messages) => ({
ctx.on('agent/pre-step', async ({ messages }) => ({
kind: 'enter' as const,
messages: [{
...messages[0]!,