mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00: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:
parent
c37b0e03b5
commit
acaa253cc8
3 changed files with 32 additions and 0 deletions
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue