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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions