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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user