Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
InstructionDirectories: config.InstructionDirectories,
PluginDirectories: config.PluginDirectories,
LargeOutput: config.LargeOutput,
ToolSearch: config.ToolSearch,
Memory: config.Memory,
Canvases: config.Canvases,
RequestCanvasRenderer: config.RequestCanvasRenderer,
Expand Down Expand Up @@ -1297,6 +1298,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
InstructionDirectories: config.InstructionDirectories,
PluginDirectories: config.PluginDirectories,
LargeOutput: config.LargeOutput,
ToolSearch: config.ToolSearch,
Memory: config.Memory,
Canvases: config.Canvases,
RequestCanvasRenderer: config.RequestCanvasRenderer,
Expand Down Expand Up @@ -2630,6 +2632,7 @@ internal record CreateSessionRequest(
IList<string>? InstructionDirectories = null,
IList<string>? PluginDirectories = null,
LargeToolOutputConfig? LargeOutput = null,
ToolSearchConfig? ToolSearch = null,
MemoryConfiguration? Memory = null,
#pragma warning disable GHCP001
IList<CanvasDeclaration>? Canvases = null,
Expand Down Expand Up @@ -2729,6 +2732,7 @@ internal record ResumeSessionRequest(
IList<string>? InstructionDirectories = null,
IList<string>? PluginDirectories = null,
LargeToolOutputConfig? LargeOutput = null,
ToolSearchConfig? ToolSearch = null,
MemoryConfiguration? Memory = null,
#pragma warning disable GHCP001
IList<CanvasDeclaration>? Canvases = null,
Expand Down
25 changes: 25 additions & 0 deletions dotnet/src/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@
private readonly Channel<SessionEvent> _eventChannel = Channel.CreateUnbounded<SessionEvent>(
new() { SingleReader = true });

/// <summary>
/// Fixed name of the runtime's built-in tool-search tool. A client can
/// replace its behavior by registering a tool with this exact name and
/// <c>OverridesBuiltInTool</c> set to <c>true</c>.
/// </summary>
private const string ToolSearchToolName = "tool_search_tool";

/// <summary>
/// Gets the unique identifier for this session.
/// </summary>
Expand Down Expand Up @@ -841,6 +848,24 @@
Arguments = arguments
};

// The built-in tool-search tool receives a snapshot of the session's
// currently initialized tools so an override can filter the live
// catalog without issuing its own RPC. Fetch it only for that tool
// to avoid a round-trip on every tool call; a failed fetch leaves
// the snapshot null rather than failing the tool.
if (toolName == ToolSearchToolName)
{
try
{
var metadata = await Rpc.Tools.GetCurrentMetadataAsync();
invocation.AvailableTools = metadata.Tools;
}
catch
{
// Leave AvailableTools null on failure.
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}

var aiFunctionArgs = new AIFunctionArguments
{
Context = new Dictionary<object, object?>
Expand Down
46 changes: 46 additions & 0 deletions dotnet/src/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,12 @@ public sealed class ToolResultObject
[JsonPropertyName("toolTelemetry")]
public IDictionary<string, object>? ToolTelemetry { get; set; }

/// <summary>
/// Names of tools returned by a tool-search tool.
/// </summary>
[JsonPropertyName("toolReferences")]
public IList<string>? ToolReferences { get; set; }

/// <summary>
/// Converts the result of an <see cref="AIFunction"/> invocation into a
/// <see cref="ToolResultObject"/>. Handles <see cref="ToolResultAIContent"/>,
Expand Down Expand Up @@ -790,6 +796,14 @@ public sealed class ToolInvocation
/// Arguments passed to the tool by the language model.
/// </summary>
public JsonElement? Arguments { get; set; }
/// <summary>
/// Snapshot of the session's currently initialized tools. The SDK populates
/// this only when the invocation targets the built-in tool-search tool
/// (<c>tool_search_tool</c>), so a tool-search override can rank/filter the
/// live catalog — including MCP tools configured in settings — without
/// issuing its own RPC. <c>null</c> for every other tool invocation.
/// </summary>
public IList<CurrentToolMetadata>? AvailableTools { get; set; }
}

/// <summary>
Expand Down Expand Up @@ -2703,6 +2717,30 @@ public sealed class LargeToolOutputConfig
public string? OutputDirectory { get; set; }
}

/// <summary>
/// Overrides the runtime's built-in tool-search behavior.
/// Defers tools to keep the model's active tool set small.
/// To override the tool-search tool's implementation, register a tool
/// named "tool_search_tool" with <c>OverridesBuiltInTool</c> set to
/// <see langword="true"/>.
/// </summary>
public sealed class ToolSearchConfig
{
/// <summary>
/// Enable or disable tool search.
/// </summary>
[JsonPropertyName("enabled")]
public bool? Enabled { get; set; }

/// <summary>
/// The tool count above which MCP and external tools are deferred behind
/// tool search. When <see langword="null"/>, the runtime default (30)
/// applies.
/// </summary>
[JsonPropertyName("deferThreshold")]
public int? DeferThreshold { get; set; }
}

/// <summary>
/// Configuration for session memory.
/// </summary>
Expand Down Expand Up @@ -2811,6 +2849,7 @@ protected SessionConfigBase(SessionConfigBase? other)
Hooks = other.Hooks;
InfiniteSessions = other.InfiniteSessions;
LargeOutput = other.LargeOutput;
ToolSearch = other.ToolSearch;
Memory = other.Memory;
McpServers = other.McpServers is not null
? (other.McpServers is Dictionary<string, McpServerConfig> dict
Expand Down Expand Up @@ -3215,6 +3254,13 @@ protected SessionConfigBase(SessionConfigBase? other)
/// </summary>
public LargeToolOutputConfig? LargeOutput { get; set; }

/// <summary>
/// Overrides the runtime's built-in tool-search behavior.
/// Tool search defers tools to keep the model's active tool set small. When <see langword="null"/>,
/// the runtime default applies.
/// </summary>
public ToolSearchConfig? ToolSearch { get; set; }

/// <summary>
/// Configuration for session memory. When set, controls whether the
/// session can read and write persistent memory.
Expand Down
2 changes: 2 additions & 0 deletions go/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses
req.DisabledSkills = config.DisabledSkills
req.InfiniteSessions = config.InfiniteSessions
req.LargeOutput = config.LargeOutput
req.ToolSearch = config.ToolSearch
req.Memory = config.Memory
req.GitHubToken = config.GitHubToken
req.RemoteSession = config.RemoteSession
Expand Down Expand Up @@ -1085,6 +1086,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string,
req.DisabledSkills = config.DisabledSkills
req.InfiniteSessions = config.InfiniteSessions
req.LargeOutput = config.LargeOutput
req.ToolSearch = config.ToolSearch
req.Memory = config.Memory
req.GitHubToken = config.GitHubToken
req.RemoteSession = config.RemoteSession
Expand Down
17 changes: 17 additions & 0 deletions go/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import (
"github.com/github/copilot-sdk/go/rpc"
)

// toolSearchToolName is the fixed name of the runtime's built-in tool-search
// tool. A client can replace its behavior by registering a [Tool] with this
// exact name and OverridesBuiltInTool set to true.
const toolSearchToolName = "tool_search_tool"

type sessionHandler struct {
id uint64
fn SessionEventHandler
Expand Down Expand Up @@ -1513,6 +1518,17 @@ func (s *Session) executeToolAndRespond(requestID, toolName, toolCallID string,
TraceContext: ctx,
}

// The built-in tool-search tool receives a snapshot of the session's
// currently initialized tools so an override can filter the live catalog
// without issuing its own RPC. Fetch it only for that tool to avoid a
// round-trip on every tool call; a failed fetch leaves the snapshot nil
// rather than failing the tool.
if toolName == toolSearchToolName {
if metadata, mErr := s.RPC.Tools.GetCurrentMetadata(ctx); mErr == nil && metadata != nil {
invocation.AvailableTools = metadata.Tools
}
}

result, err := handler(invocation)
if err != nil {
errMsg := err.Error()
Expand Down Expand Up @@ -1542,6 +1558,7 @@ func (s *Session) executeToolAndRespond(requestID, toolName, toolCallID string,
TextResultForLlm: textResultForLLM,
ToolTelemetry: result.ToolTelemetry,
ResultType: &effectiveResultType,
ToolReferences: result.ToolReferences,
}
if result.Error != "" {
rpcResult.Error = &result.Error
Expand Down
32 changes: 32 additions & 0 deletions go/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,18 @@ type LargeToolOutputConfig struct {
OutputDirectory string `json:"outputDir,omitempty"`
}

// ToolSearchConfig allows to configure tool search behavior.
// Tool search defers tools to keep the model's active tool set small.
// To override the tool-search tool's implementation, register a
// [Tool] named "tool_search_tool" with OverridesBuiltInTool set to true.
type ToolSearchConfig struct {
// Controls whether tool search is enabled.
Enabled *bool `json:"enabled,omitempty"`
// DeferThreshold is the tool count above which MCP and external tools are
// deferred behind tool search. When nil, the runtime default (30) applies.
DeferThreshold *int `json:"deferThreshold,omitempty"`
}

// SessionFSCapabilities declares optional provider capabilities.
type SessionFSCapabilities struct {
// Sqlite indicates whether the provider supports SQLite query/exists operations.
Expand Down Expand Up @@ -1153,6 +1165,10 @@ type SessionConfig struct {
// output exceeding the configured size, the output is written to a temp file
// and a reference is returned to the model instead of the full payload.
LargeOutput *LargeToolOutputConfig
// ToolSearch overrides the runtime's built-in tool-search behavior, which
// defers rarely used tools behind a searchable index. When nil, the runtime
// default applies.
ToolSearch *ToolSearchConfig
// Memory configures the memory feature for the session. When omitted, the
// runtime default applies.
Memory *MemoryConfiguration
Expand Down Expand Up @@ -1281,6 +1297,14 @@ type ToolInvocation struct {
ToolName string
Arguments any

// AvailableTools is a snapshot of the session's currently initialized
// tools. The SDK populates it only when this invocation targets the
// built-in tool-search tool ("tool_search_tool"), so a tool-search
// override can rank/filter the live catalog -- including MCP tools
// configured in settings -- without issuing its own RPC. It is nil for
// every other tool invocation.
AvailableTools []rpc.CurrentToolMetadata

// TraceContext carries the W3C Trace Context propagated from the CLI's
// execute_tool span. Pass this to OpenTelemetry-aware code so that
// child spans created inside the handler are parented to the CLI span.
Expand All @@ -1300,6 +1324,8 @@ type ToolResult struct {
Error string `json:"error,omitempty"`
SessionLog string `json:"sessionLog,omitempty"`
ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"`
// ToolReferences lists names of tools returned by a tool-search tool.
ToolReferences []string `json:"toolReferences,omitempty"`
}

// CommandContext provides context about a slash-command invocation.
Expand Down Expand Up @@ -1596,6 +1622,10 @@ type ResumeSessionConfig struct {
// output exceeding the configured size, the output is written to a temp file
// and a reference is returned to the model instead of the full payload.
LargeOutput *LargeToolOutputConfig
// ToolSearch overrides the runtime's built-in tool-search behavior, which
// defers rarely used tools behind a searchable index. When nil, the runtime
// default applies.
ToolSearch *ToolSearchConfig
// Memory configures the memory feature for the session. When omitted, the
// runtime default applies.
Memory *MemoryConfiguration
Expand Down Expand Up @@ -2109,6 +2139,7 @@ type createSessionRequest struct {
DisabledSkills []string `json:"disabledSkills,omitempty"`
InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"`
LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"`
ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"`
Memory *MemoryConfiguration `json:"memory,omitempty"`
Commands []wireCommand `json:"commands,omitempty"`
RequestElicitation *bool `json:"requestElicitation,omitempty"`
Expand Down Expand Up @@ -2198,6 +2229,7 @@ type resumeSessionRequest struct {
DisabledSkills []string `json:"disabledSkills,omitempty"`
InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"`
LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"`
ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"`
Memory *MemoryConfiguration `json:"memory,omitempty"`
Commands []wireCommand `json:"commands,omitempty"`
RequestElicitation *bool `json:"requestElicitation,omitempty"`
Expand Down
21 changes: 21 additions & 0 deletions java/src/main/java/com/github/copilot/CopilotSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ public final class CopilotSession implements AutoCloseable {
private static final Logger LOG = Logger.getLogger(CopilotSession.class.getName());
private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper();

/**
* Fixed name of the runtime's built-in tool-search tool. A client can replace
* its behavior by registering a tool with this exact name and
* {@code overridesBuiltInTool} set to {@code true}.
*/
private static final String TOOL_SEARCH_TOOL_NAME = "tool_search_tool";

/**
* The current active session ID. Initialized to the pre-generated value and may
* be updated after session.create / session.resume if the server returns a
Expand Down Expand Up @@ -912,6 +919,20 @@ private void executeToolAndRespondAsync(String requestId, String toolName, Strin
var invocation = new com.github.copilot.rpc.ToolInvocation().setSessionId(sessionId)
.setToolCallId(toolCallId).setToolName(toolName).setArguments(argumentsNode);

// The built-in tool-search tool receives a snapshot of the session's
// currently initialized tools so an override can filter the live
// catalog without issuing its own RPC. Fetch it only for that tool to
// avoid a round-trip on every tool call; a failed fetch leaves the
// snapshot null rather than failing the tool.
if (TOOL_SEARCH_TOOL_NAME.equals(toolName)) {
try {
var metadata = getRpc().tools.getCurrentMetadata().join();
invocation.setAvailableTools(metadata.tools());
} catch (Exception e) {
LOG.log(Level.FINE, "Failed to fetch tool metadata for tool search", e);
}
}

tool.handler().invoke(invocation).thenAccept(result -> {
try {
ToolResultObject toolResult;
Expand Down
21 changes: 21 additions & 0 deletions java/src/main/java/com/github/copilot/RpcHandlerDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ final class RpcHandlerDispatcher {
private static final Logger LOG = Logger.getLogger(RpcHandlerDispatcher.class.getName());
private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper();

/**
* Fixed name of the runtime's built-in tool-search tool. A client can replace
* its behavior by registering a tool with this exact name and
* {@code overridesBuiltInTool} set to {@code true}.
*/
private static final String TOOL_SEARCH_TOOL_NAME = "tool_search_tool";

private final Map<String, CopilotSession> sessions;
private final LifecycleEventDispatcher lifecycleDispatcher;
private final Executor executor;
Expand Down Expand Up @@ -162,6 +169,20 @@ private void handleToolCall(JsonRpcClient rpc, String requestId, JsonNode params
var invocation = new ToolInvocation().setSessionId(sessionId).setToolCallId(toolCallId)
.setToolName(toolName).setArguments(arguments);

// The built-in tool-search tool receives a snapshot of the session's
// currently initialized tools so an override can filter the live
// catalog without issuing its own RPC. Fetch it only for that tool to
// avoid a round-trip on every tool call; a failed fetch leaves the
// snapshot null rather than failing the tool.
if (TOOL_SEARCH_TOOL_NAME.equals(toolName)) {
Comment thread
almaleksia marked this conversation as resolved.
Outdated
try {
var metadata = session.getRpc().tools.getCurrentMetadata().join();
invocation.setAvailableTools(metadata.tools());
} catch (Exception e) {
LOG.log(Level.FINE, "Failed to fetch tool metadata for tool search", e);
}
}

tool.handler().invoke(invocation).thenAccept(result -> {
try {
ToolResultObject toolResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
request.setInstructionDirectories(config.getInstructionDirectories());
request.setPluginDirectories(config.getPluginDirectories());
request.setLargeOutput(config.getLargeOutput());
request.setToolSearch(config.getToolSearch());
request.setMemory(config.getMemory());
request.setDisabledSkills(config.getDisabledSkills());
request.setConfigDirectory(config.getConfigDirectory());
Expand Down Expand Up @@ -280,6 +281,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
request.setInstructionDirectories(config.getInstructionDirectories());
request.setPluginDirectories(config.getPluginDirectories());
request.setLargeOutput(config.getLargeOutput());
request.setToolSearch(config.getToolSearch());
request.setMemory(config.getMemory());
request.setDisabledSkills(config.getDisabledSkills());
request.setInfiniteSessions(config.getInfiniteSessions());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ public final class CreateSessionRequest {
@JsonProperty("largeOutput")
private LargeToolOutputConfig largeOutput;

@JsonProperty("toolSearch")
private ToolSearchConfig toolSearch;

@JsonProperty("memory")
private MemoryConfiguration memory;

Expand Down Expand Up @@ -620,6 +623,16 @@ public void setLargeOutput(LargeToolOutputConfig largeOutput) {
this.largeOutput = largeOutput;
}

/** Gets tool-search config. @return the tool-search config */
public ToolSearchConfig getToolSearch() {
return toolSearch;
}

/** Sets tool-search config. @param toolSearch the tool-search config */
public void setToolSearch(ToolSearchConfig toolSearch) {
this.toolSearch = toolSearch;
}

/** Gets memory config. @return the memory config */
public MemoryConfiguration getMemory() {
return memory;
Expand Down
Loading
Loading