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:
Tianyi Cui
2026-06-21 05:50:39 +08:00
parent d6a2ab30c8
commit f6bd1468f2
15 changed files with 58 additions and 81 deletions
+16 -18
View File
@@ -25,7 +25,7 @@ An agent was registered in the AgentRegistry and is ready to receive messages.
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:141`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:136`](../../packages/core/agent/src/types.ts)
#### `agent/disposed` — emit
@@ -37,7 +37,7 @@ An agent was disposed and removed from the registry; its fiber and any in-flight
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:147`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:142`](../../packages/core/agent/src/types.ts)
#### `agent/error` — emit
@@ -49,7 +49,7 @@ A step or turn errored. The loop reports a failure here (plus the logger) even w
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:224`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:219`](../../packages/core/agent/src/types.ts)
#### `agent/queued` — emit
@@ -61,7 +61,7 @@ A message entered the agent's inbox (queued or steering). `source` is the resolv
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:160`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:155`](../../packages/core/agent/src/types.ts)
#### `agent/request` — waterfall
@@ -73,7 +73,7 @@ Waterfall: mutate the fully-assembled GenerateOptions before the model call (hoo
Types: [Agent](../core-data-structures/core.md) · [GenerateOptions](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:193`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:188`](../../packages/core/agent/src/types.ts)
#### `agent/status` — emit
@@ -85,7 +85,7 @@ Agent status changed (`idle` ⇄ `running`, or → `disposed`). Drive lifecycle
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:154`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:149`](../../packages/core/agent/src/types.ts)
#### `agent/steering` — emit
@@ -97,7 +97,7 @@ Steering content was injected into a running turn.
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:218`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:213`](../../packages/core/agent/src/types.ts)
#### `agent/step-end` — emit
@@ -109,7 +109,7 @@ A step ended.
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:184`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:179`](../../packages/core/agent/src/types.ts)
#### `agent/step-result` — waterfall
@@ -121,7 +121,7 @@ Waterfall: post-process the assembled assistant Message before tool dispatch (va
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:199`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:194`](../../packages/core/agent/src/types.ts)
#### `agent/step-start` — emit
@@ -133,7 +133,7 @@ A step (one model call plus its tool dispatch) began. `step` is 1-based within t
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:179`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:174`](../../packages/core/agent/src/types.ts)
#### `agent/stream-chunk` — emit
@@ -145,7 +145,7 @@ A raw StreamChunk arrived from the model (token-level UI/log feed).
Types: [Agent](../core-data-structures/core.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Source: [`packages/core/agent/src/types.ts:213`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:208`](../../packages/core/agent/src/types.ts)
#### `agent/turn-continuation` — waterfall
@@ -157,7 +157,7 @@ Waterfall: override the turn-continuation decision. The default (computed by the
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:206`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:201`](../../packages/core/agent/src/types.ts)
#### `agent/turn-end` — emit
@@ -169,7 +169,7 @@ A turn ended. `reason` distinguishes a clean stop from a truncated or aborted on
Types: [Agent](../core-data-structures/core.md) · [TurnEndReason](../core-data-structures/session.md)
Source: [`packages/core/agent/src/types.ts:173`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:168`](../../packages/core/agent/src/types.ts)
#### `agent/turn-start` — emit
@@ -181,7 +181,7 @@ A turn began. `turn` is the 1-based turn number within the session.
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:167`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:162`](../../packages/core/agent/src/types.ts)
### `llm/*`
@@ -288,12 +288,12 @@ The agent-loop plugin (`ctx.agentLoop`): creates ReactLoopAgents, runs their loo
The loop itself is deliberately thin — every behavior beyond "call the model, run the tools, repeat" belongs to plugins listening on the event taxonomy declared in @deepseek-ai/dsh-agent.
```ts cordis-catalog
create(id: AgentId, options: AgentOptions = {}): ReactLoopAgent
create(id: string, options: AgentOptions = {}): ReactLoopAgent
createAgent(options: CreateAgentOptions): AgentHandle
async resume(options: ResumeAgentOptions): Promise<AgentHandle>
```
Source: [`packages/core/agent-loop/src/index.ts:63`](../../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:60`](../../packages/core/agent-loop/src/index.ts)
### `ctx.agents` — `AgentRegistry`
@@ -327,9 +327,7 @@ Semantics every implementation must honor:
abstract resolve(request: BashExecRequest): BashExecSpec
abstract run(spec: BashExecSpec): Promise<BashRunResult>
abstract start(spec: BashExecSpec): BashTask
abstract get(id: BashTaskId): BashTask | undefined
abstract ownerOf(id: BashTaskId): OwnerToken | undefined
abstract list(): BashTask[]
abstract readOutput(id: BashTaskId): BashTaskRead
abstract kill(id: BashTaskId): boolean
onTaskDone(listener: BashTaskListener): () => void