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
Copy file name to clipboardExpand all lines: packages/coding-agent/src/changes.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,6 +21,13 @@
21
21
- Root cause of the omo `team_wait` starvation forensics: member self-poller injections via `pi.sendUserMessage(..., { deliverAs: "followUp" })` vanished without a trace when the fresh-prompt path threw, leaving no record in the session JSONL while RPC-path `steer`/`follow_up` commands (which bypass `prompt()`) landed normally.
22
22
- Interactive `prompt()` behavior is unchanged: a rejected interactive prompt still drops the input and surfaces the error to the user (pinned by `test/suite/regressions/pre-prompt-compaction-no-continue.test.ts`).
23
23
- Coverage: `test/suite/agent-session-extension-injection.test.ts` pins retention for followUp and steer injections, exact-once delivery after recovery through the post-run drain, and no double-queueing on the streaming accept path.
24
+
25
+
## Reload-safe MCP preservation and extension-removal lifecycle event (2026-07-26)
26
+
27
+
-`session_extensions_removed` is emitted on the old extension runner when a `/reload` or a session replacement (`/new`, `/resume`, `/fork`, import) rebuilds the extension set. Its payload is `{ type: "session_extensions_removed", reason: SessionShutdownEvent["reason"], removed: Array<{ path, resolvedPath }> }`, allowing an extension that did not survive the rebuild to release resources after the new settings and active builtin set are known.
28
+
- Unchanged MCP servers now survive a classic `/reload`: the shared service reattaches and reconciles by config hash, preserving live connections while replacing changed servers and disposing removed ones. Provider-scoped MCP services still dispose on reload because their factory creates a replacement instance.
29
+
- If the MCP builtin itself is disabled during a reload or replacement, its removal event disposes the preserved classic service so stdio children cannot leak. For an otherwise wedged server, use `/mcp reconnect <name>` to force a fresh connection.
30
+
24
31
## Same-model-first transient retries and capped server waits (2026-07-26)
- Classic (non-provider-scoped) MCP reloads keep the shared `McpService` alive. The reload-time `session_start` reattaches it and its existing config-hash reconciliation preserves unchanged servers while replacing changed definitions and disposing removed definitions.
7
+
- Provider-scoped MCP services still dispose on `reload`, because rebuilding an extension factory creates a new scoped instance and preserving the old one would orphan its child processes.
8
+
- Core now emits `{ type: "session_extensions_removed", reason: "reload", removed: Array<{ path, resolvedPath }> }` on the old runner after it knows the rebuilt extension set. MCP matches its builtin identity (`<builtin:mcp>`) in that event and disposes the preserved classic service when MCP is disabled during a reload.
9
+
-`/mcp reconnect <name>` remains the explicit escape hatch for a server that is connected but wedged: it renews that server without requiring a full reload.
10
+
11
+
### Why
12
+
- Spawning every MCP server again on every classic reload adds a fixed process startup cost even when config is unchanged. Preserving and reconciling retains healthy children, while the removal event closes the only gap where the preserved singleton otherwise loses its owning extension.
13
+
14
+
### Why extension system couldn't handle this alone
15
+
- The core alone can identify removed extension entries but must remain resource-agnostic; MCP alone cannot know the post-reload builtin set at `session_shutdown`. The core event provides the lifecycle boundary and MCP owns the service-specific disposal.
0 commit comments