












@@ -874,4 +874,148 @@ describe("Tool Search", () => {
874874expect(observedSignal.aborted).toBe(true);
875875expect(abortCount).toBe(1);
876876});
877+878+it("reuses an unchanged catalog within the same run", () => {
879+const codeTool = fakeTool(TOOL_SEARCH_CODE_MODE_TOOL_NAME, "code mode");
880+const alpha = pluginTool("fake_reuse_alpha", "Alpha tool");
881+const beta = pluginTool("fake_reuse_beta", "Beta tool");
882+const config = { tools: { toolSearch: true } } as never;
883+const sessionId = "session-catalog-reuse";
884+885+const first = applyToolSearchCatalog({
886+tools: [codeTool, alpha, beta],
887+ config,
888+ sessionId,
889+});
890+expect(first.catalogRegistered).toBe(true);
891+expect(first.catalogReused).toBe(false);
892+893+const catalogAfterFirst = testing.sessionCatalogs.get(`session:${sessionId}`);
894+expect(catalogAfterFirst).toBeDefined();
895+896+const second = applyToolSearchCatalog({
897+tools: [codeTool, alpha, beta],
898+ config,
899+ sessionId,
900+});
901+expect(second.catalogRegistered).toBe(true);
902+expect(second.catalogReused).toBe(true);
903+expect(testing.sessionCatalogs.get(`session:${sessionId}`)).toBe(catalogAfterFirst);
904+905+const laterRef = createToolSearchCatalogRef();
906+const later = applyToolSearchCatalog({
907+tools: [codeTool, alpha, beta],
908+ config,
909+ sessionId,
910+sessionKey: "agent:main:tool-search-reuse",
911+catalogRef: laterRef,
912+});
913+expect(later.catalogReused).toBe(true);
914+expect(laterRef.current).toBe(catalogAfterFirst);
915+expect(testing.sessionCatalogs.get("key:agent:main:tool-search-reuse")).toBe(catalogAfterFirst);
916+});
917+918+it("restores an unchanged catalog after run cleanup", () => {
919+const codeTool = fakeTool(TOOL_SEARCH_CODE_MODE_TOOL_NAME, "code mode");
920+const alpha = pluginTool("fake_xrun_alpha", "Alpha tool");
921+const beta = pluginTool("fake_xrun_beta", "Beta tool");
922+const config = { tools: { toolSearch: true } } as never;
923+const sessionId = "session-cross-run-reuse";
924+const firstRef = createToolSearchCatalogRef();
925+926+const first = applyToolSearchCatalog({
927+tools: [codeTool, alpha, beta],
928+ config,
929+ sessionId,
930+runId: "run-1",
931+catalogRef: firstRef,
932+});
933+expect(first.catalogReused).toBe(false);
934+const firstAlphaEntry = firstRef.current?.entries.find((entry) => entry.name === alpha.name);
935+expect(firstAlphaEntry).toBeDefined();
936+937+clearToolSearchCatalog({
938+ sessionId,
939+runId: "run-1",
940+catalogRef: firstRef,
941+});
942+expect(firstRef.current).toBeUndefined();
943+expect(testing.sessionCatalogs.has("run:run-1")).toBe(false);
944+945+const secondRef = createToolSearchCatalogRef();
946+const second = applyToolSearchCatalog({
947+tools: [codeTool, alpha, beta],
948+ config,
949+ sessionId,
950+runId: "run-2",
951+catalogRef: secondRef,
952+});
953+expect(second.catalogRegistered).toBe(true);
954+expect(second.catalogReused).toBe(true);
955+expect(testing.sessionCatalogs.has("run:run-2")).toBe(true);
956+expect(secondRef.current?.entries.find((entry) => entry.name === alpha.name)).toBe(
957+firstAlphaEntry,
958+);
959+});
960+961+it("does not reuse when a same-named tool uses a different executable", () => {
962+const codeTool = fakeTool(TOOL_SEARCH_CODE_MODE_TOOL_NAME, "code mode");
963+const original = pluginTool("fake_exec_swap", "Stable description");
964+const config = { tools: { toolSearch: true } } as never;
965+const sessionId = "session-tool-exec-change";
966+const firstRef = createToolSearchCatalogRef();
967+968+applyToolSearchCatalog({
969+tools: [codeTool, original],
970+ config,
971+ sessionId,
972+runId: "run-exec-1",
973+catalogRef: firstRef,
974+});
975+clearToolSearchCatalog({
976+ sessionId,
977+runId: "run-exec-1",
978+catalogRef: firstRef,
979+});
980+981+const replacement = pluginTool("fake_exec_swap", "Stable description");
982+const secondRef = createToolSearchCatalogRef();
983+const second = applyToolSearchCatalog({
984+tools: [codeTool, replacement],
985+ config,
986+ sessionId,
987+runId: "run-exec-2",
988+catalogRef: secondRef,
989+});
990+expect(second.catalogReused).toBe(false);
991+expect(secondRef.current?.entries.find((entry) => entry.name === replacement.name)?.tool).toBe(
992+replacement,
993+);
994+});
995+996+it("does not reuse when a same-named tool changes parameters", () => {
997+const codeTool = fakeTool(TOOL_SEARCH_CODE_MODE_TOOL_NAME, "code mode");
998+const tool = pluginTool("fake_schema_swap", "Stable description");
999+const config = { tools: { toolSearch: true } } as never;
1000+const sessionId = "session-tool-schema-change";
1001+1002+applyToolSearchCatalog({
1003+tools: [codeTool, tool],
1004+ config,
1005+ sessionId,
1006+});
1007+tool.parameters = {
1008+type: "object",
1009+properties: {
1010+other: { type: "number" },
1011+},
1012+};
1013+1014+const second = applyToolSearchCatalog({
1015+tools: [codeTool, tool],
1016+ config,
1017+ sessionId,
1018+});
1019+expect(second.catalogReused).toBe(false);
1020+});
8771021});
이 콘텐츠는 인셔셔RSS(RSS 리더)가 자동으로 집계한 것으로 읽기 참고용입니다. 원문 출처 — 저작권은 원저작자에게 있습니다.