@cortexkit/opencode-anthropic-auth 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,7 +19,7 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
19
19
  | Primary Claude Pro/Max OAuth | OpenCode `/connect anthropic` | Pi `/login anthropic` |
20
20
  | Provider integration point | OpenCode plugin fetch/request transform | Pi `registerProvider("anthropic")` provider override |
21
21
  | Sidecar config | `~/.config/opencode/anthropic-auth.json` | `~/.pi/agent/anthropic-auth.json` |
22
- | Commands | `/claude-cache`, `/claude-fast`, `/claude-quota`, `/claude-dump` | `/claude-cache`, `/claude-fast`, `/claude-quota`, `/claude-dump` |
22
+ | Commands | `/claude-cache`, `/claude-cachekeep`, `/claude-fast`, `/claude-quota`, `/claude-dump` | `/claude-cache`, `/claude-cachekeep`, `/claude-fast`, `/claude-quota`, `/claude-dump` |
23
23
  | Fallback accounts, quota routing, relay, dumps, fast mode | Supported | Supported through the same shared core and Pi sidecar |
24
24
 
25
25
  ## What CortexKit adds over the original plugin
@@ -27,6 +27,7 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
27
27
  - **Fallback Claude accounts**: keep each agent's normal Anthropic login as the primary account, then route to ordered fallback OAuth accounts on auth/quota/rate-limit failures.
28
28
  - **Quota-aware routing**: skip main or fallback accounts when their 5-hour or 7-day Claude quota falls below your configured minimum.
29
29
  - **Persistent Claude cache controls**: manage Anthropic 1-hour prompt caching from `/claude-cache` with explicit, automatic, or hybrid modes.
30
+ - **Cache keepalive**: use `/claude-cachekeep HH-HH` to pre-warm hybrid cache anchors for active sessions before the 1-hour TTL expires.
30
31
  - **Fast mode toggle**: use `/claude-fast on|off` to request Anthropic fast mode for supported Opus models.
31
32
  - **Live quota visibility**: use `/claude-quota` to see main and fallback quota state, reset times, and refresh errors.
32
33
  - **User-owned Cloudflare relay**: optionally provision your own Worker relay to reduce repeated client upload bytes for large OpenCode or Pi requests.
@@ -41,7 +42,7 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
41
42
  - Support fallback Claude accounts stored in a local per-agent sidecar file.
42
43
  - Keep fallback OAuth tokens fresh in the background.
43
44
  - Apply quota thresholds before routing to main or fallback accounts.
44
- - Add `/claude-cache`, `/claude-fast`, `/claude-quota`, and `/claude-dump` commands.
45
+ - Add `/claude-cache`, `/claude-cachekeep`, `/claude-fast`, `/claude-quota`, and `/claude-dump` commands.
45
46
  - Optionally relay large requests through a Cloudflare Worker owned by the user.
46
47
 
47
48
  ## Install
@@ -172,6 +173,11 @@ Example:
172
173
  "enabled": false,
173
174
  "mode": "explicit"
174
175
  },
176
+ "cacheKeep": {
177
+ "enabled": false,
178
+ "startHour": 9,
179
+ "endHour": 23
180
+ },
175
181
  "dump": {
176
182
  "enabled": false
177
183
  },
@@ -189,7 +195,7 @@ Example:
189
195
  }
