Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@
3. Removes response-only fields (streaming, thinking, structured output, forced tool choice) from the pre-warm body
4. Each manager publishes session IDs and cache timing (never request bodies, headers, or tokens) to a host-scoped temporary lease registry; status commands aggregate live records across project/plugin processes, and stale process records age out after three minutes. Schedule mode can be a local hour window or `always`, which remains active across midnight while the process is open

**Quota Priming Flow:**
1. `/claude-prime` parses status, `on`, and `off` actions; toggles persist only the `prime.enabled` flag in account configuration, while status projection combines the main account and enabled OAuth fallbacks with due times, transient attempt results, persisted usage counters, and estimated cost — `packages/core/src/prime.ts`, `packages/core/src/accounts.ts`, `packages/opencode/src/index.ts`
2. The OpenCode plugin adopts one `PrimeManager` per account-storage identity through a process-wide registry; instances sharing a storage path rebind the existing manager on reload, while a storage-path change stops and removes the old manager when its last project slot leaves — `packages/opencode/src/prime-manager-registry.ts`, `packages/opencode/src/index.ts`
3. An enabled manager runs an unref'd 60-second interval; every tick sweeps stale markers, reloads persisted state, and evaluates the main account plus configured accounts. An account is due when its persisted five-hour `resetsAt` plus the 60-second due offset has passed; an unknown reset uses the bootstrap claim path — `packages/core/src/prime.ts`
4. A due OAuth account performs one fresh quota check through `refreshMainWithMetadata()` or the fallback refresh path before claiming. Cached or stale results never claim, a future fresh reset marks the window active instead of priming, and the Haiku request is rejected by the account or model-scoped killswitch policy before claim — `packages/core/src/prime.ts`, `packages/core/src/quota-manager.ts`, `packages/opencode/src/index.ts`, `packages/core/src/accounts.ts`
5. The manager derives a storage-path namespace and account-auth-lineage fingerprint, then atomically creates an account-and-reset marker with `writeFile(..., { flag: 'wx' })`; the storage namespace isolates independent config stores, the account namespace prevents credential-lineage reuse, and the first process to create the marker is the only process that fires for that reset epoch — `packages/core/src/prime.ts`, `packages/core/src/accounts.ts`, `packages/opencode/src/index.ts`
6. The winner sends one authenticated `POST` request through the normal request rewrite path with model `claude-haiku-4-5`, `max_tokens: 1`, system instruction `Reply with 1 when you receive 0.`, and user message `0`; the request has a 30-second timeout and records response usage when available — `packages/core/src/prime.ts`, `packages/core/src/models.ts`, `packages/opencode/src/index.ts`
7. A successful fire atomically increments the account's cumulative input/output token counters and count in runtime state, updates manager/sidebar status, and schedules a 90-second unref'd quota refresh so the newly started window is persisted after usage propagation; stopping or disabling the feature aborts in-flight work before claim or send — `packages/core/src/accounts.ts`, `packages/core/src/prime.ts`, `packages/opencode/src/index.ts`, `packages/opencode/src/sidebar-state.ts`
8. Marker cleanup recursively removes files older than six hours from the storage namespace, allowing abandoned claims to age out without allowing concurrent processes to reclaim a live marker — `packages/core/src/prime.ts`

## Key Abstractions

**QuotaManager:**
Expand Down Expand Up @@ -103,6 +113,16 @@
- Location: `packages/core/src/cachekeep-registry.ts`
- Pattern: One atomic JSON lease record per manager under the system temporary directory; records contain only session IDs and cache timing, are separated into OpenCode/Pi scopes, deduplicate by session ID, and are ignored after a three-minute stale lease

**PrimeManager:**
- Purpose: Schedules opt-in five-hour quota priming for each OAuth account and projects per-account status
- Location: `packages/core/src/prime.ts`
- Pattern: Unref'd 60-second interval with fresh quota and killswitch gates, cross-process atomic marker claims, minimal Haiku requests, runtime usage persistence, delayed quota refresh, and idempotent start/stop lifecycle

