You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
| Session fingerprinting | Implicit teardown on cwd/mcp change | Explicit `refreshSession()`| Caller-initiated is more predictable |
62
62
| Shutdown on ACP close | Process exits | No standalone process | Agent is embedded in server |
63
-
| Unsupported slash commands | Loops silently on early idle | Emits "Unsupported slash command" chunk, gated on `initializationResult().commands` so plugin/skill commands (e.g. `/skills-store`) whose echoes use a fresh uuid are not false-flagged | The SDK consumes some slash commands without producing output (e.g. `/plugin` in non-interactive mode); without this we hang. The known-commands gate avoids racing plugin/skill loads where idle can arrive before the transformed user-message echo. |
64
-
| Prompt-loop cancel race |`Promise.race([query.next(), cancelWake])` each iteration (#742) |`withAbort(query.next(), cancelController.signal)` helper in `utils/common.ts`, also guarding the `compact_boundary``getContextUsage` fetch | The classic `Promise.race` leak (nodejs/node#17469): each race call parks a reaction on the turn-lived `cancelWake` promise that retains that iteration's settled value, so every yielded message (and every stream event, since `includePartialMessages` is on) stays reachable until the turn ends. Long high-reasoning turns could pin tens of MB. `withAbort` removes its abort listener as soon as `next()` settles, so nothing accumulates. Cancel semantics are unchanged, including the force-cancel backstop. |
63
+
| Unsupported slash commands | Loops silently on early idle | Emits "Unsupported slash command" chunk, gated on `initializationResult().commands` so plugin/skill commands (e.g. `/skills-store`) whose echoes use a fresh uuid are not false-flagged. Lives in the consumer's idle handler: fires only when idle arrives with no active turn, an unsettled head turn whose leading command is unknown, and no pending orphan results. | The SDK consumes some slash commands without producing output (e.g. `/plugin` in non-interactive mode); without this we hang. The known-commands gate avoids racing plugin/skill loads where idle can arrive before the transformed user-message echo. |
64
+
| Prompt-loop cancel race | Per-iteration `addEventListener`/`removeEventListener` race in the consumer (#780) |`withAbort(query.next(), cancelController.signal)` helper in `utils/common.ts`, also guarding the `compact_boundary``getContextUsage` fetch | Same effect (no listener/reaction accumulation on the long-lived wake-up promise), different helper. `withAbort` removes its abort listener as soon as `next()` settles; the consumer re-arms a fresh controller after each abort fire, matching upstream's re-arm. |
65
+
| ACP connection wiring |`agent({name}).onRequest(...).connect(stream)` builder + narrow `AcpClient` interface (#790) | Keeps `AgentSideConnection` / `ClientSideConnection` (deprecated but fully functional in ACP 1.1.0) in `acp-connection.ts` / `base-acp-agent.ts` / codex | The fork is embedded (in-process streams, `extMethod`/`extNotification` extension surface) and the deprecated classes still route optional `extMethod`/`extNotification` to the Agent/Client. Revisit when ACP removes them; permission cancellation already uses the class's generic `request(..., { cancellationSignal })`. |
66
+
| Consumer ownership | Per-session map; consumer keyed by `sessions[id]`| Single `this.session`; consumer captures `query` + `queryGeneration` and exits quietly on mismatch |`refreshSession()` (fork-only) swaps `query`/`input` in place on the same session object; the generation guard keeps a retired consumer from tearing down the refreshed session. |
0 commit comments