Skip to content

Commit 3548cff

Browse files
authored
refactor: migrate validators to TypeBox (#86639)
* refactor: migrate validators to typebox * fix: preserve json schema resource refs * chore: clean schema preflight recursion * refactor: remove lobster ajv shim * fix: support schema array refs * fix: validate schema dependencies * fix: preserve schema contract checks * fix: support same-document schema refs * fix: preserve untyped map defaults * fix: preserve schema default semantics * test: avoid thenable schema literals * test: build conditional schema key * fix: defer resource id refs to typebox * fix: reject invalid schema enum metadata * fix: preserve default branch semantics * fix: resolve schema resource refs * fix: narrow conditional default fallback * fix: preserve uri format validation * fix: preserve validator compatibility * test: avoid ajv cache lint violation * fix: preserve typebox validation diagnostics * fix: validate defaulted conditional schemas * fix: normalize mcp draft schemas * fix: preserve tuple schema defaults * fix: resolve relative schema refs * fix: scope typebox format semantics * fix: align conditional format defaults * fix: decode schema pointer refs * fix: filter grouped secretref diagnostics * fix: preserve default conditional compatibility * fix: preserve nullable schema compatibility * fix: settle defaults before conditionals * fix: preserve default validation invariants * fix: validate dynamic schema refs * fix: reject malformed nullable schemas
1 parent b377618 commit 3548cff

33 files changed

Lines changed: 4215 additions & 490 deletions

extensions/codex/npm-shrinkwrap.json

Lines changed: 1 addition & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/codex/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"dependencies": {
1111
"@earendil-works/pi-coding-agent": "0.75.5",
1212
"@openai/codex": "0.133.0",
13-
"ajv": "8.20.0",
13+
"typebox": "1.1.38",
1414
"ws": "8.21.0",
1515
"zod": "4.4.3"
1616
},

extensions/codex/src/app-server/protocol-validators.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { describe, expect, it } from "vitest";
22
import {
3+
readCodexModelListResponse,
4+
readCodexTurn,
35
assertCodexThreadStartResponse,
46
assertCodexThreadResumeResponse,
57
} from "./protocol-validators.js";
@@ -73,3 +75,67 @@ describe("assertCodexThreadResumeResponse", () => {
7375
expect(result.thread.sessionId).toBe("thread-1");
7476
});
7577
});
78+
79+
describe("readCodexModelListResponse", () => {
80+
it("applies defaults from generated schemas behind local refs", () => {
81+
const response = readCodexModelListResponse({
82+
data: [
83+
{
84+
id: "gpt-test",
85+
model: "gpt-test",
86+
displayName: "GPT Test",
87+
description: "test model",
88+
hidden: false,
89+
isDefault: false,
90+
defaultReasoningEffort: "medium",
91+
supportedReasoningEfforts: [],
92+
},
93+
],
94+
});
95+
96+
const model = response?.data[0] as
97+
| (NonNullable<ReturnType<typeof readCodexModelListResponse>>["data"][number] & {
98+
serviceTiers?: unknown;
99+
supportsPersonality?: unknown;
100+
})
101+
| undefined;
102+
expect(model?.inputModalities).toEqual(["text", "image"]);
103+
expect(model?.serviceTiers).toEqual([]);
104+
expect(model?.supportsPersonality).toBe(false);
105+
});
106+
});
107+
108+
describe("readCodexTurn", () => {
109+
it("does not merge defaults from unrelated thread item union branches", () => {
110+
const turn = readCodexTurn({
111+
id: "turn-1",
112+
status: "completed",
113+
items: [{ id: "item-1", type: "plan", text: "ship it" }],
114+
});
115+
116+
expect(turn?.items[0]).toEqual({ id: "item-1", type: "plan", text: "ship it" });
117+
});
118+
119+
it("accepts nullable arrays in generated dynamic tool call items", () => {
120+
const turn = readCodexTurn({
121+
id: "turn-1",
122+
status: "completed",
123+
items: [
124+
{
125+
arguments: {},
126+
contentItems: null,
127+
id: "item-1",
128+
status: "completed",
129+
tool: "render",
130+
type: "dynamicToolCall",
131+
},
132+
],
133+
});
134+
135+
expect(turn?.items[0]).toMatchObject({
136+
contentItems: null,
137+
id: "item-1",
138+
type: "dynamicToolCall",
139+
});
140+
});
141+
});

0 commit comments

Comments
 (0)