**PrimeManagerRegistry:**
- Purpose: Shares one PrimeManager across OpenCode plugin instances that use the same account storage path
- Location: `packages/opencode/src/prime-manager-registry.ts`
- Pattern: Process-wide map keyed by a SHA-256 storage-path fingerprint; project slots rebind the manager on reload, and a storage-path change stops and removes an unreferenced manager

**ServerSideFallbackStreamRewriter:**
- Purpose: Opts eligible OAuth Fable 5/Opus 5 requests into Anthropic's default safety fallback, preserves fallback conversation boundaries across OpenCode storage, and classifies active/restored fallback outcomes
- Location: `packages/opencode/src/server-fallback.ts`
Expand Down Expand Up @@ -171,6 +191,6 @@

**Caching:** In-memory quota cache (`QuotaManager`) with staleness-based refresh logic; memoized system prompt sanitization (`sanitize-memo.ts`) with 8MB max; 1-hour Anthropic prompt cache managed via cache strategy (`cache1h.ts`, `cachekeep.ts`)

**Storage:** Sidecar JSON files for config + credential/quota state (separate files to avoid config overwrite), a cross-process locked `anthropic-auth-routing-state.json` containing hashed sticky session assignments, JSONC preferences for the TUI (`tui-preferences.jsonc`), JSON state for TUI sidebar IPC at `$TMPDIR/opencode-anthropic-auth/`, and an auto-sweeping dump directory for request payloads capped at 512MB by default.
**Storage:** Sidecar JSON files for config + credential/quota state (separate files to avoid config overwrite), a cross-process locked `anthropic-auth-routing-state.json` containing hashed sticky session assignments, a prime marker directory at `$TMPDIR/opencode-anthropic-auth/prime/<storage-fingerprint>/<account-fingerprint>/`, where `anthropic-auth.json` stores the prime opt-in flag and `anthropic-auth-state.json` stores per-account prime usage counters and lineage bindings, JSONC preferences for the TUI (`tui-preferences.jsonc`), JSON state for TUI sidebar IPC at `$TMPDIR/opencode-anthropic-auth/`, and an auto-sweeping dump directory for request payloads capped at 512MB by default.

**Security:** OAuth tokens are stored in the sidecar state file (separate from config); sticky routing stores only SHA-256 session hashes, account IDs, quota timestamps, and initial input byte counts; relay uses a shared secret token; RPC server uses a bearer token; token refresh and configuration/routing writes use file locks to prevent races and concurrent write loss; no secrets are stored in git
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This repo is a CortexKit-maintained Anthropic auth monorepo for OpenCode and Pi.

## Unreleased

- Add opt-in `/claude-prime` scheduling that sends a minimal Haiku 4.5 request shortly after each OAuth account's five-hour quota reset so each window starts immediately.

## 1.19.1

### Patch Changes
Expand Down
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
| Provider integration point | OpenCode plugin fetch/request transform | Pi `registerProvider("anthropic")` provider override |
| Sidecar config | `~/.config/opencode/anthropic-auth.json` | `~/.pi/agent/anthropic-auth.json` |
| Runtime state | `~/.config/opencode/anthropic-auth-state.json` | next to the Pi sidecar as `anthropic-auth-state.json` |
| Commands | `/claude-cache`, `/claude-cachekeep`, `/claude-routing`, `/claude-fast`, `/claude-quota`, `/claude-dump`, `/claude-killswitch` | `/claude-cache`, `/claude-cachekeep`, `/claude-routing`, `/claude-fast`, `/claude-quota`, `/claude-dump` |
| Commands | `/claude-cache`, `/claude-cachekeep`, `/claude-prime`, `/claude-routing`, `/claude-fast`, `/claude-quota`, `/claude-dump`, `/claude-killswitch` | `/claude-cache`, `/claude-cachekeep`, `/claude-prime` (status only), `/claude-routing`, `/claude-fast`, `/claude-quota`, `/claude-dump` |
| Quota sidebar widget | OpenCode TUI plugin via `tui.json` | Not available |
| Fallback accounts, quota routing, killswitch, relay, dumps, fast mode | Supported | Supported through the same shared core and Pi sidecar |

