@cortexkit/opencode-anthropic-auth 1.17.0 → 1.19.0

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
@@ -31,8 +31,8 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
31
31
  - **Persistent Claude cache controls**: manage Anthropic 1-hour prompt caching from `/claude-cache` with explicit, automatic, or hybrid modes.
32
32
  - **Cache keepalive**: use `/claude-cachekeep always` or `/claude-cachekeep HH-HH` to pre-warm hybrid cache anchors for active sessions before the 1-hour TTL expires.
33
33
  - **Fast mode toggle**: use `/claude-fast on|off` to request Anthropic fast mode for supported Opus models.
34
- - **Fable/Mythos reasoning visibility**: request summarized adaptive thinking for Claude Fable 5 and Mythos 5 so agents can display reasoning summaries instead of blank signed-thinking blocks.
35
- - **Fable content-filter recovery**: when Fable ends a session response with Anthropic's `refusal` content-filter reason, transparently retry with Opus 4.8 for 10 successful model responses. After each Opus response, a zero-output Fable prewarm advances the same prompt cache using the OAuth account that served the filtered Fable request. The latest Opus cache boundary is retained so a later refusal can bridge back to Opus even after more than 20 Fable content blocks; the selected OpenCode model remains Fable. The TUI sidebar shows the per-session recovery countdown and return to Fable, while OpenCode Desktop receives ignored `promptAsync` notices for both transitions.
34
+ - **Adaptive reasoning visibility**: request summarized adaptive thinking for Claude Fable 5, Mythos 5, and Opus 5. OpenCode receives native `low`, `medium`, `high`, `xhigh`, and `max` Opus 5 effort variants rather than legacy manual-thinking budgets.
35
+ - **Fable/Opus 5 safety fallback**: eligible OAuth requests opt into Anthropic's server-side safety fallback by default. The plugin preserves Anthropic's fallback conversation boundary across OpenCode history, and the TUI sidebar and OpenCode Desktop report the active target model and restoration. Set `OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE=legacy` to restore the previous deterministic 10-response Opus 4.8 recovery with source-model prewarming.
36
36
  - **Live quota visibility**: use `/claude-quota` to see main and fallback quota state, reset times, and refresh errors.
37
37
  - **Killswitch**: per-account hard-block thresholds that stop requests before hitting Anthropic's rate limits, with synthetic 429 retry-after when all accounts are exhausted.
38
38
  - **User-owned Cloudflare relay**: optionally provision your own Worker relay to reduce repeated client upload bytes for large OpenCode or Pi requests.
@@ -289,6 +289,20 @@ In OpenCode, this includes the main Anthropic account and sidecar fallback accou
289
289
 
290
290
  Reset times are rendered as relative durations, such as `resets in 10m` or `resets in 1h 15m`.
291
291
 
292
+ ## Safety fallback
293
+
294
+ Eligible Fable 5 and Opus 5 OAuth requests use Anthropic's server-side safety fallback by default. The plugin sends `fallbacks: "default"` with Anthropic's server-side fallback beta, preserves fallback conversation boundaries in OpenCode history, and reports model handoffs and restoration in the TUI sidebar or OpenCode Desktop. Follow-up requests may remain on Anthropic's selected fallback model for approximately one hour.
295
+
296
+ Custom API-key routes do not receive the server-side fallback beta or request field.
297
+
298
+ To return to CortexKit's previous deterministic recovery mechanism, start OpenCode with:
299
+
300
+ ```bash
301
+ OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE=legacy opencode
302
+ ```
303
+
304
+ Legacy mode routes a refused Fable 5 or Opus 5 session through Opus 4.8 for 10 successful responses, prewarms the selected source model's cache, and then restores that model. Unset the variable and restart OpenCode to use Anthropic's server-side fallback again.
305
+
292
306
  ## Killswitch
293
307
 
