fix review findings: whenIdle() is observation, not the teardown await

Codex's confirmation pass found the teardown-framing error went deeper than the
three prose spots already fixed: the whenIdle() JSDoc itself (and its mirrors)
claimed "the quiescence signal a teardown awaits ... a lifecycle owner disposes
the agent through its AgentHandle which ... awaits THIS". The disposer does not
call whenIdle() — it does `stop(); await agent.done` directly
(packages/core/agent-loop/src/index.ts:271). whenIdle() is the NON-OWNER
observation hook; owner teardown awaits the loop-exit promise (done) through
AgentHandle.dispose(). Reframe every copy accordingly:

- packages/core/agent/src/types.ts: the Agent.whenIdle() contract JSDoc.
- packages/core/agent-loop/src/agent.ts: the impl JSDoc.
- packages/core/agent/README.md and docs/core-data-structures/core.md (the
  type-equiv mirror of the types.ts JSDoc — re-copied verbatim).
- docs/rfc/proposed/feature/2026-06-14-acp-agent-client-protocol.md:40 and :70:
  owner teardown via AgentHandle.dispose(); a non-owner observing quiescence
  uses the interface-level agent.whenIdle(), not hand-rolled agent/status.
- Regenerate the cordis catalog (whenIdle source line moved).
This commit is contained in:
Tianyi Cui
2026-06-21 10:03:30 +08:00
parent 436305b1c2
commit c44ae5570c
6 changed files with 37 additions and 39 deletions

View File

@@ -253,12 +253,14 @@ 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: 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.
* `running`, or immediately if it is already idle with no queued work. A
* non-owner's quiescence-observation hook: a consumer that does NOT own the
* agent's lifecycle (a closing ACP connection, a UI plugin) awaits this to
* proceed only after queued/running work has fully stopped, rather than
* returning while the driver is still streaming or about to start a queued
* turn. It does NOT tear the agent down — a lifecycle owner stops and
* unregisters the agent through its `AgentHandle.dispose()` (which awaits the
* loop-exit promise directly), separate from this.
*
* "Quiescence", not merely "status changed": a disposed agent emits
* `agent/status('disposed')` from inside its disposer, BEFORE the driver loop
@@ -266,10 +268,6 @@ interface Agent {
* to actually exit (the implementation chains the loop-exit promise), not just
* observe the status flip. A mid-step disposal that never reaches `idle` still
* unblocks the await this way.
*
* Distinct from disposal: `whenIdle()` observes the transition WITHOUT tearing
* the agent down. A consumer that owns the agent's lifecycle disposes it
* separately.
*/
whenIdle(): Promise<void>