Expand All @@ -31,6 +31,7 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
- **Quota-aware routing**: skip main or fallback accounts when their 5-hour or 7-day Claude quota falls below your configured minimum.
- **Persistent Claude cache controls**: manage Anthropic 1-hour prompt caching from `/claude-cache` with explicit, automatic, or hybrid modes.
- **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.
- **Quota window priming**: opt in with `/claude-prime on` to start each 5-hour quota window about one minute after it resets instead of waiting for the next normal prompt.
- **Fast mode toggle**: use `/claude-fast on|off` to request Anthropic fast mode for supported Opus models.
- **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.
- **Fable/Opus 5 safety fallback (OpenCode)**: eligible OAuth requests try Anthropic's server-side safety fallback first. The plugin preserves Anthropic's fallback conversation boundary across OpenCode history and automatically starts its deterministic 10-response Opus 4.8 recovery if the response still ends in refusal. The TUI sidebar and OpenCode Desktop report the active target model and restoration. Set `OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE=legacy` to bypass the server policy and use client-side recovery exclusively.
Expand All @@ -49,7 +50,7 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
- Support fallback Claude accounts stored in a local per-agent sidecar file.
- Keep fallback OAuth tokens fresh in the background.
- Apply quota thresholds before routing to main or fallback accounts.
- Add `/claude-cache`, `/claude-cachekeep`, `/claude-fast`, `/claude-quota`, and `/claude-dump` commands.
- Add `/claude-cache`, `/claude-cachekeep`, `/claude-prime`, `/claude-fast`, `/claude-quota`, and `/claude-dump` commands.
- Optionally relay large requests through a Cloudflare Worker owned by the user.

## Install
Expand Down Expand Up @@ -350,6 +351,7 @@ In the OpenCode TUI, the `/claude-*` commands open interactive modal dialogs ins
- `/claude-dump` — toggle request dump capture on or off.
- `/claude-cache` — select the 1-hour cache mode (off, explicit, automatic, or hybrid).
- `/claude-cachekeep` — select `always`, enter a cache keepalive window (`HH-HH`), or turn it `off`.
- `/claude-prime` — turn quota-window priming on or off, or view its status and usage.
- `/claude-killswitch` — enable or disable the killswitch, or edit per-account `5h,1w,scoped` thresholds.

Applying a change in a modal persists it through the same configuration the slash arguments use, so the modal and the typed command (`/claude-routing fallback-first`, etc.) are equivalent. Outside the OpenCode TUI (OpenCode desktop or headless), the commands print their text summary as before; Pi is unaffected.
Expand Down Expand Up @@ -487,6 +489,24 @@ Request bodies, headers, and tokens remain in memory. A lease-backed file under

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.

## Claude quota window priming

Quota-window priming is off by default. OpenCode controls it with:

```text
/claude-prime
/claude-prime on
/claude-prime off
```

When enabled, the plugin watches each OAuth account's 5-hour quota reset. About one minute after a confirmed reset, it sends one minimal `claude-haiku-4-5` request so the next window starts without waiting for a normal prompt. Usage is measured from response accounting.

Scheduled fires happen only after the quota window has reset. If an idle account has no cached reset time, the plugin sends one bootstrap request to establish its first observed window. Atomic temporary-file claims ensure that multiple OpenCode processes sharing the same account config send only one request per account and reset.

Prime marker identities live in `anthropic-auth-state.json`. Plugin-owned refresh rotations preserve the main account's lineage, while a host credential replacement creates a new lineage. An existing main lineage without a refresh-token binding attaches to the current credential on its first check without changing identity. On upgrade, an existing fallback account receives an identity during its first prime check; that one-time marker change can send one extra request in the current window.

Pi exposes `/claude-prime` as a status-only command. Its `on` and `off` arguments are ignored; enable or disable priming from OpenCode.

## Claude fast mode

Both OpenCode and Pi packages can persistently request Anthropic fast mode for supported Opus models:
Expand Down
Loading
Loading