| author | |
| committer | |
| log | ffb5fcce7336f9b3d5bde3a5f496a816907663af |
| tree | d91016d0f112daa07df5eca721d335f59b76516c |
| parent | f26ee083fe41720207fb10616359500dcecc19d3 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>3 files changed, 58 insertions(+), 6 deletions(-)
apps/server/src/provider/Drivers/CodexDriver.ts+8-5| ... | ... | @@ -315,16 +315,19 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = { |
| 315 | 315 | // The windows just changed; re-probe so the snapshot says so. A |
| 316 | 316 | // failed probe republishes the pre-redemption limits rather than |
| 317 | 317 | // marking them failed, so "confirmed" means `checkedAt` moved |
| 318 | // past what was published before the redemption started. | |
| 319 | Effect.tap(() => | |
| 318 | // past what was published before the redemption started. Only a | |
| 319 | // reset claims the limits changed, so only a reset reports an | |
| 320 | // unconfirmed refresh. | |
| 321 | Effect.tap((outcome) => | |
| 320 | 322 | Effect.gen(function* () { |
| 321 | 323 | const before = (yield* snapshot.getSnapshot).usageLimits?.checkedAt; |
| 322 | 324 | const refreshed = yield* snapshot.refresh; |
| 323 | 325 | const after = refreshed.usageLimits?.checkedAt; |
| 324 | 326 | if ( |
| 325 | after === undefined || | |
| 326 | after === before || | |
| 327 | refreshed.usageLimits?.unavailable?.reason === "probeFailed" | |
| 327 | outcome === "reset" && | |
| 328 | (after === undefined || | |
| 329 | after === before || | |
| 330 | refreshed.usageLimits?.unavailable?.reason === "probeFailed") | |
| 328 | 331 | ) { |
| 329 | 332 | return yield* new ProviderDriverError({ |
| 330 | 333 | driver: DRIVER_KIND, |
apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts+38| ... | ... | @@ -338,6 +338,44 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 338 | 338 | }).pipe(Effect.provide(testLayer)), |
| 339 | 339 | ); |
| 340 | 340 | |
| 341 | it.live("reports Codex's answer when a redemption changed nothing", () => | |
| 342 | Effect.gen(function* () { | |
| 343 | if (yield* isHostWindows) return; | |
| 344 | const fileSystem = yield* FileSystem.FileSystem; | |
| 345 | const fixtures = yield* makeTildeProviderFixtures(); | |
| 346 | yield* fileSystem.writeFileString( | |
| 347 | fixtures.codexScriptPath, | |
| 348 | // @effect-diagnostics-next-line preferSchemaOverJson:off - fixed script document read by the external Codex mock peer. | |
| 349 | JSON.stringify({ | |
| 350 | rootThreadId: "probe-thread", | |
| 351 | notifications: [], | |
| 352 | account: { type: "chatgpt", email: "test@example.com", planType: "plus" }, | |
| 353 | failRateLimitsRead: true, | |
| 354 | resetCreditOutcome: "alreadyRedeemed", | |
| 355 | }), | |
| 356 | ); | |
| 357 | const codexId = ProviderInstanceId.make("codex_reset"); | |
| 358 | const { registry } = yield* makeProviderInstanceRegistry({ | |
| 359 | drivers: [CodexDriver], | |
| 360 | configMap: { | |
| 361 | [codexId]: { | |
| 362 | driver: ProviderDriverKind.make("codex"), | |
| 363 | enabled: true, | |
| 364 | environment: [ | |
| 365 | { name: "T3_CODEX_COLLAB_SCRIPT", value: fixtures.codexScriptPath, sensitive: false }, | |
| 366 | ], | |
| 367 | config: makeCodexConfig({ enabled: true, binaryPath: fixtures.codexBinaryPath }), | |
| 368 | }, | |
| 369 | }, | |
| 370 | }); | |
| 371 | const codex = yield* registry.getInstance(codexId); | |
| 372 | expect(codex).toBeDefined(); | |
| 373 | // The usage read fails, so the re-probe cannot confirm new limits. | |
| 374 | yield* codex!.snapshot.refresh; | |
| 375 | expect(yield* codex!.consumeResetCredit!()).toBe("alreadyRedeemed"); | |
| 376 | }).pipe(Effect.provide(testLayer)), | |
| 377 | ); | |
| 378 | ||
| 341 | 379 | it.live("runs Codex and Claude readiness probes from configured tilde paths", () => |
| 342 | 380 | Effect.gen(function* () { |
| 343 | 381 | if (yield* isHostWindows) return; |
apps/server/src/provider/testFixtures/codexCollabMockPeer.mjs+12-1| ... | ... | @@ -84,7 +84,18 @@ rl.on("line", (line) => { |
| 84 | 84 | return; |
| 85 | 85 | } |
| 86 | 86 | if (method === "account/read") { |
| 87 | write({ id, result: { account: { type: "apiKey" }, requiresOpenaiAuth: false } }); | |
| 87 | write({ | |
| 88 | id, | |
| 89 | result: { account: script.account ?? { type: "apiKey" }, requiresOpenaiAuth: false }, | |
| 90 | }); | |
| 91 | return; | |
| 92 | } | |
| 93 | if (method === "account/rateLimits/read" && script.failRateLimitsRead) { | |
| 94 | write({ id, error: { code: -32000, message: "usage unavailable" } }); | |
| 95 | return; | |
| 96 | } | |
| 97 | if (method === "account/rateLimitResetCredit/consume" && script.resetCreditOutcome) { | |
| 98 | write({ id, result: { outcome: script.resetCreditOutcome } }); | |
| 88 | 99 | return; |
| 89 | 100 | } |
| 90 | 101 | if (method === "skills/list" || method === "model/list") { |