Custom Agent Inference Control and Tool Scoping in the Copilot SDK

The Copilot SDK enables precise control over custom agent selection through the infer flag while restricting LLM tool access via tools and tool_choice parameters, allowing developers to orchestrate when agents participate in inference and what capabilities they expose.

The github/copilot-sdk repository provides a flexible framework for extending GitHub Copilot with custom agents that can execute specialized logic and interact with language models. Mastering custom agent inference control and tool scoping is essential for building secure, predictable AI workflows that only invoke specific agents when intended and limit their operational surface area to predefined tools.

Understanding Inference Control with the infer Flag

The infer property in AgentConfig determines whether the Copilot runtime may automatically select a custom agent for a given turn or requires explicit invocation.

How the Runtime Evaluates Agent Selection

When a user prompt arrives, the runtime builds a session turn containing the user message, system context, and evaluation criteria for registered agents. According to the source code in nodejs/src/session.ts, the runtime checks the infer boolean on each AgentConfig during the selection phase:

  • infer: true (default): The runtime may automatically select the agent when user prompts match the agent's trigger regex or when the model decides the agent should handle the request.
  • infer: false: Disables auto-selection. The agent can only be invoked explicitly through tool calls or user-directed requests.

This logic is implemented in the session helper's runInference method, which reads the agent's configuration before constructing the inference request.

Configuration in AgentConfig

The AgentConfig interface is defined in nodejs/src/types.ts for TypeScript and mirrored in go/types.go for Go implementations. The configuration includes:

interface AgentConfig {
  name: string;
  infer?: boolean;        // Defaults to true
  trigger?: string;       // Optional regex for auto-selection
  tools?: ToolDefinition[];
  tool_choice?: "auto" | "none" | { type: "function"; name: string };
}

When infer is set to false, optional triggers like trigger: "search" are ignored by the auto-selection logic, effectively making the agent available only through explicit tool invocations.

Implementing Tool Scoping with tools and tool_choice

Custom agents can expose restricted tool sets that limit what functions the LLM may call during inference, preventing unauthorized access to capabilities outside the agent's scope.

Defining Tool Sets for Custom Agents

The tools array contains JSON-schema definitions for each function the agent makes available. In nodejs/src/types.ts, these are defined as ToolDefinition objects. The SDK automatically serializes Zod-like schemas from nodejs/src/toolSet.ts into the proper format for the LLM provider.

A typical agent configuration restricts the available tools like this:

{
  "name": "knowledge-base-agent",
  "description": "Agent that searches internal documentation",
  "infer": false,
  "tools": [
    {
      "name": "searchKB",
      "description": "Search the internal knowledge base",
      "parameters": {
        "type": "object",
        "properties": {
          "query": { "type": "string" }
        },
        "required": ["query"]
      }
    }
  ]
}

The file is parsed by the SDK loader in nodejs/src/extension.ts and converted into an AgentConfig object at runtime.

Controlling Tool Selection Behavior

The tool_choice parameter dictates the model's willingness to invoke tools, passed directly to the provider following the OpenAI/Anthropic tool-calling contract:

  • "auto": The model may select any tool from the provided list.
  • "none": The model is forbidden from calling any tools.
  • { type: "function", name: "searchKB" }: Forces the model to use the specific named tool.

The response adapter in test/harness/responsesApiAdapter.ts (lines 23-55) normalizes these parameters through the convertResponsesTools and convertResponsesToolChoice functions, ensuring the provider's response is mapped back to the session's internal message format.

Architecture of the Inference Pipeline

Understanding how the SDK wires configuration into RPC calls reveals how inference control and tool scoping enforce boundaries at the network level.

Session Helper and Request Construction

The runInference method in nodejs/src/session.ts serves as the orchestration layer. When processing a turn, it:

  1. Evaluates agent selection criteria based on the infer flag.
  2. Merges the selected agent's tools and tool_choice into the request payload.
  3. Hands the constructed request to the RPC client.

This ensures that tool scoping is enforced immediately before the network request, preventing tool leakage across agent boundaries.

RPC Payload Generation

The actual inference request follows the CreateMessageRequest schema defined in nodejs/src/generated/rpc.ts (lines 11531-11598). The relevant fields include:

  • modelName: The target LLM identifier.
  • tools: Array of tool specifications (only present when the active agent declares tools).
  • tool_choice: The restriction hint passed to the model.

