fix(tool-search): avoid blocking known group activation

This commit is contained in:
云服务部-叶林立
2026-08-27 10:13:52 +08:00
parent 874d4da096
commit bf874455db
7 changed files with 112 additions and 22 deletions
+26 -13
View File
@@ -31,10 +31,25 @@ import {
import { ensureToolSearchDefaults, readToolSearchConfig } from "./config.ts";
const TOOL_SEARCH_NAME = "tool_search";
const POWERSHELL_TOOL_NAME = "powershell";
const CORE_TOOLS = ["read", "write", "edit", "bash", "grep", "find"];
type CatalogSource = "bundle" | "cache" | "fallback" | "hybrid" | "model";
type ModelUsage = Awaited<ReturnType<ExtensionContext["modelRegistry"]["complete"]>>["usage"];
type CatalogGenerationResult = { notice?: string; usage?: ModelUsage };
export function platformToolPolicy<T extends { name: string }>(
definitions: readonly T[],
platform: NodeJS.Platform,
): { tools: T[]; coreTools: string[] } {
const tools = platform === "win32"
? [...definitions]
: definitions.filter((tool) => tool.name !== POWERSHELL_TOOL_NAME);
const coreTools = platform === "win32"
? [...CORE_TOOLS, POWERSHELL_TOOL_NAME]
: [...CORE_TOOLS];
return { tools, coreTools };
}
function unique(values: Iterable<string>): string[] {
return [...new Set(values)];
@@ -191,20 +206,14 @@ export default function toolSearchExtension(pi: ExtensionAPI): void {
}),
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
const requestedId = params.group?.trim().toLowerCase();
const preGenerationGroup = requestedId ? groupById(requestedId) : undefined;
const generation = await ensureModelCatalog(ctx, signal);
let selected = requestedId ? groupById(requestedId) : undefined;
const generation: CatalogGenerationResult = selected
? {}
: await ensureModelCatalog(ctx, signal);
const lines: string[] = [];
if (generation.notice) lines.push(generation.notice);
let selected = requestedId ? groupById(requestedId) : undefined;
if (!selected && preGenerationGroup) {
const previousNames = new Set(preGenerationGroup.tools);
selected = [...catalog.groups]
.map((group) => ({ group, overlap: group.tools.filter((name) => previousNames.has(name)).length }))
.sort((left, right) => right.overlap - left.overlap || left.group.id.localeCompare(right.group.id))
.find((candidate) => candidate.overlap > 0)?.group;
if (selected) lines.push(`Mapped initial fallback group ${preGenerationGroup.id} to generated group ${selected.id}.`);
}
if (!selected && requestedId) selected = groupById(requestedId);
const ranked = params.query ? rankGroups(catalog, params.query).slice(0, 3) : [];
if (!selected && params.query && (ranked[0]?.score ?? 0) > 0) selected = ranked[0]?.group;
@@ -338,9 +347,13 @@ export default function toolSearchExtension(pi: ExtensionAPI): void {
function refreshState(ctx: Pick<ExtensionContext, "ui">, forceReset: boolean): void {
const nextConfig = readToolSearchConfig(agentDir);
const allTools = pi.getAllTools().filter((tool) => tool.name !== TOOL_SEARCH_NAME);
const registeredTools = pi.getAllTools().filter((tool) => tool.name !== TOOL_SEARCH_NAME);
const platformPolicy = platformToolPolicy(registeredTools, process.platform);
const allTools = platformPolicy.tools;
const availableNames = new Set(allTools.map((tool) => tool.name));
const nextPinned = new Set([...CORE_TOOLS, ...nextConfig.alwaysEnabled].filter((name) => availableNames.has(name)));
const nextPinned = new Set(
[...platformPolicy.coreTools, ...nextConfig.alwaysEnabled].filter((name) => availableNames.has(name)),
);
const hiddenTools = allTools.filter((tool) => !nextPinned.has(tool.name));
const nextConstraints = {
maxToolsPerGroup: Math.min(nextConfig.maxToolsPerGroup, nextConfig.maxDynamicTools),