simplify(agent): drop the unused public Agent.abort(), keep whenIdle()
The public Agent handle exposed abort() (step-only) and cancel() (queue-aware).
No production caller used abort() — ACP maps session/cancel to cancel(), and
lifecycle owners tear down via AgentHandle.dispose(); the loop's own stop paths
abort their per-step AbortController directly. So abort() is latent generality
that keeps a private loop mechanic public.
RFC-premise correction: the public-agent-stop-surface RFC proposed removing
whenIdle() too. Implementation found whenIdle() load-bearing — a real
quiescence primitive with a deliberate loop contract (settle-without-transition,
the replacement-turn race) and ACP test consumers; its proposed replacement
("observe the running->idle transition") is exactly the async-state race
AGENTS.md warns against. So only abort() is removed; whenIdle() stays. The RFC
is amended on the way to implemented/ to record the narrowed scope, and the new
AGENTS.md "RFCs are proposals, not golden truth" principle (PR1) gets its
worked example.
- Remove Agent.abort() from the interface + the ReactLoopAgent impl; the no-arg
'aborted' default goes with it (cancel() keeps its 'cancelled' default).
- Migrate tests: empty-queue abort() -> cancel(reason); the two review-fixes
tests whose subject is the in-flight step's AbortController drive that
controller directly via the private currentAbort field (cancel() would clear
the inbox and destroy the queued steering one of them proves survives a step
abort). The no-arg-default test is dropped (cancel()'s default is already
covered in cancel.spec.ts).
- Resulting public stop surface: cancel() + whenIdle(). Update agent/agent-loop
READMEs, architecture.md, core.md type-equiv, the extension cookbook, the
lifecycle RFC (short note), and the proposed ACP RFC.
Implements docs/rfc/implemented/simplification/2026-06-20-public-agent-stop-surface.md
This commit is contained in:
@@ -233,12 +233,8 @@ interface Agent {
|
||||
*/
|
||||
inject(content: ContentBlock[], options?: SendOptions): void
|
||||
|
||||
/** Abort the in-flight step (if any); the turn ends with reason 'aborted'. */
|
||||
abort(reason?: string): void
|
||||
|
||||
/**
|
||||
* Cancel ALL pending work for the agent — the narrower {@link abort} kills
|
||||
* only the in-flight step. `cancel()`:
|
||||
* Cancel ALL pending work for the agent. `cancel()`:
|
||||
*
|
||||
* - clears the queued FIFO (un-started prompts never run) and the steering
|
||||
* FIFO (steering for the cancelled turn is dropped, not re-enqueued);
|
||||
@@ -258,11 +254,11 @@ interface Agent {
|
||||
/**
|
||||
* Resolve once the agent has reached quiescence after settling out of
|
||||
* `running`, or immediately if it is already idle with no queued work. The
|
||||
* quiescence signal a teardown awaits: `agent.abort()` then
|
||||
* `await agent.whenIdle()` guarantees queued/running work has fully stopped
|
||||
* before the caller proceeds (a closing ACP connection, a disposing UI
|
||||
* plugin), rather than returning while the driver is still streaming or about
|
||||
* to start a queued turn.
|
||||
* quiescence signal a teardown awaits: a lifecycle owner disposes the agent
|
||||
* through its `AgentHandle` (which aborts in-flight work then awaits this), so
|
||||
* the caller proceeds only after queued/running work has fully stopped (a
|
||||
* closing ACP connection, a disposing UI plugin) rather than returning while
|
||||
* the driver is still streaming or about to start a queued turn.
|
||||
*
|
||||
* "Quiescence", not merely "status changed": a disposed agent emits
|
||||
* `agent/status('disposed')` from inside its disposer, BEFORE the driver loop
|
||||
|
||||
Reference in New Issue
Block a user