The code is this file's marker for "a race was detected" - the same one
the reappeared-path and concurrent-replacement guards raise - not a
report that a no-replace link() or open('wx') hit an existing path. Say
so at the throw, so debugging by err.code does not send anyone looking
for a no-replace failure that never happened.
Built [OnSteroids](https://onsteroids.ai)
createFileNoReplace and publishCanonicalContent carried the same
open-wx / fchmod / write / fsync / close block and the same cleanup
handler. Extract writeDurableTempFile and discardTempFile so each
publisher is left with only what distinguishes it: a no-replace link, or
the compare-and-swap guard and the rename.
Also spell out in publishCanonicalContent that its guard is read-then-act
rather than atomic. POSIX has no compare-and-swap rename, so the window
is narrowed from the ~100 ms the old claim-and-republish path left open
to two adjacent syscalls, not closed - and the pre-image sidecar is what
keeps that last outcome recoverable. Worth stating so the guard is not
mistaken for a strict guarantee later.
Built [OnSteroids](https://onsteroids.ai)
Build the canonical identity from the stat getCanonicalFile already
takes, instead of a second lstat of the same inode. One syscall less,
and mode, mtime and identity now describe the same moment rather than
two adjacent ones.
Assert in the adoption race tests that the foreign writer never fired.
It writes only when the canonical path is observed empty, so a zero
count states the invariant the fix establishes - the path is never left
without a regular file - instead of only checking the final content.
Built [OnSteroids](https://onsteroids.ai)
Adoption moved the canonical settings.json aside with rename() and left
the path empty until publication, roughly 100 ms later. Claude Code or a
second `ccs` starting inside that window found no file and seeded an
empty placeholder; publication then failed with EEXIST because link() is
no-replace, and the rollback published a backup and unlinked the claim,
destroying the only remaining copy of the user's settings. Recovering
meant digging through sidecar files by hand.
Publish by replacement instead: write a temp file next to the canonical
inode and rename() it over the target, so the path always holds a regular
file and no placeholder can be seeded. A compare-and-swap guard on
(ino, mtime, size) runs immediately before the rename and refuses to
publish when the canonical inode changed since it was read, so a writer
that got there first is still never clobbered. The pre-image backup is
published before the replacement, keeping the old content recoverable if
publication is interrupted.
Drops the canonical claim entirely along with restoreCanonicalClaim, and
folds the two identical sidecar publishers into one helper.
recoverOrphanedCanonicalClaim stays, since claims written by older
versions may still be on disk.
New tests cover both writers seen in the incident: Claude Code seeding
`{}` with a trailing newline, and a second `ccs` seeding the 2-byte
variant from shared-dir-linker. Four tests that pinned the claim-based
design were rewritten, among them `preserves a canonical write that
lands during no-replace publication`, whose intent is now enforced by
the CAS guard instead of by an EEXIST from a no-replace link.
Built [OnSteroids](https://onsteroids.ai)
Add explicit `claude-sonnet-5` and `claude-sonnet-5-thinking` entries to the
web-server PRICING_REGISTRY at the standard Sonnet rates ($3/$15 in/out,
$3.75/$0.30 cache).
The model catalog and dashboard already ship Sonnet 5 (it is the Claude
default), but pricing was missing: getModelPricing() fell through to
UNKNOWN_MODEL_PRICING, which only coincidentally matches Sonnet rates. As a
result hasCustomPricing('claude-sonnet-5') returned false and getKnownModels()
omitted it, so Sonnet 5 was treated as an unknown model in cost accounting and
known-model listings.
Registering it explicitly makes the pricing intentional and consistent with
every other Claude model, without changing any resolved values.
Built [OnSteroids](https://onsteroids.ai)
generateThinkingSignature() returned an object
({type, hash, length, timestamp}) but Anthropic requires a thinking
block's `signature` to be a non-empty opaque STRING. The object made
the thinking block invalid, breaking the stream against reasoning
backends and surfacing as a false 502 "did not respond within 600s".
Return a deterministic base64 token instead (no Date.now()), and
narrow ThinkingSignature to a string alias so existing imports stay
intact. The signature is only ever assigned, never read as an object,
so consumers are unaffected. Update the existing unit test to the
corrected string contract.
Built [OnSteroids](https://onsteroids.ai)
Add two contract tests on the public proxy path
createAnthropicProxyResponse: JSON and SSE. Anthropic requires a
thinking block's signature to be a non-empty opaque string, but ccs
currently fabricates the signature as an object via
generateThinkingSignature(), which breaks the stream against reasoning
backends.
Built [OnSteroids](https://onsteroids.ai)
Final review polish:
- Add an inline comment on the claude-opus-4-7-thinking registry entry
explaining it is a pricing-only id kept for historical analytics data
(no catalog model after Opus 4.7 moved to adaptive thinking levels),
and why it carries no fast tier.
- Add a test asserting applyServiceTier preserves the serviceTiers map on
its result, guarding against a future simplification dropping it.
Built [OnSteroids](https://onsteroids.ai)
Address presto review on the fast-tier work:
- Move the claude-opus-4-8 entry after claude-opus-4-7-thinking so the
registry keeps chronological 4.6 -> 4.7 -> 4.8 ordering (the 4.8 insert
had split the 4-7 / 4-7-thinking pair).
- Drop the fast serviceTier from claude-opus-4-7-thinking: that id is a
legacy pricing-only entry with no catalog model, so a fast premium on it
is meaningless. The active 4.6-thinking variant (agy provider) keeps its
fast tier.
- Extend the 4.7 fast-tier test with cache-rate assertions and add an
equivalent 4.6 fast-tier test so the derived buildRates math is covered
for every premium-tier model.
Built [OnSteroids](https://onsteroids.ai)
Hoist the fast-mode rate sets into OPUS_46_47_FAST_RATES and
OPUS_48_FAST_RATES module constants (built via buildRates) so the
registry entries reference shared values instead of repeating literal
buildRates(...) calls. Also wires the fast tier onto the -thinking
aliases (claude-opus-4-6-thinking / -4-7-thinking) so they bill at the
same premium as their base ids.
Built [OnSteroids](https://onsteroids.ai)
Address review feedback on the per-service-tier pricing:
- Extract `buildRates(input, output)` plus named CACHE_5M_WRITE_MULTIPLIER
/ CACHE_READ_MULTIPLIER constants so fast-tier cache rates are derived
from Anthropic's documented multipliers instead of hand-computed numbers.
This removes the inconsistency where Opus 4.6 fast-tier lacked the
explanatory inline comments that 4.7/4.8 carried (presto suggestion #3).
- Document in `applyServiceTier` that no production caller passes
`serviceTier` yet (Anthropic's service_tier is not captured on
CliproxyRequestDetail), so fast-mode usage is currently billed at the
standard rate until the usage pipeline records the tier.
- Add a combined date-suffix + fast-tier lookup test to prove the two
resolution stages compose.
Built [OnSteroids](https://onsteroids.ai)
Commit 72ea1fc9 ("fix(accounts): simplify codex free tier badges") split
free codex accounts out of the `personal` audience into a distinct
`free` audience and dropped the `'Personal · '` prefix on inlineLabel,
but `account-visual-groups.test.ts` was not updated and has been
failing in CI ever since.
Update expectations to match the shipped behavior:
- `audience` for `-free` tokens is `'free'`, not `'personal'`
- `inlineLabel` for a bare free account is `'Free'`, not `'Personal · Free'`
- `compactDetailLabel` is `null` when no extra parts are present
- Sort order: business -> personal -> free -> unknown
Drive-by fix unrelated to the Opus 4.8 work in this PR but caught
because these tests started failing on every push to the branch.
Built [OnSteroids](https://onsteroids.ai)
Extend `ModelPricing` with an optional `serviceTiers` map keyed by
Anthropic's `service_tier` request parameter. `getModelPricing(model,
{ serviceTier })` returns the matching tier rates when present and
transparently falls through to base rates otherwise — existing call
sites keep current behavior.
Registry entries added (per Anthropic Fast mode pricing docs):
- claude-opus-4-8 fast: $10/$50 input/output (2x premium)
- claude-opus-4-7 fast: $30/$150 (6x premium)
- claude-opus-4-6 fast: $30/$150 (6x premium)
Cache rates derived from the documented Prompt caching multipliers
(1.25x for 5-min cache write, 0.1x for cache read).
Note: tier tracking through CliproxyRequestDetail is not wired yet —
that's a follow-up so usage transformers can pass `serviceTier` when
Anthropic returns it on the response. Schema is in place; integration
can land independently.
Also adds a defensive `claude-opus-4-8-20260530` pricing test to exercise
`stripDateSuffix` even though Anthropic no longer issues date-stamped IDs
for the 4.6+ generation (addresses presto-review suggestion #1).
Built [OnSteroids](https://onsteroids.ai)
Register `claude-opus-4-8` in the cliproxy model catalog, web-server
pricing table, and UI catalog as an additive entry next to 4.7. Defaults
are unchanged (claude provider stays on sonnet-4-6); users opt into 4.8
explicitly via ANTHROPIC_MODEL or the dashboard.
Configuration mirrors 4.7:
- adaptive thinking levels (low through max), max distinct from xhigh
- extendedContext (1M) available
- nativeImageInput
- pricing $5/$25 in/out, $6.25/$0.5 cache
Built [OnSteroids](https://onsteroids.ai)
Bun's `mock.module()` is process-wide and is NOT undone by `mock.restore()`.
Two test files used module-level mocks that leaked across test runs,
silently breaking unrelated suites that imported the mocked modules
transitively:
- tests/unit/cliproxy/version-checker-stale-cache.test.ts mocked
binary/version-checker, contaminating cliproxy-stats-routes-install
and cliproxy-stats-routes-model-update suites that import a route
module which transitively imports version-checker.
- tests/unit/cliproxy/service-manager-startup.test.ts mocked 8 modules
at top level (binary-manager, stats-fetcher, etc.), contaminating any
later file that imports them.
CI happens to be green because file ordering on the GitHub runner places
victim files BEFORE contaminators in `bun test`. On Linux locally the
order is reversed, producing 6 reproducible test failures on every
`bun run test:fast` run. A change in OS, bun version, or new test files
that import the mocked modules could silently flip the CI runner's
order and surface these failures unexpectedly.
Replaced module-level mocks with explicit dependency-injection seams in
production code, following the existing pattern in version-checker.ts
and version-cache.ts (`fetchJsonFn?`, `fetchLatestVersionFn?`):
- src/cliproxy/types.ts: `BinaryManagerConfig.checkForUpdatesFn` (optional)
- src/cliproxy/binary/lifecycle.ts: route through new fn with default
- src/cliproxy/service-manager.ts: `ensureCliproxyService(deps?)` with
optional `ensureBinaryFn`, `detectRunningProxyFn`,
`configNeedsRegenerationFn`, `withStartupLockFn`
All deps fields are optional with real-implementation defaults, so
production callers are unchanged. Tests now inject deterministic stubs
through call-site parameters instead of module-level mocks.
Verified locally: \`bun run validate\` is now 2540 pass / 0 fail
(previously 2534 pass / 6 fail at the same upstream/main HEAD).
Built [OnSteroids](https://onsteroids.ai)
Anthropic exposes `max` as a distinct adaptive-thinking effort above
`xhigh` on Opus 4.7. Previously the validator aliased user input `max`
down to `xhigh`, so users couldn't reach the real top-tier effort
through CCS.
Extend the validator:
- Add `max` to VALID_THINKING_LEVELS and THINKING_LEVEL_RANK (rank 6,
above xhigh)
- Add `max` to THINKING_LEVEL_BUDGETS (65536, CCS-internal numeric
mapping for closest-level lookups)
- Widen ThinkingSupport.maxLevel union to include 'max'
The existing `max: 'xhigh'` alias in findClosestLevel is kept as a
graceful fallback for models whose levels list does not include `max`
(e.g. Codex `gpt-5.4`), because exact-match on validLevels takes
priority — so Opus 4.7 returns `max` directly while Codex still maps
`max` -> `xhigh`.
Update the claude.claude-opus-4-7 catalog entry to expose
`['low', 'medium', 'high', 'xhigh', 'max']` with `maxLevel: 'max'`.
Built [OnSteroids](https://onsteroids.ai)
Co-Authored-By: OnSteroids <[email protected]>
Per Anthropic docs, Claude Opus 4.7 only supports adaptive thinking;
manual `thinking.type: "enabled"` with `budget_tokens` is rejected
with HTTP 400.
Switch the claude provider's `claude-opus-4-7` entry from
`type: 'budget'` to `type: 'levels'` with the effort tiers exposed
by the API: `low | medium | high | xhigh`. The proxy is expected to
translate these into `thinking.type: "adaptive"` with the effort
parameter.
Opus 4.6 and Sonnet 4.6 keep `type: 'budget'` for now since the
deprecated mode is still functional on those models.
Built [OnSteroids](https://onsteroids.ai)
Co-Authored-By: OnSteroids <[email protected]>
Mirrors the existing `claude-opus-4-6` cache pricing test for both
`claude-opus-4-7` and `claude-opus-4-7-thinking` to explicitly
validate cache creation/read rates in end-to-end cost calculation.
Addresses PResto atrvd/ccs#8 issue #6 (suggestion).
Built [OnSteroids](https://onsteroids.ai)
Adds a unit test verifying that `claude-opus-4-7` is registered in
the `claude` provider with its provider-specific settings:
- `thinking.zeroAllowed: false` (contrasts with AGY where it is true)
- `extendedContext: true` (1M context via Anthropic API)
Addresses PResto atrvd/ccs#8 issue #4 (suggestion).
Built [OnSteroids](https://onsteroids.ai)
- model-catalog: change `claude-opus-4-7` description from "Latest
flagship without thinking" to "Latest flagship model". The model
supports thinking budget (min 1024, max 128000) but doesn't enable
it by default; the old wording implied no thinking support, which
was misleading. Aligns with the `claude` provider's description.
- generator: add DEFAULT_ANTIGRAVITY_ALIASES entry for non-thinking
`claude-opus-4-7`, restoring symmetry with `claude-sonnet-4-6`
(which has both -thinking and non-thinking aliases). Bump
CLIPROXY_CONFIG_VERSION 18 → 19 to trigger regeneration on upgrade.
Addresses PResto atrvd/ccs#8 issues #2 (important) and #3 (suggestion).
Built [OnSteroids](https://onsteroids.ai)
Addresses PResto review (atrvd/ccs#8, issue #1): bumping
CLIPROXY_CONFIG_VERSION 17→18 was missing a changelog entry in the
header comment. Adds the v18 line to preserve regeneration history.
Built [OnSteroids](https://onsteroids.ai)
Adds Claude Opus 4.7 as a new model to the AGY (Antigravity) and Claude
provider catalogs, with pricing, Cursor IDE support, and updated defaults
across the codebase.
- Add claude-opus-4-7-thinking and claude-opus-4-7 to AGY catalog
(defaultModel bumped to claude-opus-4-7-thinking)
- Add claude-opus-4-7 entry to Claude provider catalog
- Register claude-opus-4-7-thinking fork alias in CLIProxy config
(CLIPROXY_CONFIG_VERSION bumped 17 -> 18)
- Add pricing for claude-opus-4-7 and claude-opus-4-7-thinking
($5/$25 per million, matching Opus 4.6)
- Add claude-4.7-opus and claude-4.7-opus-fast-mode to Cursor catalog
- Update ANTHROPIC_DEFAULT_OPUS_MODEL in base-claude settings to 4.7
- Update Droid adapter and code-reviewer fallback models to 4.7
- Update unit tests for model-catalog and model-pricing
Opus 4.6 is retained as a supported fallback in both catalogs.
Built [OnSteroids](https://onsteroids.ai)
Husky v9 invokes hooks via `sh -e` regardless of the script's shebang,
causing `set -o pipefail` and bash regex `[[ =~ ]]` in .husky/pre-push
to fail with "Illegal option -o pipefail" on systems where /bin/sh is
not bash (Debian/Ubuntu dash).
Add a POSIX-compatible preamble that re-execs the script under bash
when invoked without BASH_VERSION. No changes to gate logic.
Built [OnSteroids](https://onsteroids.ai)
- Add null check for catalog entries from Partial<Record> type
- Include issueUrl in JSON output for broken model tracking
Built [OnSteroids](https://onsteroids.ai)
- Include thinking support configuration (type, min, max, levels) in
JSON output for reasoning-capable models
- Add test verifying explicit false booleans are preserved (not omitted)
- Add test verifying thinking config appears for thinking models
Built [OnSteroids](https://onsteroids.ai)
Use !== undefined checks instead of truthy checks so that explicitly
set false booleans (e.g. extendedContext: false) appear in JSON output,
allowing consumers to distinguish "not set" from "explicitly disabled".
Built [OnSteroids](https://onsteroids.ai)
- Add unit tests verifying JSON output structure, field filtering,
and minified format
- Document that --json takes priority over subcommands (refresh/reset)
Built [OnSteroids](https://onsteroids.ai)
- Add null check for catalog entries from Partial<Record> type
- Include issueUrl in JSON output for broken model tracking
Built [OnSteroids](https://onsteroids.ai)
- Include thinking support configuration (type, min, max, levels) in
JSON output for reasoning-capable models
- Add test verifying explicit false booleans are preserved (not omitted)
- Add test verifying thinking config appears for thinking models
Built [OnSteroids](https://onsteroids.ai)
Use !== undefined checks instead of truthy checks so that explicitly
set false booleans (e.g. extendedContext: false) appear in JSON output,
allowing consumers to distinguish "not set" from "explicitly disabled".
Built [OnSteroids](https://onsteroids.ai)
- Add unit tests verifying JSON output structure, field filtering,
and minified format
- Document that --json takes priority over subcommands (refresh/reset)
Built [OnSteroids](https://onsteroids.ai)
Align with claudecode-env-stripping.test.ts style — explain why
CLAUDE_CONFIG_DIR is cleared in beforeEach for consistency across
test files.
Built [OnSteroids](https://onsteroids.ai)
- Extract NEUTRAL_PROFILE_TYPE constant with JSDoc explaining why
CCS_PROFILE_TYPE must be neutralised in subprocess env blocks
- Replace 6 bare '' occurrences with the named constant (DRY)
- Group save/delete operations in beforeEach with section comments
Built [OnSteroids](https://onsteroids.ai)
When tests run inside a CCS-managed Claude session, host env vars
(CLAUDE_CONFIG_DIR, CCS_PROFILE_TYPE) leak into subprocess spawns
and fixture setup, causing 9 test failures:
- persist-command-handler: reads real symlinked settings.json instead
of temp fixture because CLAUDE_CONFIG_DIR overrides scoped CCS_HOME
- websearch-transformer: hook silently exits via shouldSkipHook()
because CCS_PROFILE_TYPE=account triggers native_account_profile skip
- claudecode-env-stripping: normalizeSharedPluginMetadataPaths receives
leaked CLAUDE_CONFIG_DIR instead of undefined for default profiles
Fix: clear CCS-managed env vars in beforeEach and neutralize
CCS_PROFILE_TYPE in subprocess env blocks.
Built [OnSteroids](https://onsteroids.ai)
- Fix timeout test to use HTTPS server (tunnel uses https.request)
- Fix timeout event handling - call reject directly instead of relying on destroy error event
- Fix misleading comment in remote-token-uploader (Authorization header, not X-Management-Key)
- Remove unused imports in test files
Built [OnSteroids](https://onsteroids.ai)
Co-Authored-By: OnSteroids <[email protected]>
- Fix race condition in start() using Promise instead of boolean flag
- Document that tunnel intentionally doesn't limit response sizes (streaming)
- Improve token upload failure visibility with actionable user message
Built [OnSteroids](https://onsteroids.ai)
Co-Authored-By: OnSteroids <[email protected]>