| 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,16 +315,19 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = { |
| 315 | // The windows just changed; re-probe so the snapshot says so. A | 315 | // The windows just changed; re-probe so the snapshot says so. A |
| 316 | // failed probe republishes the pre-redemption limits rather than | 316 | // failed probe republishes the pre-redemption limits rather than |
| 317 | // marking them failed, so "confirmed" means `checkedAt` moved | 317 | // marking them failed, so "confirmed" means `checkedAt` moved |
| 318 | // past what was published before the redemption started. | 318 | // past what was published before the redemption started. Only a |
| 319 | Effect.tap(() => | 319 | // reset claims the limits changed, so only a reset reports an |
| 320 | // unconfirmed refresh. | ||
| 321 | Effect.tap((outcome) => | ||
| 320 | Effect.gen(function* () { | 322 | Effect.gen(function* () { |
| 321 | const before = (yield* snapshot.getSnapshot).usageLimits?.checkedAt; | 323 | const before = (yield* snapshot.getSnapshot).usageLimits?.checkedAt; |
| 322 | const refreshed = yield* snapshot.refresh; | 324 | const refreshed = yield* snapshot.refresh; |
| 323 | const after = refreshed.usageLimits?.checkedAt; | 325 | const after = refreshed.usageLimits?.checkedAt; |
| 324 | if ( | 326 | if ( |
| 325 | after === undefined || | 327 | outcome === "reset" && |
| 326 | after === before || | 328 | (after === undefined || |
| 327 | refreshed.usageLimits?.unavailable?.reason === "probeFailed" | 329 | after === before || |
| 330 | refreshed.usageLimits?.unavailable?.reason === "probeFailed") | ||
| 328 | ) { | 331 | ) { |
| 329 | return yield* new ProviderDriverError({ | 332 | return yield* new ProviderDriverError({ |
| 330 | driver: DRIVER_KIND, | 333 | driver: DRIVER_KIND, |
apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts+38| ... | @@ -338,6 +338,44 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -338,6 +338,44 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 338 | }).pipe(Effect.provide(testLayer)), | 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 | it.live("runs Codex and Claude readiness probes from configured tilde paths", () => | 379 | it.live("runs Codex and Claude readiness probes from configured tilde paths", () => |
| 342 | Effect.gen(function* () { | 380 | Effect.gen(function* () { |
| 343 | if (yield* isHostWindows) return; | 381 | if (yield* isHostWindows) return; |
apps/server/src/provider/testFixtures/codexCollabMockPeer.mjs+12-1| ... | @@ -84,7 +84,18 @@ rl.on("line", (line) => { | ... | @@ -84,7 +84,18 @@ rl.on("line", (line) => { |
| 84 | return; | 84 | return; |
| 85 | } | 85 | } |
| 86 | if (method === "account/read") { | 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 | return; | 99 | return; |
| 89 | } | 100 | } |
| 90 | if (method === "skills/list" || method === "model/list") { | 101 | if (method === "skills/list" || method === "model/list") { |