feat(tool-search): expand dynamic group activation

This commit is contained in:
云服务部-叶林立
2026-08-28 12:05:11 +08:00
parent bf874455db
commit 2dcec0207c
15 changed files with 897 additions and 119 deletions
+65 -7
View File
@@ -10,6 +10,7 @@ import toolSearchExtension, { platformToolPolicy } from "../extensions/index.ts"
interface ToolSearchResult {
details: {
loadedGroup?: string;
loadedGroups: string[];
evictedGroups: string[];
activeGroups: string[];
catalogSource: string;
@@ -20,7 +21,7 @@ interface ToolSearchResult {
interface RegisteredTool {
execute(
id: string,
params: { group?: string; query?: string },
params: { group?: string; groups?: string[]; query?: string },
signal: AbortSignal | undefined,
onUpdate: undefined,
ctx: ExtensionContext,
@@ -67,6 +68,9 @@ test("loads whole groups and evicts the least-recently-used group", async () =>
const handlers = new Map<string, (...args: any[]) => unknown>();
const registered = new Map<string, RegisteredTool>();
const activeCalls: string[][] = [];
const commands = new Map<string, (...args: any[]) => unknown>();
const notifications: string[] = [];
let failActivation = false;
const sourceTools = [
sourceTool("read", "Read a file"),
sourceTool("bash", "Run a command"),
@@ -85,13 +89,22 @@ test("loads whole groups and evicts the least-recently-used group", async () =>
const tool = definition as RegisteredTool & { name: string };
registered.set(tool.name, tool);
},
registerCommand: () => {},
setActiveTools: (names: string[]) => activeCalls.push([...names]),
registerCommand: (name: string, definition: { handler: (...args: any[]) => unknown }) => commands.set(name, definition.handler),
setActiveTools: (names: string[]) => {
if (failActivation) throw new Error("simulated setActiveTools failure");
activeCalls.push([...names]);
},
on: (event: string, handler: (...args: any[]) => unknown) => handlers.set(event, handler),
} as unknown as ExtensionAPI;
toolSearchExtension(api);
const ctx = testContext();
const ctx = testContext({
hasUI: true,
ui: {
setStatus: () => {},
notify: (message: string) => notifications.push(message),
} as unknown as ExtensionContext["ui"],
});
handlers.get("session_start")?.({}, ctx);
assert.ok(activeCalls.at(-1)?.includes("tool_search"));
@@ -113,6 +126,36 @@ test("loads whole groups and evicts the least-recently-used group", async () =>
assert.ok(activeCalls.at(-1)?.includes("alpha_one"));
assert.ok(activeCalls.at(-1)?.includes("gamma_one"));
assert.ok(!activeCalls.at(-1)?.includes("beta_one"));
const batch = await tool?.execute("call-4", { groups: ["beta", "gamma"] }, undefined, undefined, ctx);
assert.deepEqual(batch?.details.loadedGroups, ["beta", "gamma"]);
assert.deepEqual(batch?.details.evictedGroups, ["alpha"]);
assert.deepEqual(batch?.details.activeGroups, ["gamma", "beta"]);
const rejected = await tool?.execute(
"call-5",
{ groups: ["alpha", "beta", "gamma"] },
undefined,
undefined,
ctx,
);
assert.deepEqual(rejected?.details.loadedGroups, []);
assert.deepEqual(rejected?.details.activeGroups, ["gamma", "beta"]);
await commands.get("tool-search-status")?.("", ctx);
assert.match(notifications.at(-1) ?? "", /Dynamic groups: 2\/2/);
assert.match(notifications.at(-1) ?? "", /LRU oldest → newest: beta → gamma/);
assert.match(notifications.at(-1) ?? "", /Last eviction: alpha \(group cap 2\)/);
failActivation = true;
await assert.rejects(
tool!.execute("call-6", { groups: ["alpha", "gamma"] }, undefined, undefined, ctx),
/simulated setActiveTools failure/,
);
failActivation = false;
await commands.get("tool-search-status")?.("", ctx);
assert.match(notifications.at(-1) ?? "", /LRU oldest → newest: beta → gamma/);
assert.match(notifications.at(-1) ?? "", /Last eviction: alpha \(group cap 2\)/);
} finally {
if (previousAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR;
else process.env.PI_CODING_AGENT_DIR = previousAgentDir;
@@ -203,11 +246,15 @@ test("uses the precomputed bundle groups without calling a model", async () => {
try {
const handlers = new Map<string, (...args: any[]) => unknown>();
const registered = new Map<string, RegisteredTool>();
const activeCalls: string[][] = [];
let modelCalls = 0;
const sourceTools = [
sourceTool("read", "Read a file"),
sourceTool("tavily_web_search", "Search broadly for current web sources"),
sourceTool("tavily_web_fetch", "Fetch a selected Tavily source"),
sourceTool("ctx_execute", "Run commands in a context sandbox"),
sourceTool("ctx_execute_file", "Analyze a file in a context sandbox"),
sourceTool("ctx_batch_execute", "Run a bounded command batch"),
];
const api = {
getAllTools: () => sourceTools,
@@ -216,7 +263,7 @@ test("uses the precomputed bundle groups without calling a model", async () => {
registered.set(tool.name, tool);
},
registerCommand: () => {},
setActiveTools: () => {},
setActiveTools: (names: string[]) => activeCalls.push([...names]),
on: (event: string, handler: (...args: any[]) => unknown) => handlers.set(event, handler),
} as unknown as ExtensionAPI;
toolSearchExtension(api);
@@ -231,10 +278,21 @@ test("uses the precomputed bundle groups without calling a model", async () => {
} as unknown as ExtensionContext["modelRegistry"],
});
handlers.get("session_start")?.({}, ctx);
assert.ok(["ctx_execute", "ctx_execute_file", "ctx_batch_execute"].every((name) => activeCalls.at(-1)?.includes(name)));
const ambiguous = await registered.get("tool_search")?.execute(
"call-1",
{ query: "web search" },
undefined,
undefined,
ctx,
);
assert.equal(ambiguous?.details.loadedGroup, undefined);
assert.deepEqual(ambiguous?.details.activeGroups, []);
const result = await registered.get("tool_search")?.execute(
"call-1",
{ group: "web-tavily" },
"call-2",
{ query: "tavily_web_search" },
undefined,
undefined,
ctx,