refactor(events): remove the agent/steering mirror emit

agent/steering was the last transient mirror of a durable session event:
drainSteering appended the durable steering/message {turn, content, source}
and emitted the identical fact one line later. Zero production listeners
existed — every steering consumer (hook bridges, goldens, deriveMessages)
reads the durable event — and the one regression test subscriber asserted
a fact the log already records.

Remove the declaration (dsh-agent types + JSDoc list + README row), the
emit in drainSteering (its ctx parameter goes too), and the emit line in
the loop-pseudocode blocks (loop.ts module doc, architecture.md); the
cordis catalog is regenerated. The regression test now pins source
preservation on the durable steering/message event. Live-notification
needs keep their surviving homes: agent/queued at enqueue time,
session/event at drain time.

RFC: docs/rfc/implemented/simplification/2026-07-04-remove-agent-steering-mirror.md
(moved from proposed/, amended to shipped reality). The three implemented
RFCs that stated the retention — the boundary-mirror removal, the
stream-chunk removal, and event-domain-semantics — are amended to point
at that RFC as the record of the removal, per implemented/AGENTS.md.
The rejected retire-mid-turn-steering RFC keeps its frozen text (it
records the declined proposal); the steering capability itself —
steer(), the durable event, continuation forcing — is untouched.
This commit is contained in:
Tianyi Cui
2026-07-04 15:36:40 +08:00
parent 226a8b5e4c
commit c6d2eeea6b
12 changed files with 53 additions and 68 deletions

View File

@@ -166,7 +166,7 @@ export interface LoopHandle {
* → dispatch → tools/post-execute
* session('tool/result')
* append buffered post-execute additionalContext → session('context/message')(s)
* drain steering → session('steering/message'); emit agent/steering
* drain steering → session('steering/message')
* session('step/end') ⟵ durable step boundary (no agent/* mirror)
* cont = waterfall agent/turn-continuation ⟵ ContinuationDecision; default
* {action: hadToolCalls||steered ? 'continue':'stop'}; a continue.reason is
@@ -420,7 +420,7 @@ async function runTurn(ctx: Context, agent: ReactLoopAgent, handle: LoopHandle,
// Steering from the previous round's continuation listeners joins before
// the request.
drainSteering(ctx, agent, turn)
drainSteering(agent, turn)
// The step's AbortController exists BEFORE any async pre-step work so a
// dispose() or cancel() — in a synchronous turn-start listener or an
@@ -529,7 +529,7 @@ async function runTurn(ctx: Context, agent: ReactLoopAgent, handle: LoopHandle,
if (stepReason) reason = stepReason
// Steering that arrived during streaming/tool execution.
const steered = drainSteering(ctx, agent, turn)
const steered = drainSteering(agent, turn)
if (closeStep()) break
@@ -635,11 +635,10 @@ async function runTurn(ctx: Context, agent: ReactLoopAgent, handle: LoopHandle,
}
/** Drain the steering queue into the session. Returns whether any arrived. */
function drainSteering(ctx: Context, agent: ReactLoopAgent, turn: number): boolean {
function drainSteering(agent: ReactLoopAgent, turn: number): boolean {
const messages = agent.inbox.drainSteering()
for (const message of messages) {
agent.session.append('steering/message', { turn, content: message.content, source: message.source }, { surfaceOp: 'append' })
ctx.emit('agent/steering', agent, turn, message.content, message.source)
}
return messages.length > 0
}