Merge remote-tracking branch 'origin/master' into simpl-a3-knobs
# Conflicts: # docs/rfc/README.md
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ describe('MEDIUM: misc registry and config fixes', () => {
|
||||
expect(agent.session.deriveMessages().at(-1)?.content).toEqual([{ type: 'text', text: 'routed' }])
|
||||
})
|
||||
|
||||
it('agent/queued carries the resolved source; agent/steering carries its source', async () => {
|
||||
it('agent/queued carries the resolved source; steering/message records its source', async () => {
|
||||
const adapter = new MockAdapter([toolCallResponse('c1', 'noop', {}), textResponse('done')])
|
||||
const ctx = await harness(adapter)
|
||||
const agent = ctx.agentLoop.create(AgentId('a1'), { model: 'mock' })
|
||||
@@ -425,15 +425,16 @@ describe('MEDIUM: misc registry and config fixes', () => {
|
||||
}))
|
||||
|
||||
const queuedSources: { source: MessageSource; steering: boolean }[] = []
|
||||
const steeringSources: MessageSource[] = []
|
||||
ctx.on('agent/queued', (_agent, _content, info) => void queuedSources.push(info))
|
||||
ctx.on('agent/steering', (_agent, _turn, _content, source) => void steeringSources.push(source))
|
||||
|
||||
send(agent, 'go') // no explicit source → default {kind:'user'} must be visible
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
expect(queuedSources[0]).toEqual({ source: { kind: 'user' }, steering: false })
|
||||
expect(queuedSources[1]).toEqual({ source: { kind: 'plugin', plugin: 'goal' }, steering: true })
|
||||
// The drain appends the durable steering/message with the caller's source
|
||||
// intact — the log, not a transient emit, is where consumers read it.
|
||||
const steeringSources = agent.session.events.flatMap(e => e.type === 'steering/message' ? [e.data.source] : [])
|
||||
expect(steeringSources).toEqual([{ kind: 'plugin', plugin: 'goal' }])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -50,9 +50,8 @@ Turn and step boundaries are NOT mirrored as `agent/*` emits: a consumer that ne
|
||||
|
||||
Tool interception is the `tools/pre-execute` / `tools/post-execute` pair in [`dsh-tools`](../tools/README.md) (`PreToolDecision` allow/deny/ask, `PostToolDecision` accept/block) — same typed-Decision idiom, owned there because it is the tool registry's seam.
|
||||
|
||||
#### Live control notifications (emit)
|
||||
#### Error notifications (emit)
|
||||
|
||||
- `agent/steering` — steering content injected mid-turn
|
||||
- `agent/error` — step/turn error
|
||||
|
||||
The model's token stream is NOT an `agent/*` event: read it off the durable `session/event` feed as `assistant/chunk` (the same feed persistence and the ACP bridge use).
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* `agent/request`/`agent/step-result`/`agent/turn-continuation` waterfalls and
|
||||
* the serial `agent/pre-step`) that mutate/veto, and TRANSIENT emits
|
||||
* (`agent/status`, `agent/error`, `agent/created`/
|
||||
* `agent/disposed`, `agent/queued`, `agent/steering`, `agent/session-start`)
|
||||
* `agent/disposed`, `agent/queued`, `agent/session-start`)
|
||||
* that notify with the `Agent` in hand. Turn/step boundaries are NOT here —
|
||||
* they are durable `session/event` records. Answers "right now, with the agent
|
||||
* object — intercept or observe."
|
||||
@@ -367,16 +367,7 @@ declare module 'cordis' {
|
||||
*/
|
||||
'agent/turn-continuation'(agent: Agent, turn: number, defaultDecision: ContinuationDecision, next: () => Promise<ContinuationDecision>): Promise<ContinuationDecision>
|
||||
|
||||
// ---- streaming + tool notifications (emit) ----
|
||||
/**
|
||||
* Steering content was injected into a running turn.
|
||||
* @param agent - the agent that absorbed the steering.
|
||||
* @param turn - the running turn that received it.
|
||||
* @param content - the injected blocks.
|
||||
* @param source - the steering message's resolved source.
|
||||
* @mode emit
|
||||
*/
|
||||
'agent/steering'(agent: Agent, turn: number, content: ContentBlock[], source: MessageSource): void
|
||||
// ---- error notifications (emit) ----
|
||||
/**
|
||||
* A step or turn errored. The loop reports a failure here (plus the logger)
|
||||
* even when the error has no in-turn position for a session `error` event.
|
||||
|
||||
Reference in New Issue
Block a user