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 lets an embedder supply externally-implemented tools at create time (SessionConfig.tools: Option<Vec<Tool>>, where each Tool may carry an Arc<dyn ToolHandler>), but there is no public way to change that tool set once the session is live. The per-session handler map is frozen exactly once, at create:
Client::create_session collects each Tool's handler into runtime.tool_handlers, then builds SessionHandlers { tools: Arc::new(std::mem::take(&mut runtime.tool_handlers)), .. } (src/session.rs, ~L901; identical in resume_session, ~L1166). After that the map is an immutable Arc<HashMap<String, Arc<dyn ToolHandler>>> and is never mutated.
Dispatch of external_tool.requested does handlers.tools.get(&tool_name); on None it early-returns (src/session.rs, ~L1776) — there is no wildcard/fallback handler. So a tool that wasn't present at create is silently un-invokable, and a handler that was present at create can't be removed.
Tool definitions reach the CLI only through the create/resume RPC (SessionConfig.tools). There is no public runtime path to add, replace, or remove definitions after create.
The one runtime tool-registration wire method — sessions.registerExtensionToolsOnSession — is exposed only as SessionRpc::register_extension_tools_on_session, which is pub(crate), flagged Experimental, has no public caller, and whose own doc says it "disappears once extension discovery / launch / tool registration are owned by the runtime." Even if it were reached via the public Client::call, it wires up nothing in the in-process SessionHandlers.tools map, so a resulting tool.call still wouldn't reach any ToolHandler.
Net effect: an embedder can define/advertise externally-implemented tools only at create, and can neither add a newly-available tool nor remove an existing one mid-session.
Why this matters
An embedder that fronts multiple clients — or a single client whose published tool set changes over the life of a session — needs to reflect tool add/replace/remove on the liveSession without tearing it down. Recreating the session to change its tool set destroys turn/session state and conversation history, which isn't acceptable for a long-lived session. This is the tool analogue of Session::set_model, which already lets the model change mid-session.
Current behavior (public source, for reference)
SessionConfig.tools: Option<Vec<Tool>> and ResumeSessionConfig.tools: Option<Vec<Tool>> are the only inputs for externally-implemented tools; both are consumed once, at create/resume.
Each Tool may carry an optional Arc<dyn ToolHandler>; those are collected into runtime.tool_handlers, then moved out via std::mem::take into the per-session SessionHandlers.tools map — built once, never mutated.
external_tool.requested dispatch: handlers.tools.get(&tool_name); None ⇒ early return (no fallback / wildcard handler).
The public Session surface has set_model, send, abort, rpc(), etc. — nothing for tools.
SessionRpc::register_extension_tools_on_session (wire sessions.registerExtensionToolsOnSession) is pub(crate) + Experimental + slated for removal, and does not populate the in-process handler map.
Proposed API
Public per-session methods on Session that update both the CLI-visible tool definitions (so the model can actually call newly-added tools and stops seeing removed ones) and the in-process handler map (so external_tool.requested routes back to the ToolHandler):
implSession{/// Replace the session's externally-implemented tool set wholesale./// Tools carrying a handler become invokable; any previously-registered/// tool not present here is removed.pubasyncfnset_tools(&self,tools:Vec<Tool>) -> Result<(),Error>;/// Add or replace individual tools by name (upsert), leaving others intact.pubasyncfnregister_tools(&self,tools:Vec<Tool>) -> Result<(),Error>;/// Remove tools by name.pubasyncfnremove_tools(&self,names:&[String]) -> Result<(),Error>;}
Requirements:
Update the CLI-visible tool definitions so the model gains/loses the tools for subsequent turns.
Update the SDK-side handler map (make the per-session tools map mutable behind the existing Arc — e.g. Arc<Mutex<..>> or an arc-swap — instead of a one-shot std::mem::take) so dispatch resolves against the current set.
Last-write-wins / idempotent semantics, and concurrency-safe with in-flight tool.calls.
The change is purely additive — create-time behavior is unchanged.
Alternatives considered
Call sessions.registerExtensionToolsOnSession via the public Client::call — not viable: it's pub(crate) / Experimental / slated for removal, and it doesn't touch the in-process handler map, so a subsequent tool.call still wouldn't reach a ToolHandler.
Recreate the session on every tool change — destroys turn/session state and history; not acceptable for a live session.
Related
Mutable sessions #941 ("Mutable sessions", closed as duplicate/tracked-elsewhere) captured the broad idea of mid-session mutability across tools, prompt, etc. This issue is the focused, tools-only slice with a concrete public API and the specific runtime blocker spelled out.
Summary
Sessionlets an embedder supply externally-implemented tools at create time (SessionConfig.tools: Option<Vec<Tool>>, where eachToolmay carry anArc<dyn ToolHandler>), but there is no public way to change that tool set once the session is live. The per-session handler map is frozen exactly once, at create:Client::create_sessioncollects eachTool's handler intoruntime.tool_handlers, then buildsSessionHandlers { tools: Arc::new(std::mem::take(&mut runtime.tool_handlers)), .. }(src/session.rs, ~L901; identical inresume_session, ~L1166). After that the map is an immutableArc<HashMap<String, Arc<dyn ToolHandler>>>and is never mutated.external_tool.requesteddoeshandlers.tools.get(&tool_name); onNoneit early-returns (src/session.rs, ~L1776) — there is no wildcard/fallback handler. So a tool that wasn't present at create is silently un-invokable, and a handler that was present at create can't be removed.SessionConfig.tools). There is no public runtime path to add, replace, or remove definitions after create.sessions.registerExtensionToolsOnSession— is exposed only asSessionRpc::register_extension_tools_on_session, which ispub(crate), flagged Experimental, has no public caller, and whose own doc says it "disappears once extension discovery / launch / tool registration are owned by the runtime." Even if it were reached via the publicClient::call, it wires up nothing in the in-processSessionHandlers.toolsmap, so a resultingtool.callstill wouldn't reach anyToolHandler.Net effect: an embedder can define/advertise externally-implemented tools only at create, and can neither add a newly-available tool nor remove an existing one mid-session.
Why this matters
An embedder that fronts multiple clients — or a single client whose published tool set changes over the life of a session — needs to reflect tool add/replace/remove on the live
Sessionwithout tearing it down. Recreating the session to change its tool set destroys turn/session state and conversation history, which isn't acceptable for a long-lived session. This is the tool analogue ofSession::set_model, which already lets the model change mid-session.Current behavior (public source, for reference)
SessionConfig.tools: Option<Vec<Tool>>andResumeSessionConfig.tools: Option<Vec<Tool>>are the only inputs for externally-implemented tools; both are consumed once, at create/resume.Toolmay carry an optionalArc<dyn ToolHandler>; those are collected intoruntime.tool_handlers, then moved out viastd::mem::takeinto the per-sessionSessionHandlers.toolsmap — built once, never mutated.external_tool.requesteddispatch:handlers.tools.get(&tool_name);None⇒ earlyreturn(no fallback / wildcard handler).Sessionsurface hasset_model,send,abort,rpc(), etc. — nothing for tools.SessionRpc::register_extension_tools_on_session(wiresessions.registerExtensionToolsOnSession) ispub(crate)+ Experimental + slated for removal, and does not populate the in-process handler map.Proposed API
Public per-session methods on
Sessionthat update both the CLI-visible tool definitions (so the model can actually call newly-added tools and stops seeing removed ones) and the in-process handler map (soexternal_tool.requestedroutes back to theToolHandler):Requirements:
toolsmap mutable behind the existingArc— e.g.Arc<Mutex<..>>or an arc-swap — instead of a one-shotstd::mem::take) so dispatch resolves against the current set.tool.calls.The change is purely additive — create-time behavior is unchanged.
Alternatives considered
sessions.registerExtensionToolsOnSessionvia the publicClient::call— not viable: it'spub(crate)/ Experimental / slated for removal, and it doesn't touch the in-process handler map, so a subsequenttool.callstill wouldn't reach aToolHandler.Related