Fix architecture-review findings in the loop and service packages
High (loop pipeline): agent/step-result now runs before the
assistant/message append so the session log records what tool dispatch
actually uses; abort is honored between tool calls, not just
mid-stream; steering drains at step start, pending steering overrides
a negative turn-continuation decision (/goal pattern), and leftover
steering is re-enqueued as queued messages so it is never stranded;
exceptions from turn-continuation listeners and session/flush are
contained to the turn (error event + agent/error) instead of killing
the driver loop.
Medium: disposal emits agent/status('disposed') and mid-turn disposal
records reason 'disposed'; duplicate LLM adapter registration throws
(all-or-nothing); SessionEvent is a real discriminated union (casts
removed); model-less agents fail with a clear actionable error unless
agent/request supplies a model.
Low: agent/queued and agent/steering carry the resolved MessageSource;
streamBlocks() yields strictly in stream order and flushes delta-only
blocks (matches generate()); BlockAssembler freezes blocks on
block-end and ignores stragglers from malformed streams; turn
numbering is a counter seeded from the log (fork-safe); LoopAgent's
stop disposer is infallible (a throwing status listener cannot skip
registry cleanup); AgentLoop.create uses a generator effect so stop
and unregister are independent disposables; SessionStore wires
onAppend inside its effect.
21 regression tests added (review-fixes.spec.ts), organized by
finding. Docs updated: loop pseudocode (status emissions, ordering,
error containment, steering guarantees) and waterfall composition
caveat in docs/architecture.md; AGENTS.md notes that excessive tests
are welcome.
This commit is contained in:
@@ -69,8 +69,11 @@ declare module 'cordis' {
|
||||
'agent/disposed'(agent: Agent): void
|
||||
/** Agent status changed (idle/running/disposed). */
|
||||
'agent/status'(agent: Agent, status: AgentStatus): void
|
||||
/** A message entered the agent's inbox (queued or steering). */
|
||||
'agent/queued'(agent: Agent, content: ContentBlock[], options: SendOptions & { steering: boolean }): void
|
||||
/**
|
||||
* A message entered the agent's inbox (queued or steering). `source` is
|
||||
* the resolved source (defaults applied), not the caller's raw options.
|
||||
*/
|
||||
'agent/queued'(agent: Agent, content: ContentBlock[], info: { source: MessageSource; steering: boolean }): void
|
||||
|
||||
// ---- turn/step boundaries (emit) ----
|
||||
'agent/turn-start'(agent: Agent, turn: number): void
|
||||
@@ -100,7 +103,7 @@ declare module 'cordis' {
|
||||
/** A raw stream chunk arrived (token-level UI/log feed). */
|
||||
'agent/stream-chunk'(agent: Agent, turn: number, step: number, chunk: StreamChunk): void
|
||||
/** Steering content was injected into a running turn. */
|
||||
'agent/steering'(agent: Agent, turn: number, content: ContentBlock[]): void
|
||||
'agent/steering'(agent: Agent, turn: number, content: ContentBlock[], source: MessageSource): void
|
||||
/** A step or turn errored. */
|
||||
'agent/error'(agent: Agent, turn: number, step: number, error: Error): void
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user