(agent: &Agent)
| 104 | } |
| 105 | |
| 106 | pub(super) async fn close_agent(agent: &Agent) { |
| 107 | // Mark closed *before* iterating so concurrent `session()` calls fail fast. |
| 108 | if agent.closed.swap(true, std::sync::atomic::Ordering::AcqRel) { |
| 109 | return; |
| 110 | } |
| 111 | |
| 112 | // Snapshot live handles so we can close them outside the registry lock. |
| 113 | // Also prune dead `Weak` entries here: a high-churn create-and-drop |
| 114 | // workload that never calls `list_sessions`/`close_session` would |
| 115 | // otherwise leave dangling entries in the registry until agent close. |
| 116 | let handles: Vec<Arc<SessionCloseHandle>> = { |
| 117 | let mut sessions = agent |
| 118 | .sessions |
| 119 | .lock() |
| 120 | .unwrap_or_else(|poison| poison.into_inner()); |
| 121 | sessions.retain(|_, weak| weak.strong_count() > 0); |
| 122 | sessions.values().filter_map(Weak::upgrade).collect() |
| 123 | }; |
| 124 | for handle in handles { |
| 125 | handle.close().await; |
| 126 | } |
| 127 | |
| 128 | // Tear down global MCP connections so background workers exit. |
| 129 | if let Some(mcp) = &agent.global_mcp { |
| 130 | for name in mcp.list_connected().await { |
| 131 | if let Err(e) = mcp.disconnect(&name).await { |
| 132 | tracing::warn!( |
| 133 | server = %name, |
| 134 | error = %e, |
| 135 | "Failed to disconnect MCP server during Agent::close" |
| 136 | ); |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | pub(super) fn create_session_for_agent( |
| 143 | agent: &Agent, |
no test coverage detected