authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-10-05 21:28:36-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-05 21:28:36-07:00
log255ac27d5200b01c50acef105ab0ec8bb6ab8345
tree1ad5ce43615cf436411b23551bd3515779f3764e
parent24edd55d9459edfa0a2956094b7868102bba5b6f
signature Signed by PGP key B5690EEEBB952194

fix(server): Claude limits load again for users with large transcript histories (#16358)

The Claude capabilities probe now asks for usage with skipBehaviors, so it no longer scans every local transcript and misses its 4 s deadline. Takes over #14456. Co-authored-by: Ashkaan <a@ashkaan.me>

2 files changed, 25 insertions(+), 1 deletions(-)

apps/server/src/provider/ClaudeCapabilitiesProbe.test.ts+22
......@@ -353,3 +353,25 @@ it.effect("preserves initialized capabilities when optional usage times out", ()
353353 assert.equal(abortSignal?.aborted, true);
354354 }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),
355355);
356
357it.effect("asks for usage without the local transcript scan", () =>
358 Effect.gen(function* () {
359 let usageOptions: unknown;
360 const query = vi.spyOn(ClaudeSdk, "query").mockImplementation(
361 () =>
362 ({
363 initializationResult: async () => ({
364 account: { email: "dev@example.com", subscriptionType: "max", tokenSource: "oauth" },
365 commands: [],
366 }),
367 usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET: async (options?: unknown) => {
368 usageOptions = options;
369 return { rate_limits_available: true, rate_limits: null };
370 },
371 }) as unknown as ReturnType<typeof ClaudeSdk.query>,
372 );
373 yield* Effect.addFinalizer(() => Effect.sync(() => query.mockRestore()));
374 yield* probeClaudeCapabilities(decodeClaudeSettings({ binaryPath: "claude" }));
375 assert.deepEqual(usageOptions, { skipBehaviors: true });
376 }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),
377);
apps/server/src/provider/ClaudeProvider.ts+3-1
......@@ -367,9 +367,11 @@ const probeClaudeCapabilities = (
367367 Effect.flatMap(({ q, init }) =>
368368 Effect.gen(function* () {
369369 // Usage has its own deadline so a slow optional request cannot discard initialization.
370 // Only the rate limits are read, so skip the local transcript scan that fills
371 // `behaviors`: with a few GB of transcripts it outlasts the deadline.
370372 const usageResult = includeUsage
371373 ? yield* Effect.tryPromise(() =>
372 q.usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET(),
374 q.usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET({ skipBehaviors: true }),
373375 ).pipe(Effect.timeout(DEFAULT_TIMEOUT_MS), Effect.result)
374376 : undefined;
375377 const usage =