Skip to content

AI: CustomAgentConfig.Tools not enforced when agent pre-selected via SessionConfig.Agent #859

@KnicKnic

Description

@KnicKnic

Bug

When an agent is pre-selected at session creation via SessionConfig.Agent, the agent's Tools list is not enforced — the model sees all session tools instead of only the tools declared in CustomAgentConfig.Tools.

Note: This works correctly when the agent is selected post-creation via Rpc.Agent.SelectAsync() (tested separately).

Versions

  • SDK: GitHub.Copilot.SDK 0.1.33-preview.0
  • CLI: GitHub Copilot CLI 1.0.5
  • Runtime: .NET 8.0

Expected Behavior

When SessionConfig.Agent = "restricted" and restricted.Tools = ["view"], the model should only see the view tool.

Actual Behavior

The model sees all 17 session tools (powershell, write_powershell, read_powershell, stop_powershell, list_powershell, view, create, edit, web_fetch, report_intent, skill, sql, read_agent, list_agents, grep, glob, task) regardless of the agent's Tools constraint.

Repro Output

restricted.Tools   = [view]
unrestricted.Tools = <null> (all session tools)

Creating session with SessionConfig.Agent = 'restricted'...
(No post-creation Rpc.Agent.SelectAsync call)
Session created.

Asking model to list its tools...

--- Model Response ---
powershell, write_powershell, read_powershell, stop_powershell, list_powershell,
 view, create, edit, web_fetch, report_intent, skill, sql, read_agent, list_agents, grep, glob, task
--- End Response ---

Agent 'restricted' Tools: [view]
Tools model reported: 17
Extra tools (should be 0): 14

Extra tools the model sees (should NOT be visible):
  - powershell
  - write_powershell
  - read_powershell
  - stop_powershell
  - list_powershell
  - create
  - edit
  - web_fetch
  - sql
  - read_agent
  - list_agents
  - grep
  - glob
  - task

CustomAgentConfig.Tools is NOT enforced by the CLI.

Self-Contained Repro Code

using GitHub.Copilot.SDK;

public class AgentToolScopingSessionAgent
{
    public async Task RunAsync(string cliPath)
    {
        var restricted = new CustomAgentConfig
        {
            Name = "restricted",
            Description = "Agent with only 1 tool",
            Prompt = "You are a restricted agent. You should only have access to 'view' tool.",
            Tools = new List<string> { "view" }
        };

        var unrestricted = new CustomAgentConfig
        {
            Name = "unrestricted",
            Description = "Agent with all tools",
            Prompt = "You are an unrestricted agent with access to everything."
        };

        await using var client = new CopilotClient(new CopilotClientOptions { CliPath = cliPath });
        await client.StartAsync();

        // Pre-select via SessionConfig.Agent — no post-creation SelectAsync
        var sessionConfig = new SessionConfig
        {
            Model = "gpt5-mini",
            CustomAgents = new List<CustomAgentConfig> { restricted, unrestricted },
            Agent = "restricted",
            OnPermissionRequest = PermissionHandler.ApproveAll,
        };

        await using var session = await client.CreateSessionAsync(sessionConfig);

        // Ask the model what tools it sees
        var done = new TaskCompletionSource();
        string? content = null;
        using var sub = session.On(evt =>
        {
            switch (evt)
            {
                case AssistantMessageEvent msg: content = msg.Data.Content; break;
                case SessionIdleEvent: done.TrySetResult(); break;
                case SessionErrorEvent err: done.TrySetException(new Exception(err.Data.Message)); break;
            }
        });
        await session.SendAsync(new MessageOptions
        {
            Prompt = "List every tool name you have access to. Output ONLY a comma-separated list."
        });
        await done.Task;

        Console.WriteLine($"Agent 'restricted' Tools: [view]");
        Console.WriteLine($"Model sees: {content}");
        // BUG: Model reports all session tools, not just "view"
    }
}

Notes

  • Rpc.Agent.SelectAsync("restricted") does correctly enforce Tools — only the SessionConfig.Agent preselect path is broken.
  • This forces SDK consumers to avoid SessionConfig.Agent and always use Rpc.Agent.SelectAsync() as a workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions