Skip to content

Polish tool picker with toolsets #252054

Description

@roblourens

We should be able to see the tools for a toolset. This needs quickpick support. Related: #250638 and #250676

Activity

  1. added this to the June 2025 milestone on Jun 21, 2025
  2. TylerLeonhardt commented on Jun 27, 2025

    @TylerLeonhardt
    Member

    Here's what I'm cooking in #252662

    Image

  3. TylerLeonhardt commented on Jul 28, 2025

    @TylerLeonhardt
    Member

    Fixed in #257748

  4. TylerLeonhardt commented on Aug 6, 2025

    @TylerLeonhardt
    Member

    I'm reopening this due to #259920 which I "fixed" by not showing tools under toolsets.

    While investigating this candidate, I realized that this is more complicated to implement because the actual data model itself only cares about ToolSet enablement and not the enablement of a ToolSet's tools:

    /**
    * All tools and tool sets with their enabled state.
    */
    get entriesMap(): IObservable<IToolAndToolSetEnablementMap> {
    return derived(r => {
    const map = new Map<IToolData | ToolSet, boolean>();
    const currentMode = this._mode.read(r);
    let currentMap = this._sessionStates.get(currentMode.id);
    const modeTools = currentMode.customTools?.read(r);
    if (!currentMap && currentMode.kind === ChatModeKind.Agent && modeTools) {
    currentMap = this._toolsService.toToolAndToolSetEnablementMap(modeTools);
    }
    if (currentMap) {
    for (const tool of this._allTools.read(r)) {
    if (tool.canBeReferencedInPrompt) {
    map.set(tool, currentMap.get(tool) === true); // false if not present
    }
    }
    for (const toolSet of this._toolsService.toolSets.read(r)) {
    map.set(toolSet, currentMap.get(toolSet) === true); // false if not present
    }
    } else {
    const currData = this._selectedTools.read(r);
    const disabledToolSets = new Set(currData.disabledToolSets ?? []);
    const disabledTools = new Set(currData.disabledTools ?? []);
    for (const tool of this._allTools.read(r)) {
    if (tool.canBeReferencedInPrompt) {
    map.set(tool, !disabledTools.has(tool.id));
    }
    }
    for (const toolSet of this._toolsService.toolSets.read(r)) {
    map.set(toolSet, !disabledToolSets.has(toolSet.id));
    }
    }
    return map;
    });
    }

    export type IToolAndToolSetEnablementMap = ReadonlyMap<IToolData | ToolSet, boolean>;

    So if a ToolSet is enabled, the children tools inherit that. They are all enabled. If the ToolSet is disabled, then the children are disabled as well. This caused a bug where you would disable 1 tool in a toolset and it would disable the entire ToolSet which would disable the rest of the tools in the ToolSet.

    I tried to make a change for a candidate that would allow this to work, but it was getting too large, and I also realized that this disablement of ToolSets is stored in a Memento:

    /**
    * New tools and new tool sources that come in should generally be enabled until
    * the user disables them. To store things, we store only the tool sets and
    * individual tools that were disabled, so the new data sources that come in
    * are enabled, and new tools that come in for data sources not disabled are
    * also enabled.
    */
    type StoredData = {
    readonly disabledToolSets?: readonly string[];
    readonly disabledTools?: readonly string[];
    };
    export enum ToolsScope {
    Global,
    Session,
    Mode
    }
    export class ChatSelectedTools extends Disposable {
    private readonly _selectedTools: ObservableMemento<StoredData>;

    Proposal:

    I think that a ToolSet's enablement should be derived from its tools' enablement. In other words, if there is any tool that is enabled in a toolset, then the ToolSet is enabled. If none of its tools are enabled, then it is disabled.

  5. TylerLeonhardt commented on Aug 6, 2025

    @TylerLeonhardt
    Member

    I think at this point, I hand this over to Martin Aeschlimann (@aeschli) as the owner of the model.

  6. added
    unreleasedPatch has not yet been released in VS Code Insiders
    insiders-releasedPatch has been released in VS Code Insiders
    and removed
    unreleasedPatch has not yet been released in VS Code Insiders
    on Aug 21, 2025
  7. locked and limited conversation to collaborators on Oct 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions