docs: address review on RFCs 009-011

Resolve the inline review feedback on PR #18 (all verified against the
codebase, the published @agentclientprotocol/sdk@0.25.1 tarball, and
Cordis fiber semantics):

- 009: dsh-session owns SessionMeta (persistence re-exports) to avoid a
  package cycle; split mutable summary into a sidecar so the event log
  stays append-only and list/load can return it; pick one load-repair
  rule (resume from the last complete turn/end, overwrite the orphan).
- 010: SDK has a zod peer dep + runtime zod/v4 import (drop "zero runtime
  deps"); session/new needs a create seam taking {sessionId, meta};
  propose an abstract create/resume factory on dsh-agent so the bridge
  depends on the interface not the loop, and observe agent/status for
  quiescence since agent.done is LoopAgent-only; add the explicit
  TurnEndReason -> ACP StopReason wire mapping + test; reject non-empty
  additionalDirectories for the MVP; remove the EOF blank line.
- 011: ctx.extend() does not create a disposable fiber — use a real
  per-session disposer scope.
This commit is contained in:
Tianyi Cui
2026-06-14 21:43:16 +08:00
parent a44f7f3486
commit 64ce7caedc
3 changed files with 16 additions and 15 deletions

View File

@@ -18,8 +18,8 @@ The harness core already supports many agents (`AgentRegistry.list()` and `Agent
## Plan
1. Generalize the two id maps to multi-entry and add the `agent→sessionId` reverse map; add a per-session record holding the agent, the in-flight-prompt state, the pending-permission registry, and the session's child context (see step 2).
2. Give each session its own child Cordis context (`ctx.extend()`) and register that session's listeners on it, so per-session listeners are fiber-scoped — disposing one session's child fiber removes exactly its listeners while the other N-1 sessions (and the bridge root) keep running. Demux every `agent/*` and `session/event` by id into the right session record. Note the single global `tools/execute` listener stays on the bridge root (it must see all agents) and routes via the reverse map.
1. Generalize the two id maps to multi-entry and add the `agent→sessionId` reverse map; add a per-session record holding the agent, the in-flight-prompt state, the pending-permission registry, and the session's disposer scope (see step 2).
2. Give each session a real per-session disposer scope, NOT `ctx.extend()` — in Cordis `ctx.extend()` only creates a child context/prototype, but `ctx.on()` registered on it is still owned by the current plugin fiber, so disposing it would not remove that session's listeners. Use a genuine child fiber (load a per-session sub-plugin, e.g. `ctx.plugin(...)` returning a fork, or collect each session's `ctx.on` disposers in its session record and call them on teardown). Demux every `agent/*` and `session/event` by id into the right session record. Note the single global `tools/execute` listener stays on the bridge root (it must see all agents) and routes via the reverse map.
3. Lift the `session/new` guard; keep `session/load` (RFC 010) working per session.
4. Tests for cross-session isolation: two sessions streaming and permission-prompting concurrently never interleave; a cancel/abort in one session leaves the other's stream and pending permission untouched; per-session in-flight-prompt enforcement holds independently; disposing one session leaves the others running.