190
196
  ```
191
197
 
192
- The `claudeCache` block controls the `/claude-cache` command's persisted mode, and `claudeFast` controls `/claude-fast`. The `main` field identifies OpenCode's primary auth entry; Pi keeps primary OAuth credentials in Pi's own credential store, but uses the same sidecar shape for CortexKit settings and fallback accounts.
198
+ The `claudeCache` block controls the `/claude-cache` command's persisted mode, `cacheKeep` controls `/claude-cachekeep`, and `claudeFast` controls `/claude-fast`. The `main` field identifies OpenCode's primary auth entry; Pi keeps primary OAuth credentials in Pi's own credential store, but uses the same sidecar shape for CortexKit settings and fallback accounts.
193
199
 
194
200
  ## Fallback accounts
195
201
 
@@ -275,6 +281,22 @@ Modes:
275
281
 
276
282
  In OpenCode, subagent requests do not receive 1-hour TTL caching. The plugin detects child sessions through OpenCode's `x-parent-session-id` header, strips that internal header before forwarding to Anthropic, and leaves default ephemeral caching in place for those requests.
277
283
 
284
+ ### Cache keepalive
285
+
286
+ `/claude-cachekeep` keeps recently used hybrid-mode session caches warm while the agent process is running:
287
+
288
+ ```text
289
+ /claude-cachekeep
290
+ /claude-cachekeep 09-23
291
+ /claude-cachekeep off
292
+ ```
293
+
294
+ The hour range uses local 24-hour time and is start-inclusive/end-exclusive. `09-23` means cache keepalive may run from 09:00 until 22:59. Overnight windows such as `23-09` are accepted.
295
+
296
+ Cache keepalive only tracks requests when `/claude-cache` is enabled in `hybrid` mode. For each active session seen that day, the package keeps an in-memory clone of the latest rewritten Anthropic request and sends a non-streaming `max_tokens: 0` pre-warm request about five minutes before the 1-hour cache entry would expire. Nothing is written to disk except the schedule configuration.
297
+
298
+ Pre-warm requests preserve explicit cache anchors but remove response-only fields that Anthropic rejects with `max_tokens: 0`, such as streaming, enabled thinking, structured output format, and forced/any tool choice. The feature works only while OpenCode or Pi is running and the machine is awake, and cache writes are still billed when the cache entry is no longer warm.
299
+
278
300
  ## Claude fast mode
279
301
 
280
302
  Both OpenCode and Pi packages can persistently request Anthropic fast mode for supported Opus models:
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { authorize, buildClaudeQuotaSummary, buildFallbackQuotaSummaries, buildRefreshOperationError, CACHE_1H_COMMAND_NAME, CLAUDE_DUMP_COMMAND_NAME, CLAUDE_FAST_COMMAND_NAME, CLAUDE_QUOTAS_COMMAND_NAME, ClaudeOAuthRefreshError, exchange, executeCache1hCommand, executeDumpCommand, executeFastModeCommand, FallbackAccountManager, fetchOAuthQuotaSnapshot, formatRefreshBackoffMessage, getCache1hMode, getCache1hPersistentMode, getQuotaCheckIntervalMs, getQuotaNextRefreshAt, getRelayConfig, hashRefreshToken, isCache1hEnabled, isCache1hPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModeEnabled, isFastModePersistentlyEnabled, isFastModeSupportedModel, loadAccounts, log, parseCache1hCommandAction, parseDumpCommandAction, parseFastModeCommandAction, quotaSnapshotPassesPolicy, refreshBackoffActive, refreshClaudeOAuthToken, resolveClaudeCodeIdentity, saveAccounts, sendViaRelay, setCache1hPersistentEnabled, setCache1hPersistentMode, setCache1hState, setDumpEnabled, setDumpPersistentEnabled, setFastModeEnabled, setFastModePersistentEnabled, shouldFallbackStatus, } from '@cortexkit/anthropic-auth-core';
2
+ import { authorize, buildClaudeQuotaSummary, buildFallbackQuotaSummaries, buildRefreshOperationError, CACHE_1H_COMMAND_NAME, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, CLAUDE_CACHE_KEEP_COMMAND_NAME, CLAUDE_DUMP_COMMAND_NAME, CLAUDE_FAST_COMMAND_NAME, CLAUDE_QUOTAS_COMMAND_NAME, ClaudeOAuthRefreshError, exchange, executeCache1hCommand, executeCacheKeepCommand, executeDumpCommand, executeFastModeCommand, FallbackAccountManager, fetchOAuthQuotaSnapshot, formatRefreshBackoffMessage, getCache1hMode, getCache1hPersistentMode, getCacheKeepWindow, getQuotaCheckIntervalMs, getQuotaNextRefreshAt, getRelayConfig, hashRefreshToken, isCache1hEnabled, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModeEnabled, isFastModePersistentlyEnabled, isFastModeSupportedModel, loadAccounts, log, mergeAnthropicBetas, parseCache1hCommandAction, parseCacheKeepCommandAction, parseDumpCommandAction, parseFastModeCommandAction, quotaSnapshotPassesPolicy, refreshBackoffActive, refreshClaudeOAuthToken, resolveClaudeCodeIdentity, saveAccounts, sendViaRelay, setCache1hPersistentEnabled, setCache1hPersistentMode, setCache1hState, setCacheKeepPersistentEnabled, setCacheKeepPersistentWindow, setDumpEnabled, setDumpPersistentEnabled, setFastModeEnabled, setFastModePersistentEnabled, shouldFallbackStatus, } from '@cortexkit/anthropic-auth-core';
3
3
  import { resolvePromptContext } from "./prompt-context.js";
4
4
  import { addFastModeBetaHeader, createStrippedStream, isInsecure, mergeHeaders, rewriteRequestBody, rewriteUrl, setOAuthHeaders, } from "./transform.js";
5
5
  const HANDLED_SENTINEL = '__OPENCODE_ANTHROPIC_AUTH_COMMAND_HANDLED__';
@@ -7,6 +7,9 @@ const MAIN_AUTH_REFRESH_TICK_MS = 60_000;
7
7
  const DEFAULT_MAIN_REFRESH_BEFORE_EXPIRY_MINUTES = 30;
8
8
  let nextPerfRequestId = 1;
9
9
  let eventLoopLagMonitorStarted = false;
10
+ function perfLoggingEnabled() {
11
+ return process.env.OPENCODE_ANTHROPIC_AUTH_PERF === '1';
12
+ }
10
13
  function nowMs() {
11
14
  return performance.now();
12
15
  }
@@ -14,8 +17,11 @@ function roundMs(value) {
14
17
  return Math.round(value * 10) / 10;
15
18
  }
16
19
  function startEventLoopLagMonitor() {
17
- if (eventLoopLagMonitorStarted || process.env.NODE_ENV === 'test')
20
+ if (eventLoopLagMonitorStarted ||
21
+ process.env.NODE_ENV === 'test' ||
22
+ !perfLoggingEnabled()) {
18
23
  return;
24
+ }
19
25
  eventLoopLagMonitorStarted = true;
20
26
  const intervalMs = 100;
21
27
  const thresholdMs = 250;
@@ -40,28 +46,37 @@ function createPerfTrace(data) {
40
46
  last: start,
41
47
  mark(stage, stageData) {
42
48
  const current = nowMs();
43
- log('[perf] opencode request stage', {
44
- requestId: trace.requestId,
45
- stage,
46
- deltaMs: roundMs(current - trace.last),
47
- totalMs: roundMs(current - trace.start),
48
- ...stageData,
49
- });
49
+ if (perfLoggingEnabled()) {
50
+ log('[perf] opencode request stage', {
51
+ requestId: trace.requestId,
52
+ stage,
53
+ deltaMs: roundMs(current - trace.last),
54
+ totalMs: roundMs(current - trace.start),
55
+ ...stageData,
56
+ });
57
+ }
50
58
  trace.last = current;
51
59
  },
52
60
  done(stage, stageData) {
53
61
  const current = nowMs();
54
- log('[perf] opencode request done', {
55
- requestId: trace.requestId,
56
- stage,
57
- deltaMs: roundMs(current - trace.last),
58
- totalMs: roundMs(current - trace.start),
59
- ...stageData,
60
- });
62
+ if (perfLoggingEnabled()) {
63
+ log('[perf] opencode request done', {
64
+ requestId: trace.requestId,
65
+ stage,
66
+ deltaMs: roundMs(current - trace.last),
67
+ totalMs: roundMs(current - trace.start),
68
+ ...stageData,
69
+ });
70
+ }
61
71
  trace.last = current;
62
72
  },
63
73
  };
64
- log('[perf] opencode request start', { requestId: trace.requestId, ...data });
74
+ if (perfLoggingEnabled()) {
75
+ log('[perf] opencode request start', {
76
+ requestId: trace.requestId,
77
+ ...data,
78
+ });
79
+ }
65
80
  return trace;
66
81
  }
67
82
  async function sendIgnoredMessage(ctx, sessionId, text) {
@@ -98,6 +113,43 @@ export const AnthropicAuthPlugin = async (ctx) => {
98
113
  const { client } = ctx;
99
114
  const fallbackManager = new FallbackAccountManager();
100
115
  fallbackManager.startBackgroundRefresh();
116
+ let latestRefreshMainAccessToken = null;
117
+ const cacheKeepManager = new CacheKeepManager({
118
+ loadStorage: () => loadAccounts(),
119
+ prepareHeaders: async (headers, target) => {
120
+ if (!latestGetAuth)
121
+ return headers;
122
+ const auth = await latestGetAuth();
123
+ if (auth.type !== 'oauth')
124
+ return headers;
125
+ if (!auth.access || (auth.expires && auth.expires < Date.now())) {
126
+ if (!latestRefreshMainAccessToken)
127
+ return headers;
128
+ auth.access = await latestRefreshMainAccessToken();
129
+ }
130
+ if (!auth.access)
131
+ return headers;
132
+ try {
133
+ const parsedBody = JSON.parse(target.bodyText);
134
+ const identity = await resolveClaudeCodeIdentity(auth.access, typeof parsedBody.model === 'string' ? parsedBody.model : undefined);
135
+ headers.delete('anthropic-beta');
136
+ setOAuthHeaders(headers, auth.access, {
137
+ body: parsedBody,
138
+ identity,
139
+ });
140
+ headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [
141
+ CACHE_KEEP_EXTENDED_TTL_BETA,
142
+ ]));
143
+ if (parsedBody.speed === 'fast')
144
+ addFastModeBetaHeader(headers);
145
+ }
146
+ catch {
147
+ setOAuthHeaders(headers, auth.access);
148
+ }
149
+ return headers;
150
+ },
151
+ log,
152
+ });
101
153
  const initialCache1hStorage = await loadAccounts();
102
154
  const relayConfig = getRelayConfig(initialCache1hStorage);
103
155
  setCache1hState({
@@ -116,6 +168,24 @@ export const AnthropicAuthPlugin = async (ctx) => {
116
168
  function mainRefreshEnabled(storage) {
117
169
  return storage?.refresh?.enabled !== false;
118
170
  }
171
+ async function clearStaleMainRefreshError(refreshToken) {
172
+ if (!refreshToken)
173
+ return;
174
+ const storage = await loadAccounts();
175
+ const error = storage?.refresh?.mainLastRefreshError;
176
+ if (!storage?.refresh || !error?.tokenHash)
177
+ return;
178
+ const tokenHash = hashRefreshToken(refreshToken);
179
+ if (error.tokenHash === tokenHash)
180
+ return;
181
+ storage.refresh.mainLastRefreshError = undefined;
182
+ await saveAccounts(storage);
183
+ log('[refresh] opencode main oauth cleared stale backoff after token rotation', {
184
+ previousCheckedAt: error.checkedAt,
185
+ previousNextRetryAt: error.nextRetryAt,
186
+ previousRetryCount: error.retryCount,
187
+ });
188
+ }
119
189
  async function buildQuotaCommandSummary() {
120
190
  const accounts = [];
121
191
  if (latestGetAuth) {
@@ -180,6 +250,26 @@ export const AnthropicAuthPlugin = async (ctx) => {
180
250
  setCache1hState({ enabled, mode });
181
251
  return executeCache1hCommand({ argumentsText, enabled, mode });
182
252
  }
253
+ async function executePersistentCacheKeepCommand(argumentsText) {
254
+ const action = parseCacheKeepCommandAction(argumentsText);
255
+ let storage = await loadAccounts();
256
+ if (action.type === 'window') {
257
+ storage = await setCacheKeepPersistentWindow(action.startHour, action.endHour);
258
+ }
259
+ else if (action.type === 'disable') {
260
+ storage = await setCacheKeepPersistentEnabled(false);
261
+ }
262
+ const window = getCacheKeepWindow(storage);
263
+ const stats = cacheKeepManager.stats(window);
264
+ return executeCacheKeepCommand({
265
+ argumentsText,
266
+ enabled: isCacheKeepPersistentlyEnabled(storage),
267
+ window,
268
+ hybridActive: isCacheKeepHybridActive(storage),
269
+ trackedSessions: stats.trackedSessions,
270
+ nextPrewarmAt: stats.nextPrewarmAt,
271
+ });
272
+ }
183
273
  async function executePersistentDumpCommand(argumentsText) {
184
274
  const action = parseDumpCommandAction(argumentsText);
185
275
  if (action.type === 'enable' || action.type === 'disable') {
@@ -214,6 +304,10 @@ export const AnthropicAuthPlugin = async (ctx) => {
214
304
  template: CACHE_1H_COMMAND_NAME,
215
305
  description: 'Show or toggle 1-hour Anthropic ephemeral prompt cache TTL.',
216
306
  },
307
+ [CLAUDE_CACHE_KEEP_COMMAND_NAME]: {
308
+ template: CLAUDE_CACHE_KEEP_COMMAND_NAME,
309
+ description: 'Keep hybrid Claude cache warm for recently used sessions during a local time window.',
310
+ },
217
311
  [CLAUDE_QUOTAS_COMMAND_NAME]: {
218
312
  template: CLAUDE_QUOTAS_COMMAND_NAME,
219
313
  description: 'Show current Claude OAuth quota usage for all accounts.',
@@ -233,6 +327,10 @@ export const AnthropicAuthPlugin = async (ctx) => {
233
327
  await sendIgnoredMessage(ctx, input.sessionID, await executePersistentCache1hCommand(input.arguments));
234
328
  throwHandledSentinel();
235
329
  }
330
+ if (input.command === CLAUDE_CACHE_KEEP_COMMAND_NAME) {
331
+ await sendIgnoredMessage(ctx, input.sessionID, await executePersistentCacheKeepCommand(input.arguments));
332
+ throwHandledSentinel();
333
+ }
236
334
  if (input.command === CLAUDE_QUOTAS_COMMAND_NAME) {
237
335
  await sendIgnoredMessage(ctx, input.sessionID, await buildQuotaCommandSummary());
238
336
  throwHandledSentinel();
@@ -303,14 +401,33 @@ export const AnthropicAuthPlugin = async (ctx) => {
303
401
  const storage = await loadAccounts();
304
402
  const refreshTokenHash = hashRefreshToken(freshAuth.refresh);
305
403
  const mainError = storage?.refresh?.mainLastRefreshError;
404
+ log('[refresh] opencode main oauth refresh check', {
405
+ attempt,
406
+ expiresInMs: freshAuth.expires
407
+ ? freshAuth.expires - Date.now()
408
+ : undefined,
409
+ hasBackoff: Boolean(mainError),
410
+ backoffActive: mainError
411
+ ? refreshBackoffActive(mainError, freshAuth.refresh, Date.now())
412
+ : false,
413
+ retryCount: mainError?.retryCount,
414
+ nextRetryAt: mainError?.nextRetryAt,
415
+ });
306
416
  if (mainError &&
307
417
  refreshBackoffActive(mainError, freshAuth.refresh, Date.now())) {
418
+ log('[refresh] opencode main oauth refresh skipped backoff', {
419
+ nextRetryAt: mainError.nextRetryAt,
420
+ retryCount: mainError.retryCount,
421
+ });
308
422
  throw new Error(formatRefreshBackoffMessage(mainError, Date.now()));
309
423
  }
310
424
  if (storage?.refresh?.mainRefreshLeaseUntil &&
311
425
  storage.refresh.mainRefreshLeaseUntil > Date.now() &&
312
426
  storage.refresh.mainRefreshLeaseTokenHash ===
313
427
  refreshTokenHash) {
428
+ log('[refresh] opencode main oauth refresh skipped lease', {
429
+ leaseUntil: storage.refresh.mainRefreshLeaseUntil,
430
+ });
314
431
  throw new Error('Claude OAuth refresh is already in progress');
315
432
  }
316
433
  leaseId = randomUUID();
@@ -325,11 +442,18 @@ export const AnthropicAuthPlugin = async (ctx) => {
325
442
  refreshTokenHash;
326
443
  });
327
444
  const latestLease = await loadAccounts();
445
+ log('[refresh] opencode main oauth refresh lease acquired', {
446
+ attempt,
447
+ leaseUntil: Date.now() + 2 * 60_000,
448
+ });
328
449
  if (latestLease?.refresh?.mainRefreshLeaseId !== leaseId ||
329
450
  latestLease.refresh.mainRefreshLeaseTokenHash !==
330
451
  refreshTokenHash) {
331
452
  throw new Error('Claude OAuth refresh is already in progress');
332
453
  }
454
+ log('[refresh] opencode main oauth refresh request start', {
455
+ attempt,
456
+ });
333
457
  const refreshed = await refreshClaudeOAuthToken({
334
458
  refreshToken: freshAuth.refresh,
335
459
  });
@@ -355,6 +479,10 @@ export const AnthropicAuthPlugin = async (ctx) => {
355
479
  storage.refresh.mainRefreshLeaseTokenHash = undefined;
356
480
  }
357
481
  });
482
+ log('[refresh] opencode main oauth refresh succeeded', {
483
+ attempt,
484
+ expiresInMs: refreshed.expires - Date.now(),
485
+ });
358
486
  return refreshed.access;
359
487
  }
360
488
  catch (error) {
@@ -371,6 +499,13 @@ export const AnthropicAuthPlugin = async (ctx) => {
371
499
  error.status >= 500))) {
372
500
  continue;
373
501
  }
502
+ log('[refresh] opencode main oauth refresh attempt failed', {
503
+ attempt,
504
+ error: error instanceof Error
505
+ ? error.message
506
+ : String(error),
507
+ transient: isNetworkError,
508
+ });
374
509
  const failedRefreshToken = freshAuth?.refresh;
375
510
  if (failedRefreshToken &&
376
511
  error instanceof ClaudeOAuthRefreshError) {
@@ -412,6 +547,7 @@ export const AnthropicAuthPlugin = async (ctx) => {
412
547
  }
413
548
  return refreshPromise;
414
549
  }
550
+ latestRefreshMainAccessToken = refreshMainAccessToken;
415
551
  function startMainBackgroundRefresh() {
416
552
  if (mainBackgroundRefreshTimer) {
417
553
  clearInterval(mainBackgroundRefreshTimer);
@@ -425,14 +561,24 @@ export const AnthropicAuthPlugin = async (ctx) => {
425
561
  const latestAuth = await getAuth();
426
562
  if (latestAuth.type !== 'oauth')
427
563
  return;
564
+ await clearStaleMainRefreshError(latestAuth.refresh);
428
565
  if (!latestAuth.expires)
429
566
  return;
430
- if (latestAuth.expires - Date.now() >
431
- mainRefreshBeforeExpiryMs(storage)) {
567
+ const expiresInMs = latestAuth.expires - Date.now();
568
+ const refreshBeforeMs = mainRefreshBeforeExpiryMs(storage);
569
+ if (expiresInMs > refreshBeforeMs) {
432
570
  return;
433
571
  }
572
+ log('[refresh] opencode main oauth background due', {
573
+ expiresInMs,
574
+ refreshBeforeMs,
575
+ });
434
576
  if (latestAuth.refresh &&
435
577
  refreshBackoffActive(storage?.refresh?.mainLastRefreshError, latestAuth.refresh, Date.now())) {
578
+ log('[refresh] opencode main oauth background skipped backoff', {
579
+ nextRetryAt: storage?.refresh?.mainLastRefreshError?.nextRetryAt,
580
+ retryCount: storage?.refresh?.mainLastRefreshError?.retryCount,
581
+ });
436
582
  return;
437
583
  }
438
584
  if (latestAuth.refresh &&
@@ -607,6 +753,24 @@ export const AnthropicAuthPlugin = async (ctx) => {
607
753
  });
608
754
  }
609
755
  const rewritten = rewriteUrl(input);
756
+ if (route === 'main' &&
757
+ typeof body === 'string' &&
758
+ !subagentRequest &&
759
+ isCache1hEnabled() &&
760
+ getCache1hMode() === 'hybrid') {
761
+ const storage = await loadAccounts();
762
+ const tracked = await cacheKeepManager.track({
763
+ sessionId: relayAffinity,
764
+ url: rewritten.url?.toString() ?? rewritten.input.toString(),
765
+ headers: requestHeaders,
766
+ bodyText: body,
767
+ storage,
768
+ cacheMode: 'hybrid',
769
+ });
770
+ if (tracked.tracked) {
771
+ trace?.mark('cachekeep_track', { session: relayAffinity });
772
+ }
773
+ }
610
774
  const directFetch = () => fetch(rewritten.input, {
611
775
  ...init,
612
776
  body,
@@ -746,7 +910,14 @@ export const AnthropicAuthPlugin = async (ctx) => {
746
910
  trace.done('non_oauth_passthrough', { status: response.status });
747
911
  return response;
748
912
  }
913
+ await clearStaleMainRefreshError(auth.refresh);
749
914
  if (!auth.access || !auth.expires || auth.expires < Date.now()) {
915
+ log('[refresh] opencode main oauth refresh required for request', {
916
+ hasAccess: Boolean(auth.access),
917
+ expiresInMs: auth.expires
918
+ ? auth.expires - Date.now()
919
+ : undefined,
920
+ });
750
921
  const refreshStart = nowMs();
751
922
  auth.access = await refreshMainAccessToken();
752
923
  trace.mark('refresh_main_access', {
@@ -63,7 +63,10 @@ export async function resolvePromptContext(client, sessionId) {
63
63
  return null;
64
64
  let messages = [];
65
65
  try {
66
- messages = extractMessages(await Promise.resolve(typedClient.session.messages({ path: { id: sessionId } })));
66
+ messages = extractMessages(await Promise.resolve(typedClient.session.messages({
67
+ path: { id: sessionId },
68
+ query: { limit: 100 },
69
+ })));
67
70
  }
68
71
  catch {
69
72
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-anthropic-auth",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,6 +40,6 @@
40
40
  "@opencode-ai/plugin": "*"
41
41
  },
42
42
  "dependencies": {
43
- "@cortexkit/anthropic-auth-core": "1.1.1"
43
+ "@cortexkit/anthropic-auth-core": "1.1.2"
44
44
  }
45
45
  }