294
308
  The killswitch is a per-account hard-block that stops requests when remaining quota drops below configured thresholds, even if Anthropic's API would still accept them. Unlike `minimumRemaining` (which routes to fallback accounts), the killswitch removes accounts from the routing pool entirely.
@@ -387,7 +401,7 @@ Both OpenCode and Pi packages can persistently request Anthropic fast mode for s
387
401
  /claude-fast off
388
402
  ```
389
403
 
390
- When enabled, supported requests add `speed: "fast"` to the Anthropic JSON body and include the `fast-mode-2026-02-01` beta header. Unsupported models are left at standard speed. Anthropic currently documents fast mode for `claude-opus-4-6`, `claude-opus-4-7`, and `claude-opus-4-8`; Claude Fable 5 and Mythos 5 are not fast-mode models.
404
+ When enabled, supported requests add `speed: "fast"` to the Anthropic JSON body and include the `fast-mode-2026-02-01` beta header. Unsupported models are left at standard speed. Anthropic currently documents fast mode for `claude-opus-4-6`, `claude-opus-4-7`, `claude-opus-4-8`, and `claude-opus-5`; Claude Fable 5 and Mythos 5 are not fast-mode models.
391
405
 
392
406
  Fast and standard speeds do not share prompt-cache prefixes, so switching this setting can cause cache misses.
393
407
 
@@ -517,6 +531,7 @@ Dump state is persisted in the active sidecar config as `dump.enabled` (`~/.conf
517
531
  | `ANTHROPIC_BASE_URL` | Override the Anthropic API endpoint. Must be HTTP(S). |
518
532
  | `ANTHROPIC_INSECURE` | Set to `1` or `true` to skip TLS verification when `ANTHROPIC_BASE_URL` is set. |
519
533
  | `OPENCODE_ANTHROPIC_AUTH_FILE` | Override the OpenCode sidecar config path. |
534
+ | `OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE` | Set to `legacy` to use the previous deterministic 10-response client-side refusal recovery. The default is Anthropic server-side safety fallback. |
520
535
  | `PI_ANTHROPIC_AUTH_FILE` | Override the Pi sidecar config path. |
521
536
  | `PI_AGENT_DIR` | Override Pi's agent directory when deriving the default sidecar path. |
522
537
  | `CLOUDFLARE_API_TOKEN` | Cloudflare token used by `bunx @cortexkit/opencode-anthropic-auth relay setup`. Not stored. |
@@ -532,7 +547,8 @@ For Claude Pro/Max OAuth requests, the plugin works at the final Anthropic wire-
532
547
  4. Prepends Claude Code identity and billing-header blocks.
533
548
  5. Rewrites cache controls according to `/claude-cache` mode.
534
549
  6. Renames MCP tool names into Claude-compatible PascalCase form.
535
- 7. Computes final-body `cch` over the fully serialized request body.
550
+ 7. Opts eligible Fable 5 and Opus 5 OAuth requests into Anthropic's server-side safety fallback and restores stored fallback boundary markers.
551
+ 8. Computes final-body `cch` over the fully serialized request body.
536
552
 
537
553
  The sanitizer is anchor-based: it removes paragraphs containing known OpenCode documentation or source anchors, performs a small set of inline replacements, and preserves the rest of the prompt including user/project instructions, tool policy, environment context, and file paths.