The payload construction logic in responsesApiAdapter.ts demonstrates how the SDK injects these fields into the JSON-RPC request sent to the Copilot backend, ensuring that providers receive exactly the tool set and constraints defined by the active agent.

Practical Implementation Examples

Defining a TypeScript Custom Agent

Create an agent descriptor file that disables auto-inference and scopes tools to a single search function:

// plugins/agents/kb-agent.md
{
  "name": "kb-agent",
  "description": "Knowledge base search agent",
  "infer": false,
  "tools": [
    {
      "name": "searchKB",
      "description": "Search internal documentation",
      "parameters": {
        "type": "object",
        "properties": {
          "query": { "type": "string", "description": "Search terms" }
        },
        "required": ["query"]
      }
    }
  ],
  "tool_choice": { "type": "function", "name": "searchKB" }
}

Invoking the Agent from Application Code

Since infer is disabled, explicitly call the agent via tool syntax:

import { createSession } from '@github/copilot-sdk';

const session = await createSession({ pluginDir: './plugins' });

const result = await session.run(`
  <tool name="searchKB">
    {"query": "Custom agent inference control"}
  </tool>
`);

console.log(result);

The request payload automatically includes only the searchKB tool definition and forces the model to use it via the tool_choice parameter.

Handling Tool Calls in Agent Logic

Implement the handler that executes when the LLM invokes the scoped tool:

// plugins/agents/kb-agent.handler.ts
export async function handleToolCall(name: string, args: any) {
  if (name === 'searchKB') {
    const results = await documentationIndex.search(args.query);
    return { result: results.slice(0, 5) };
  }
  throw new Error(`Tool ${name} not in agent scope`);
}

The SDK registers this handler automatically when loading the plugin directory.

Runtime Configuration in Go

The Go SDK mirrors the TypeScript behavior, allowing dynamic configuration of inference control:

import "github.com/github/copilot-sdk/go/copilot"

cfg := copilot.NewSessionConfig().
    WithCustomAgent(&copilot.AgentConfig{
        Name:  "kb-agent",
        Infer: false,
        Tools: searchToolSet,
    })

session, _ := copilot.NewSession(cfg)
resp, _ := session.RunPrompt(`
  <tool name="searchKB">
    {"query": "tool scoping semantics"}
  </tool>
`)

The AgentConfig struct in go/types.go and tool serialization in go/toolset.go ensure parity with the Node.js implementation.

Summary

  • Inference control via the infer flag in AgentConfig determines whether the Copilot runtime auto-selects custom agents or requires explicit invocation, defined in nodejs/src/types.ts and go/types.go.
  • Tool scoping restricts LLM capabilities through the tools array and tool_choice parameter, preventing unauthorized tool access beyond the agent's defined surface area.
  • The session helper in nodejs/src/session.ts merges these configurations into CreateMessageRequest payloads defined in nodejs/src/generated/rpc.ts.
  • Response adapters normalize tool schemas and choices between the SDK's internal format and provider-specific JSON-RPC formats.

Frequently Asked Questions

What is the difference between infer true and false in Copilot SDK custom agents?

When infer is true (the default), the Copilot runtime may automatically select the agent for a turn if the user prompt matches the agent's trigger regex or if the model determines the agent should handle the request. When set to false, auto-selection is disabled and the agent can only be invoked explicitly through tool calls or direct user commands, providing strict control over agent activation.

How does tool_choice restrict LLM behavior in Copilot SDK?

The tool_choice parameter controls the model's tool-calling freedom by accepting "auto" (model may choose any provided tool), "none" (model cannot call tools), or a specific function object like { type: "function", name: "searchKB" } which forces the model to use that exact tool. This is enforced in the RPC payload generated by test/harness/responsesApiAdapter.ts before transmission to the LLM provider.

Where is the AgentConfig interface defined in the Copilot SDK source code?

The AgentConfig interface is defined in nodejs/src/types.ts for TypeScript implementations and mirrored in go/types.go for Go implementations. These files specify the shape of agent configuration including the infer boolean, tools array, and tool_choice union type.

Can I change inference control settings at runtime in the Copilot SDK?

Yes, inference control settings are configured when constructing session objects. In TypeScript, pass the configuration when calling createSession(). In Go, use copilot.NewSessionConfig().WithCustomAgent() to specify AgentConfig with Infer: false before instantiating the session. Once a session is active, the configuration remains fixed for that session's lifecycle.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →