@@ -42,16 +42,13 @@ func (r *LocalRuntime) registerDefaultTools() {
4242}
4343
4444// finalizeEventChannel performs cleanup at the end of a RunStream goroutine:
45- // clears elicitation state, emits the StreamStopped event, fires hooks, and
46- // closes the events channel.
47- func (r * LocalRuntime ) finalizeEventChannel (ctx context.Context , sess * session.Session , events chan Event ) {
48- // Clear the elicitation events channel before closing the events channel
49- // to prevent a send-on-closed-channel panic in elicitationHandler.
50- // Skip for background sessions (ToolsApproved=true) — they never set the
51- // channel, so clearing it would null out the parent session's channel.
52- if ! sess .ToolsApproved {
53- r .clearElicitationEventsChannel ()
54- }
45+ // restores the previous elicitation channel, emits the StreamStopped event,
46+ // fires hooks, and closes the events channel.
47+ func (r * LocalRuntime ) finalizeEventChannel (ctx context.Context , sess * session.Session , prevElicitationCh , events chan Event ) {
48+ // Swap back the parent's elicitation channel before closing this
49+ // stream's channel. This prevents a send-on-closed-channel panic
50+ // and restores elicitation for the parent session.
51+ r .swapElicitationEventsChannel (prevElicitationCh )
5552
5653 defer close (events )
5754
@@ -80,14 +77,11 @@ func (r *LocalRuntime) RunStream(ctx context.Context, sess *session.Session) <-c
8077 ))
8178 defer sessionSpan .End ()
8279
83- // Set the events channel for elicitation requests.
84- // Skip for background sessions (ToolsApproved=true): they have all tools
85- // pre-approved and will never trigger elicitation prompts. Setting the
86- // channel would overwrite the parent session's channel; clearing it at
87- // teardown would break any pending MCP auth flow in the parent.
88- if ! sess .ToolsApproved {
89- r .setElicitationEventsChannel (events )
90- }
80+ // Swap in this stream's events channel for elicitation and save the
81+ // previous one so it can be restored on teardown. This allows nested
82+ // RunStream calls to temporarily own elicitation without losing the
83+ // parent's channel.
84+ prevElicitationCh := r .swapElicitationEventsChannel (events )
9185
9286 a := r .resolveSessionAgent (sess )
9387
@@ -120,7 +114,7 @@ func (r *LocalRuntime) RunStream(ctx context.Context, sess *session.Session) <-c
120114
121115 events <- StreamStarted (sess .ID , a .Name ())
122116
123- defer r .finalizeEventChannel (ctx , sess , events )
117+ defer r .finalizeEventChannel (ctx , sess , prevElicitationCh , events )
124118
125119 r .registerDefaultTools ()
126120
0 commit comments