Describe the bug
An SDK host can register an external tool named store_memory (or vote_memory) with overridesBuiltInTool: true. The runtime plans the override and removes the built-in from the tool list the model sees. However, when the model calls the tool, the runtime runs the built-in memory executor instead of the host's tool, including the built-in memory permission flow. The host's handler is never invoked. No error or warning is raised.
Overrides of other built-ins, such as read_file, work as expected.
Root cause
- Planning (
session/tool_initialization.rs, plan_eager_tool_initialization): memory tools are added to the built-in list there, so session_local_external_tool_overrides_json correctly marks store_memory as overridden. The model is offered the host's definition.
- Dispatch (
tools/session_tool_invoker.rs, external_override_definition): an override is honoured only when is_runtime_builtin is true. That check uses registry::get_builtin_tool_descriptor(name) plus a few special cases (rg, lsp, task, shell tools).
store_memory and vote_memory are materialized by the tool catalog and aren't returned by get_builtin_tool_descriptor. See registry::is_catalog_materialized_builtin_tool_name, which lists them explicitly.
external_override_definition therefore returns Ok(None) and execute_tool_result falls through to the built-in memory handler.
- Planning and dispatch disagree about which names are overridable built-ins. Any other catalog-materialized name not otherwise special-cased (
lexical_code_search, semantic_code_search, canvas tools) may be affected the same way.
Affected version
1.0.13
Steps to reproduce the behavior
- Using the .NET SDK (reported on 1.0.13), create a session with memory enabled.
- Register an external tool named
store_memory with OverridesBuiltInTool = true and a handler that logs its invocation. Optionally register read_file the same way as a control.
- Prompt the agent to remember a fact, for example "Remember that our build command is
make ci."
- Observe that the host
store_memory handler is never called and the built-in memory permission request is raised instead. A read_file override in the same session is invoked normally.
Expected behavior
When a host registers store_memory / vote_memory with overridesBuiltInTool: true:
- calls go to the host's external tool handler, and
- the built-in memory executor and its permission prompt are not used.
More generally, the set of names accepted as overridable at planning time should match the set honoured at dispatch.
Additional context
- SDK: GitHub Copilot SDK for .NET 1.0.13 (reporter). Root cause verified against current
main.
- Workaround: disable built-in memory (
Memory = new MemoryConfiguration { Enabled = false }) and register memory tools under distinct names.
Describe the bug
An SDK host can register an external tool named
store_memory(orvote_memory) withoverridesBuiltInTool: true. The runtime plans the override and removes the built-in from the tool list the model sees. However, when the model calls the tool, the runtime runs the built-in memory executor instead of the host's tool, including the built-in memory permission flow. The host's handler is never invoked. No error or warning is raised.Overrides of other built-ins, such as
read_file, work as expected.Root cause
session/tool_initialization.rs,plan_eager_tool_initialization): memory tools are added to the built-in list there, sosession_local_external_tool_overrides_jsoncorrectly marksstore_memoryas overridden. The model is offered the host's definition.tools/session_tool_invoker.rs,external_override_definition): an override is honoured only whenis_runtime_builtinis true. That check usesregistry::get_builtin_tool_descriptor(name)plus a few special cases (rg,lsp,task, shell tools).store_memoryandvote_memoryare materialized by the tool catalog and aren't returned byget_builtin_tool_descriptor. Seeregistry::is_catalog_materialized_builtin_tool_name, which lists them explicitly.external_override_definitiontherefore returnsOk(None)andexecute_tool_resultfalls through to the built-in memory handler.lexical_code_search,semantic_code_search, canvas tools) may be affected the same way.Affected version
1.0.13
Steps to reproduce the behavior
store_memorywithOverridesBuiltInTool = trueand a handler that logs its invocation. Optionally registerread_filethe same way as a control.make ci."store_memoryhandler is never called and the built-in memory permission request is raised instead. Aread_fileoverride in the same session is invoked normally.Expected behavior
When a host registers
store_memory/vote_memorywithoverridesBuiltInTool: true:More generally, the set of names accepted as overridable at planning time should match the set honoured at dispatch.
Additional context
main.Memory = new MemoryConfiguration { Enabled = false }) and register memory tools under distinct names.