fix: return the era-parsed tool, and hide aggregate reports in paginated mode (#1909)
Copilot round 8. The era contract validated with the wire schema but returned the raw input, so the wire schema's normalization was discarded — both era schemas are z.object, which strips unknown top-level fields as the strict codec does, so salvaged tools could carry fields the strict path drops. The parsed output is now what gets normalized and returned. This also simplifies the bridge: issues come straight from the wire parse with their paths intact, so the custom-issue forwarding (and its early describeIssue application) is gone. The malformed-entry report is written by the aggregate walk's salvage, but in paginated mode the tools/prompts/resources panels render the paged stores, which neither write nor clear it — leaving a stale warning above a freshly fetched page it does not describe, with indices into an aggregate the user is no longer looking at. Those reports are now suppressed in paginated mode; resource templates keep theirs, having no paged store. Suppressing rather than adding per-page salvage: pointing at the wrong entry costs more than staying quiet, and per-page salvage is a feature, not a display fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDXCSrnSBV7vZfxycG5Z6E Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
dbda70795b
commit
7db807b0de
1 changed files with 144 additions and 45 deletions
|
|
@ -197,6 +197,29 @@ describe("salvage era item contract (#1909)", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("strips unknown fields the way the strict codec does", async () => {
|
||||
// The contract validates through a wire schema; returning the ORIGINAL
|
||||
// value rather than the parsed one would leave salvaged tools carrying
|
||||
// fields the strict path drops, so the two paths would hand the UI
|
||||
// differently-shaped tools for the same server.
|
||||
const strayTool = {
|
||||
name: "stray",
|
||||
inputSchema: { type: "object" },
|
||||
notAToolField: "should not survive",
|
||||
};
|
||||
const parsed = toolItemSchemaForEra(false).safeParse(strayTool);
|
||||
expect(parsed.success).toBe(true);
|
||||
expect(parsed.success && parsed.data).not.toHaveProperty("notAToolField");
|
||||
|
||||
// And the strict path agrees — this is the shape being matched, not an
|
||||
// invented rule.
|
||||
const server = await startToolsServer([strayTool], false);
|
||||
stop = server.stop;
|
||||
const connected = await connect(server.url, "legacy");
|
||||
const { tools } = await connected.listAllTools();
|
||||
expect(tools[0]).not.toHaveProperty("notAToolField");
|
||||
});
|
||||
|
||||
it("the era contract matches the strict path on both divergences", () => {
|
||||
// Legacy: rejected, exactly as the strict parse above rejected it.
|
||||
expect(
|
||||
|
|
|
|||
Loading…
Reference in a new issue