| author | |
| committer | |
| log | 75ab5ab3fb6ad35117da754644c404a31b2fed84 |
| tree | 84dbe12f5605ca367f8a843a2f241b479bcdd01a |
| parent | b90898077e60290a1eb7bea4224f325fd3bb1595 |
| signature | Signed by PGP key B5690EEEBB952194 |
3 files changed, 86 insertions(+), 1 deletions(-)
packages/effect-codex-app-server/scripts/generate.ts+36-1| ... | ... | @@ -194,6 +194,40 @@ const Codex0150DefinitionSchemas: Record<string, Schema.Json> = { |
| 194 | 194 | }, |
| 195 | 195 | }; |
| 196 | 196 | |
| 197 | function applyCodex0151DefinitionCompatibility( | |
| 198 | exportName: string, | |
| 199 | definitionName: string, | |
| 200 | definitionSchema: Schema.Json, | |
| 201 | ): Schema.Json { | |
| 202 | const isThreadResponse = | |
| 203 | exportName === "V2ThreadReadResponse" || | |
| 204 | exportName === "V2ThreadResumeResponse" || | |
| 205 | exportName === "V2ThreadRollbackResponse"; | |
| 206 | if ( | |
| 207 | !isThreadResponse || | |
| 208 | definitionName !== "CodexErrorInfo" || | |
| 209 | typeof definitionSchema !== "object" | |
| 210 | ) { | |
| 211 | return definitionSchema; | |
| 212 | } | |
| 213 | ||
| 214 | const schema = definitionSchema as { | |
| 215 | readonly oneOf?: ReadonlyArray<{ readonly enum?: ReadonlyArray<string> }>; | |
| 216 | }; | |
| 217 | const [firstVariant, ...remainingVariants] = schema.oneOf ?? []; | |
| 218 | if (!firstVariant?.enum || firstVariant.enum.includes("rateLimitExceeded")) { | |
| 219 | return definitionSchema; | |
| 220 | } | |
| 221 | ||
| 222 | return { | |
| 223 | ...definitionSchema, | |
| 224 | oneOf: [ | |
| 225 | { ...firstVariant, enum: [...firstVariant.enum, "rateLimitExceeded"] }, | |
| 226 | ...remainingVariants, | |
| 227 | ], | |
| 228 | }; | |
| 229 | } | |
| 230 | ||
| 197 | 231 | const getGeneratedPaths = Effect.fn("getGeneratedPaths")(function* () { |
| 198 | 232 | const path = yield* Path.Path; |
| 199 | 233 | const generatedDir = path.join(import.meta.dirname, "..", "src", "_generated"); |
| ... | ... | @@ -660,7 +694,8 @@ const generateFiles = Effect.fn("generateFiles")(function* () { |
| 660 | 694 | |
| 661 | 695 | for (const [definitionName, definitionSchema] of Object.entries(parsed.definitions ?? {})) { |
| 662 | 696 | const compatibleDefinitionSchema = |
| 663 | Codex0150DefinitionSchemas[definitionName] ?? definitionSchema; | |
| 697 | Codex0150DefinitionSchemas[definitionName] ?? | |
| 698 | applyCodex0151DefinitionCompatibility(file.exportName, definitionName, definitionSchema); | |
| 664 | 699 | aggregateSchemas[localDefinitionNames.get(definitionName)!] = stripNullDefaults( |
| 665 | 700 | normalizeNullableTypes( |
| 666 | 701 | rewriteExternalRefs( |
packages/effect-codex-app-server/src/_generated/schema.gen.ts+6| ... | ... | @@ -16854,6 +16854,7 @@ export type V2ThreadReadResponse__CodexErrorInfo = |
| 16854 | 16854 | | "badRequest" |
| 16855 | 16855 | | "threadRollbackFailed" |
| 16856 | 16856 | | "sandboxError" |
| 16857 | | "rateLimitExceeded" | |
| 16857 | 16858 | | "other" |
| 16858 | 16859 | | { readonly httpConnectionFailed: { readonly httpStatusCode?: number | null } } |
| 16859 | 16860 | | { readonly responseStreamConnectionFailed: { readonly httpStatusCode?: number | null } } |
| ... | ... | @@ -16877,6 +16878,7 @@ export const V2ThreadReadResponse__CodexErrorInfo = Schema.Union( |
| 16877 | 16878 | "badRequest", |
| 16878 | 16879 | "threadRollbackFailed", |
| 16879 | 16880 | "sandboxError", |
| 16881 | "rateLimitExceeded", | |
| 16880 | 16882 | "other", |
| 16881 | 16883 | ]), |
| 16882 | 16884 | Schema.Struct({ |
| ... | ... | @@ -17207,6 +17209,7 @@ export type V2ThreadResumeResponse__CodexErrorInfo = |
| 17207 | 17209 | | "badRequest" |
| 17208 | 17210 | | "threadRollbackFailed" |
| 17209 | 17211 | | "sandboxError" |
| 17212 | | "rateLimitExceeded" | |
| 17210 | 17213 | | "other" |
| 17211 | 17214 | | { readonly httpConnectionFailed: { readonly httpStatusCode?: number | null } } |
| 17212 | 17215 | | { readonly responseStreamConnectionFailed: { readonly httpStatusCode?: number | null } } |
| ... | ... | @@ -17230,6 +17233,7 @@ export const V2ThreadResumeResponse__CodexErrorInfo = Schema.Union( |
| 17230 | 17233 | "badRequest", |
| 17231 | 17234 | "threadRollbackFailed", |
| 17232 | 17235 | "sandboxError", |
| 17236 | "rateLimitExceeded", | |
| 17233 | 17237 | "other", |
| 17234 | 17238 | ]), |
| 17235 | 17239 | Schema.Struct({ |
| ... | ... | @@ -17485,6 +17489,7 @@ export type V2ThreadRollbackResponse__CodexErrorInfo = |
| 17485 | 17489 | | "badRequest" |
| 17486 | 17490 | | "threadRollbackFailed" |
| 17487 | 17491 | | "sandboxError" |
| 17492 | | "rateLimitExceeded" | |
| 17488 | 17493 | | "other" |
| 17489 | 17494 | | { readonly httpConnectionFailed: { readonly httpStatusCode?: number | null } } |
| 17490 | 17495 | | { readonly responseStreamConnectionFailed: { readonly httpStatusCode?: number | null } } |
| ... | ... | @@ -17508,6 +17513,7 @@ export const V2ThreadRollbackResponse__CodexErrorInfo = Schema.Union( |
| 17508 | 17513 | "badRequest", |
| 17509 | 17514 | "threadRollbackFailed", |
| 17510 | 17515 | "sandboxError", |
| 17516 | "rateLimitExceeded", | |
| 17511 | 17517 | "other", |
| 17512 | 17518 | ]), |
| 17513 | 17519 | Schema.Struct({ |
packages/effect-codex-app-server/src/schema.test.ts+44| ... | ... | @@ -4,6 +4,9 @@ import * as Schema from "effect/Schema"; |
| 4 | 4 | import * as CodexSchema from "./schema.ts"; |
| 5 | 5 | |
| 6 | 6 | const isGetAccountResponse = Schema.is(CodexSchema.V2GetAccountResponse); |
| 7 | const isThreadReadResponse = Schema.is(CodexSchema.V2ThreadReadResponse); | |
| 8 | const isThreadResumeResponse = Schema.is(CodexSchema.V2ThreadResumeResponse); | |
| 9 | const isThreadRollbackResponse = Schema.is(CodexSchema.V2ThreadRollbackResponse); | |
| 7 | 10 | |
| 8 | 11 | it("keeps async questions in live notifications and thread history", () => { |
| 9 | 12 | const item = { |
| ... | ... | @@ -97,6 +100,47 @@ it("accepts Codex 0.150 multi-agent values", () => { |
| 97 | 100 | assert.equal(Schema.is(CodexSchema.V2ThreadResumeResponse)(resumeResponse), true); |
| 98 | 101 | }); |
| 99 | 102 | |
| 103 | it("accepts Codex rate limit errors for thread responses", () => { | |
| 104 | const failedThread = { | |
| 105 | cliVersion: "0.150.0", | |
| 106 | createdAt: 0, | |
| 107 | cwd: "/tmp/project", | |
| 108 | ephemeral: false, | |
| 109 | id: "thread-1", | |
| 110 | modelProvider: "openai", | |
| 111 | preview: "", | |
| 112 | sessionId: "session-1", | |
| 113 | source: "cli", | |
| 114 | status: { type: "idle" }, | |
| 115 | turns: [ | |
| 116 | { | |
| 117 | error: { | |
| 118 | codexErrorInfo: "rateLimitExceeded", | |
| 119 | message: "Rate limit exceeded", | |
| 120 | }, | |
| 121 | id: "turn-1", | |
| 122 | items: [], | |
| 123 | status: "failed", | |
| 124 | }, | |
| 125 | ], | |
| 126 | updatedAt: 0, | |
| 127 | }; | |
| 128 | assert.equal(isThreadReadResponse({ thread: failedThread }), true); | |
| 129 | assert.equal( | |
| 130 | isThreadResumeResponse({ | |
| 131 | approvalPolicy: "never", | |
| 132 | approvalsReviewer: "user", | |
| 133 | cwd: "/tmp/project", | |
| 134 | model: "gpt-5.6-sol", | |
| 135 | modelProvider: "openai", | |
| 136 | sandbox: { type: "dangerFullAccess" }, | |
| 137 | thread: failedThread, | |
| 138 | }), | |
| 139 | true, | |
| 140 | ); | |
| 141 | assert.equal(isThreadRollbackResponse({ thread: failedThread }), true); | |
| 142 | }); | |
| 143 | ||
| 100 | 144 | it("accepts Codex 0.150 account plan values", () => { |
| 101 | 145 | const planTypes = [ |
| 102 | 146 | "self_serve_business_prolite", |