| ... | @@ -111,18 +111,40 @@ describe("Pi MCP tool results", () => { | ... | @@ -111,18 +111,40 @@ describe("Pi MCP tool results", () => { |
| 111 | }); | 111 | }); |
| 112 | | 112 | |
| 113 | describe("Pi MCP tool exposure", () => { | 113 | describe("Pi MCP tool exposure", () => { |
| | 114 | const discoveryContext = { |
| | 115 | model: { api: "anthropic-messages" }, |
| | 116 | ui: { notify: () => undefined }, |
| | 117 | }; |
| | 118 | |
| | 119 | it.each(["session_start", "session_tree", "model_select", "before_agent_start"])( |
| | 120 | "declares optional T3 tools for OpenAI-compatible gateways at %s", |
| | 121 | async (hookName) => { |
| | 122 | const bridge = await loadMcpBridge({ modern: true, toolSearchAvailable: true }); |
| | 123 | const hook = bridge.handlers.get(hookName); |
| | 124 | assert.isDefined(hook); |
| | 125 | await hook!( |
| | 126 | { systemPrompt: "Pi system prompt" }, |
| | 127 | { model: { api: "openai-completions" }, ui: { notify: assert.fail } }, |
| | 128 | ); |
| | 129 | assert.isTrue( |
| | 130 | bridge.tools |
| | 131 | .filter((tool) => tool.name.startsWith("mcp__t3-code__")) |
| | 132 | .every((tool) => bridge.getActiveTools().includes(tool.name)), |
| | 133 | ); |
| | 134 | const result = await bridge.tools |
| | 135 | .find((tool) => tool.name === "mcp__t3-code__preview_snapshot")! |
| | 136 | .execute("gateway-call", {}); |
| | 137 | assert.equal(result.content[0]?.text, "browser snapshot"); |
| | 138 | assert.equal(bridge.tools.filter((tool) => tool.exposure === "hidden").length, 4); |
| | 139 | }, |
| | 140 | ); |
| | 141 | |
| 114 | it("keeps orchestration direct and optional bridge tools discoverable on modern Pi", async () => { | 142 | it("keeps orchestration direct and optional bridge tools discoverable on modern Pi", async () => { |
| 115 | const bridge = await loadMcpBridge({ modern: true, toolSearchAvailable: true }); | 143 | const bridge = await loadMcpBridge({ modern: true, toolSearchAvailable: true }); |
| 116 | await bridge.handlers.get("session_start")!( | 144 | await bridge.handlers.get("session_start")!({ systemPrompt: "" }, discoveryContext); |
| 117 | { systemPrompt: "" }, | | |
| 118 | { ui: { notify: () => undefined } }, | | |
| 119 | ); | | |
| 120 | const start = bridge.handlers.get("before_agent_start"); | 145 | const start = bridge.handlers.get("before_agent_start"); |
| 121 | assert.isDefined(start); | 146 | assert.isDefined(start); |
| 122 | const prompt = await start!( | 147 | const prompt = await start!({ systemPrompt: "Pi system prompt" }, discoveryContext); |
| 123 | { systemPrompt: "Pi system prompt" }, | | |
| 124 | { ui: { notify: () => undefined } }, | | |
| 125 | ); | | |
| 126 | assert.include(prompt.systemPrompt, "orchestrator_capabilities"); | 148 | assert.include(prompt.systemPrompt, "orchestrator_capabilities"); |
| 127 | assert.equal(bridge.servers.length, 0); | 149 | assert.equal(bridge.servers.length, 0); |
| 128 | assert.equal(bridge.tools.length, 8); | 150 | assert.equal(bridge.tools.length, 8); |
| ... | @@ -154,6 +176,33 @@ describe("Pi MCP tool exposure", () => { | ... | @@ -154,6 +176,33 @@ describe("Pi MCP tool exposure", () => { |
| 154 | assert.equal(bridge.requests.at(-1)?.method, "tools/call"); | 176 | assert.equal(bridge.requests.at(-1)?.method, "tools/call"); |
| 155 | }); | 177 | }); |
| 156 | | 178 | |
| | 179 | it("restores gateway declarations after model switches and older tree loadouts without excluded tools", async () => { |
| | 180 | const bridge = await loadMcpBridge({ |
| | 181 | modern: true, |
| | 182 | toolSearchAvailable: true, |
| | 183 | allowsTool: (name) => name !== "mcp__t3-code__delegate_task", |
| | 184 | }); |
| | 185 | const event = { systemPrompt: "" }; |
| | 186 | await bridge.handlers.get("session_start")!(event, discoveryContext); |
| | 187 | const gatewayContext = { model: { api: "openai-completions" }, ui: { notify: assert.fail } }; |
| | 188 | await bridge.handlers.get("model_select")!(event, gatewayContext); |
| | 189 | bridge.restoreActiveTools(["read", "mcp__t3-code__task_status"]); |
| | 190 | await bridge.handlers.get("session_tree")!(event, gatewayContext); |
| | 191 | assert.deepEqual(bridge.getActiveTools(), [ |
| | 192 | "read", |
| | 193 | "mcp__t3-code__task_status", |
| | 194 | "tool_search", |
| | 195 | "mcp__t3-code__orchestrator_capabilities", |
| | 196 | "mcp__t3-code__preview_snapshot", |
| | 197 | ]); |
| | 198 | await bridge.handlers.get("model_select")!(event, discoveryContext); |
| | 199 | assert.equal( |
| | 200 | bridge.tools.find((tool) => tool.name === "mcp__t3-code__preview_snapshot")?.exposure, |
| | 201 | "deferred", |
| | 202 | ); |
| | 203 | assert.include(bridge.getActiveTools(), "mcp__t3-code__preview_snapshot"); |
| | 204 | }); |
| | 205 | |
| 157 | it.each(["legacy Pi", "disabled tool search"])( | 206 | it.each(["legacy Pi", "disabled tool search"])( |
| 158 | "keeps tool execution available with %s", | 207 | "keeps tool execution available with %s", |
| 159 | async (mode) => { | 208 | async (mode) => { |
| ... | @@ -164,7 +213,7 @@ describe("Pi MCP tool exposure", () => { | ... | @@ -164,7 +213,7 @@ describe("Pi MCP tool exposure", () => { |
| 164 | }); | 213 | }); |
| 165 | if (mode !== "legacy Pi") { | 214 | if (mode !== "legacy Pi") { |
| 166 | const start = bridge.handlers.get("session_start"); | 215 | const start = bridge.handlers.get("session_start"); |
| 167 | await start!({ systemPrompt: "Pi system prompt" }, { ui: { notify: () => undefined } }); | 216 | await start!({ systemPrompt: "Pi system prompt" }, discoveryContext); |
| 168 | } | 217 | } |
| 169 | assert.equal(bridge.tools.filter((tool) => tool.exposure !== "hidden").length, 4); | 218 | assert.equal(bridge.tools.filter((tool) => tool.exposure !== "hidden").length, 4); |
| 170 | assert.isTrue( | 219 | assert.isTrue( |
| ... | @@ -198,10 +247,7 @@ describe("Pi MCP tool exposure", () => { | ... | @@ -198,10 +247,7 @@ describe("Pi MCP tool exposure", () => { |
| 198 | allowsTool: (name) => | 247 | allowsTool: (name) => |
| 199 | name === "read" || name === "tool_search" || name.startsWith("mcp__t3-code__"), | 248 | name === "read" || name === "tool_search" || name.startsWith("mcp__t3-code__"), |
| 200 | }); | 249 | }); |
| 201 | await bridge.handlers.get("session_start")!( | 250 | await bridge.handlers.get("session_start")!({ systemPrompt: "" }, discoveryContext); |
| 202 | { systemPrompt: "" }, | | |
| 203 | { ui: { notify: () => undefined } }, | | |
| 204 | ); | | |
| 205 | assert.equal(bridge.tools.length, 4); | 251 | assert.equal(bridge.tools.length, 4); |
| 206 | assert.equal(bridge.tools.filter((tool) => tool.exposure === "direct").length, 3); | 252 | assert.equal(bridge.tools.filter((tool) => tool.exposure === "direct").length, 3); |
| 207 | const tool = bridge.tools.find((tool) => tool.name === "mcp__t3-code__preview_snapshot"); | 253 | const tool = bridge.tools.find((tool) => tool.name === "mcp__t3-code__preview_snapshot"); |
| ... | @@ -218,10 +264,7 @@ describe("Pi MCP tool exposure", () => { | ... | @@ -218,10 +264,7 @@ describe("Pi MCP tool exposure", () => { |
| 218 | allowsTool: (name) => | 264 | allowsTool: (name) => |
| 219 | name !== "mcp__t3-code__delegate_task" && (!excludeSearch || name !== "tool_search"), | 265 | name !== "mcp__t3-code__delegate_task" && (!excludeSearch || name !== "tool_search"), |
| 220 | }); | 266 | }); |
| 221 | await bridge.handlers.get("session_start")!( | 267 | await bridge.handlers.get("session_start")!({ systemPrompt: "" }, discoveryContext); |
| 222 | { systemPrompt: "" }, | | |
| 223 | { ui: { notify: () => undefined } }, | | |
| 224 | ); | | |
| 225 | bridge.restoreActiveTools([ | 268 | bridge.restoreActiveTools([ |
| 226 | "read", | 269 | "read", |
| 227 | "mcp__t3-code__task_status", | 270 | "mcp__t3-code__task_status", |
| ... | @@ -229,7 +272,7 @@ describe("Pi MCP tool exposure", () => { | ... | @@ -229,7 +272,7 @@ describe("Pi MCP tool exposure", () => { |
| 229 | ]); | 272 | ]); |
| 230 | const tree = bridge.handlers.get("session_tree"); | 273 | const tree = bridge.handlers.get("session_tree"); |
| 231 | assert.isDefined(tree); | 274 | assert.isDefined(tree); |
| 232 | await tree!({ systemPrompt: "" }, { ui: { notify: () => undefined } }); | 275 | await tree!({ systemPrompt: "" }, discoveryContext); |
| 233 | assert.deepEqual(bridge.getActiveTools(), [ | 276 | assert.deepEqual(bridge.getActiveTools(), [ |
| 234 | "read", | 277 | "read", |
| 235 | "mcp__t3-code__task_status", | 278 | "mcp__t3-code__task_status", |