mirror of
https://github.com/earendil-works/pi.git
synced 2026-10-02 08:44:38 +08:00
fix(coding-agent): validate extension tool parameter schemas
Reject malformed extension tools during registration instead of allowing them to break provider request serialization. Fixes #9300
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
- Fixed direct RPC `steer` and `follow_up` commands bypassing extension `input` handlers ([#8718](https://github.com/earendil-works/pi/issues/8718)).
|
||||
- Fixed premature missing-model errors after login by waiting for catalog discovery. Radius now defaults to `balanced`, falling back to the first available Radius model when needed.
|
||||
- Fixed fullscreen mode reserving a blank row for custom footers that render zero rows ([#8919](https://github.com/earendil-works/pi/issues/8919)).
|
||||
- Fixed extension tools without parameter schemas to be rejected during registration instead of breaking provider requests ([#9300](https://github.com/earendil-works/pi/issues/9300)).
|
||||
|
||||
## [0.85.1] - 2026-09-05
|
||||
|
||||
|
||||
@@ -286,6 +286,11 @@ function createExtensionAPI(
|
||||
|
||||
registerTool(tool: ToolDefinition): void {
|
||||
assertActive();
|
||||
if (typeof tool.parameters !== "object" || tool.parameters === null || Array.isArray(tool.parameters)) {
|
||||
throw new Error(
|
||||
`Tool "${tool.name}" registered by extension "${extension.path}" must define an object parameter schema.`,
|
||||
);
|
||||
}
|
||||
extension.tools.set(tool.name, {
|
||||
definition: tool,
|
||||
sourceInfo: extension.sourceInfo,
|
||||
|
||||
@@ -390,6 +390,32 @@ describe("ExtensionRunner", () => {
|
||||
expect(tools.map((t) => t.definition.name).sort()).toEqual(["tool_a", "tool_b"]);
|
||||
});
|
||||
|
||||
// Regression test for #9300.
|
||||
it("rejects extension tools without a parameter schema", async () => {
|
||||
const extensionPath = path.join(extensionsDir, "missing-parameters.js");
|
||||
fs.writeFileSync(
|
||||
extensionPath,
|
||||
`export default function(pi) {
|
||||
pi.registerTool({
|
||||
name: "noop",
|
||||
label: "No-op",
|
||||
description: "Do nothing",
|
||||
execute: async () => ({ content: [{ type: "text", text: "ok" }] }),
|
||||
});
|
||||
}`,
|
||||
);
|
||||
|
||||
const result = await loadExtensions([extensionPath], tempDir);
|
||||
|
||||
expect(result.extensions).toHaveLength(0);
|
||||
expect(result.errors).toEqual([
|
||||
{
|
||||
path: extensionPath,
|
||||
error: `Failed to load extension: Tool "noop" registered by extension "${extensionPath}" must define an object parameter schema.`,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps first tool when two extensions register the same name", async () => {
|
||||
const first = `
|
||||
import { Type } from "typebox";
|
||||
|
||||
Reference in New Issue
Block a user