538
554
 
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{C as z,D as N,F as P,H as L,J as E,L as K,Lb as T,M as S,Nb as q,ya as U}from"./index-xvfesm3w.js";import"./index-y2n9h7p6.js";import{rc as w}from"./index-f5r96vyv.js";import{stdin as x,stdout as v}from"node:process";import{createInterface as y}from"node:readline/promises";function R(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function _(){console.log(`Usage:
2
+ import{D as z,E as N,G as P,I as L,K as E,M as K,N as S,Ob as T,Qb as q,za as U}from"./index-h0pmqm96.js";import"./index-1ets4bf8.js";import{uc as w}from"./index-9aeag0wm.js";import{stdin as x,stdout as v}from"node:process";import{createInterface as y}from"node:readline/promises";function R(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function _(){console.log(`Usage:
3
3
  opencode-anthropic-auth login [label]
4
4
  opencode-anthropic-auth api add [label]
5
5
  opencode-anthropic-auth list
@@ -8,6 +8,7 @@ export type FableStandbyCacheAnchor = {
8
8
  };
9
9
  export type FableFallbackPlan = {
10
10
  sessionId: string;
11
+ recoveryKey: string;
11
12
  requestedModel: string;
12
13
  effectiveModel: string;
13
14
  bodyText: string;
@@ -18,9 +19,21 @@ export type FableFallbackPlan = {
18
19
  requestSequence?: number;
19
20
  completed?: boolean;
20
21
  };
22
+ /**
23
+ * Models whose content-filter refusal we recover from by downgrading to Opus 4.8
24
+ * for a short window. Fable 5 is the original case; Opus 5 is identical in
25
+ * shape (cyber safety classifiers that can return `stop_reason: "refusal"`,
26
+ * Anthropic's own fallback default is `claude-opus-4-8`, same pricing). Mythos
27
+ * has no classifiers and Sonnet 5 is not flagged — both stay outside this
28
+ * gate. The single shared fallback id matches Anthropic's default for both
29
+ * source models, so the recovery period is uniform.
30
+ */
31
+ export type RecoverableRefusalFamily = 'fable-5' | 'opus-5';
32
+ export declare function recoverableRefusalFamily(model: unknown): RecoverableRefusalFamily | null;
33
+ export declare function isRecoverableRefusalModel(model: unknown): model is string;
21
34
  export declare class FableFallbackManager {
22
35
  private readonly now;
23
- private readonly sessions;
36
+ private readonly recoveries;
24
37
  private nextCycle;
25
38
  constructor(now?: () => number);
26
39
  private prune;
@@ -32,5 +45,5 @@ export declare class FableFallbackManager {
32
45
  counted: boolean;
33
46
  remaining: number;
34
47
  };
35
- remaining(sessionId: string): number;
48
+ remaining(plan: FableFallbackPlan): number;
36
49
  }
@@ -1,4 +1,4 @@
1
1
  import*as j from"node:fs";import*as P from"node:os";import*as V from"node:path";var U={error:0,warn:1,info:2,debug:3,trace:4},O=new Set(Object.keys(U));function F(q){if(!q)return null;let Q=q.trim().toLowerCase();return O.has(Q)?Q:null}var S=F(process.env.OPENCODE_ANTHROPIC_AUTH_LOG_LEVEL),M=S??"info";function L(q){M=q}function i(){return M}function X(q){return U[q]<=U[M]}var J=process.env.OPENCODE_ANTHROPIC_AUTH_LOG_FILE??V.join(P.tmpdir(),"opencode-anthropic-auth.log");function h(){return J}var R=5242880,N=3;function I(){try{if(!j.existsSync(J))return;if(j.statSync(J).size<R)return}catch{return}try{for(let q=N;q>=1;q--){let Q=q===1?J:`${J}.${q-1}`,W=`${J}.${q}`;try{if(q===N)try{j.unlinkSync(W)}catch{}if(j.existsSync(Q))j.renameSync(Q,W)}catch{}}}catch{}}var T=/^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i;function k(q){if(T.test(q))return!0;let Q=q.toLowerCase().replace(/[-_]/g,"");if(Q.includes("apikey"))return!0;if(Q.endsWith("secret")||Q.endsWith("password"))return!0;if(Q.endsWith("token")&&!Q.endsWith("tokens"))return!0;return!1}var f=[/Bearer\s+\S+/,/sk-[A-Za-z0-9-]+/,/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/];function _(q){if(typeof q!=="string")return!1;return f.some((Q)=>Q.test(q))}function G(q){if(q==null||Object.keys(q).length===0)return;let Q=new WeakSet;function W($){if($==null||typeof $!=="object"){if(_($))return"***REDACTED***";return $}if(Q.has($))return"[Circular]";Q.add($);try{if(Array.isArray($))return $.map(W);let x=$,B={};for(let[H,C]of Object.entries(x)){if(k(H)){B[H]="***REDACTED***";continue}B[H]=W(C)}return B}finally{Q.delete($)}}return W(q)}function g(q,Q,W,$){let x=new Date().toISOString(),B=q.toUpperCase().padEnd(5),H=Q?` [${Q}]`:"",C=(()=>{try{let w=G($),A=w?{...w,pid:process.pid}:{pid:process.pid};return` ${JSON.stringify(A)}`}catch{return` {"pid":${process.pid},"payload":"[unserializable]"}`}})();return`[${x}] ${B}${H} ${W}${C}`}var D=null;function E(q){D=q}function Y(q,Q,W,$){if(!X(q))return;if(D)try{D({level:q,channel:Q,message:W,payload:G($)})}catch{}if(Z)return;try{let x=`${g(q,Q,W,$)}
2
2
  `;if(K.push(x),K.length>=p)b();else r()}catch{}}var m={error(q,Q,W){Y("error",q,Q,W)},warn(q,Q,W){Y("warn",q,Q,W)},info(q,Q,W){Y("info",q,Q,W)},debug(q,Q,W){Y("debug",q,Q,W)},trace(q,Q,W){Y("trace",q,Q,W)}},Z=!1,c=500,p=50,K=[],z=null;function b(){if(z)clearTimeout(z),z=null;if(K.length===0)return;let q=K.join("");K=[];try{I(),j.appendFileSync(J,q)}catch{}}function r(){if(z)return;z=setTimeout(()=>{z=null,b()},c)}var y=/^\[([\w-]+)\]\s*/;function o(q,Q){if(!X("debug"))return;if(Z)return;try{let W="",$=q,x=q.match(y);if(x)W=x[1]??"",$=q.slice(x[0].length);let B;if(Q!=null){if(Q instanceof Error)B={error:`${Q.message}${Q.stack?`
3
3
  ${Q.stack}`:""}`};else if(typeof Q==="object")B=Q}Y("debug",W,$,B)}catch{}}function v(q,Q){if(!X("debug"))return;if(Z)return;try{let W;if(Q!=null){if(typeof Q==="object"&&!(Q instanceof Error))W=Q}Y("debug","relay",q,W)}catch{}}if(!Z)process.on("exit",b);
4
- export{F as _b,L as $b,i as ac,X as bc,h as cc,T as dc,k as ec,G as fc,g as gc,E as hc,m as ic,o as jc,v as kc};
4
+ export{F as bc,L as cc,i as dc,X as ec,h as fc,T as gc,k as hc,G as ic,g as jc,E as kc,m as lc,o as mc,v as nc};
@@ -1,2 +1,2 @@
1
1
  import{createRequire as q}from"node:module";var j=Object.create;var{getPrototypeOf:k,defineProperty:i,getOwnPropertyNames:l}=Object;var m=Object.prototype.hasOwnProperty;function n(a){return this[a]}var o,p,r=(a,b,d)=>{var f=a!=null&&typeof a==="object";if(f){var g=b?o??=new WeakMap:p??=new WeakMap,h=g.get(a);if(h)return h}d=a!=null?j(k(a)):{};let c=b||!a||!a.__esModule?i(d,"default",{value:a,enumerable:!0}):d;for(let e of l(a))if(!m.call(c,e))i(c,e,{get:n.bind(a,e),enumerable:!0});if(f)g.set(a,c);return c};var s=(a,b)=>()=>(b||a((b={exports:{}}).exports,b),b.exports);var u=q(import.meta.url);
2
- export{r as pc,s as qc,u as rc};
2
+ export{r as sc,s as tc,u as uc};