- Allow partial composite updates in PUT endpoint (|| -> &&)
- Map service validation errors to 400 status, not 404
- Widen 4xx regex to cover full 400-499 range
- Thinking config: 24 tests for applyThinkingSuffix, detectTierFromModel,
getThinkingValueForTier, and applyThinkingConfig with compositeTierThinking
- Fallback: 42 tests for isProviderError, detectFailedTier, applyFallback,
and PROVIDER_ERROR_PATTERNS
- Variant service: 9 tests for updateCompositeVariant,
saveCompositeVariantUnified, and listVariantsFromConfig with composite types
- Extend create dialog with composite mode toggle and per-tier config tabs
- Add cliproxy-edit-dialog for modifying existing single and composite variants
- Add Edit action and composite type badge to variant table
- Extend API types with composite variant fields
- Update variant routes: GET (composite fields), POST (composite create), PUT (composite update)
- Add PROVIDER_ERROR_PATTERNS for HTTP 4xx/5xx, overloaded, quota, rate limit
- Add detectFailedTier() to identify which composite tier failed from stderr
- Add isProviderError() to distinguish provider failures from normal exits
- Add handleEdit() interactive wizard for modifying existing variants
- Add updateCompositeVariant() to variant-service for partial tier updates
- Route 'edit' subcommand in cliproxy index
- Update help text with edit command documentation
- Export updateCompositeVariant from services index
- Add fallback? (provider+model) to CompositeTierConfig for per-tier retry
- Add thinking? string to CompositeTierConfig for per-tier thinking budget
- Add hasFallback flag to VariantConfig for runtime detection
- Propagate new fields in listVariantsFromConfig()
Refs #506
- Add --composite flag to 'ccs cliproxy variant create'
- Implement selectTierConfig() interactive prompt per tier
- Add default tier selection after all tiers configured
- Display composite label in auth list and variant remove
- Show per-tier details in remove confirmation
- Update help text with --composite usage
Refs #506
- Add getCompositeEnvVars() for root URL + per-tier model env vars
- Extend ProxyChainConfig with composite fields
- Route composite variants through root URL in buildClaudeEnvironment()
- Multi-provider auth check for composite (all tiers must pass)
- Multi-provider token refresh for composite
- Per-tier broken model check
- Block --config for composite variants
- Pass composite config through to env resolver
Refs #506
- Add saveCompositeVariantUnified() for persisting composite configs
- Add createCompositeVariant() with per-tier backend validation
- Add createCompositeSettingsFile() with root URL + per-tier model env vars
- Add getCompositeRelativeSettingsPath() helper
- Handle composite in listVariantsFromConfig() and removeVariantFromUnifiedConfig()
- Block model update for composite variants (edit config.yaml instead)
- Export new functions from services barrel
Refs #506
Use specific patterns ('rate limit', 'resource_exhausted', 'too many
requests') instead of broad 'rate' or 'limit' substrings that would
false-positive on unrelated errors like 'character limit exceeded'.
HIGH PRIORITY FIXES:
- Extract shared buffer parsing logic into parseProtobufFrames generator
method (DRY violation fix) - both JSON and SSE transformers now use
common frame parsing loop, eliminating ~60% code duplication
- Use COMPRESS_FLAG constants instead of hardcoded 0x01/0x02/0x03 in
parseConnectRPCFrame for better maintainability
MEDIUM PRIORITY FIXES:
- Return empty buffer on gzip decompression failure (prevents silent
data corruption) - ALREADY FIXED
- Add debug warning for unknown message roles in convertMessages
- Create GitHub issue #535 for FIELD namespace refactoring follow-up
- Add test coverage: transformProtobufToSSE, error paths, unknown roles
LOW PRIORITY FIXES:
- Rename checksum test to clarify timestamp granularity (~16 min)
- Fix debug log function name in SSE transformer - FIXED BY REFACTOR
- Add comment to TOOL_RESULT_RESULT field documenting future use
All tests pass (1593 pass, 0 fail)
All validation checks pass (typecheck + lint + format + tests)
- Add comprehensive unit tests for cursor-auth.test.ts
- validateToken: valid/invalid tokens, short tokens, UUID formats, empty strings
- extractUserInfo: JWT parsing, email handling, non-JWT tokens, malformed base64
- saveCredentials/loadCredentials: round-trip, invalid JSON/types, missing fields
- checkAuthStatus: authenticated/not authenticated, expired tokens, JWT exp, invalid dates
- deleteCredentials: delete existing/non-existent files, multiple deletes
- All tests use CCS_HOME env var for isolation, real file I/O, no mocks
- Fix dead try-catch around new Date() in checkAuthStatus()
- Replace try-catch with isNaN check (new Date('garbage') returns Invalid Date, not throw)
- Properly handle Invalid Date by checking isNaN(getTime())
- Fix email populated with sub claim in extractUserInfo()
- Change email: decoded.email || decoded.sub to email: decoded.email || undefined
- Prevent non-email values (UUIDs) from populating email field
- Add type guards for JSON.parse result in extractUserInfo()
- Cast to Record<string, unknown> and validate types
- Use typeof checks for email, userId, exp fields
- Enhanced extractUserInfo to return JWT exp claim for expiry detection
- Updated checkAuthStatus to use JWT exp when available, fallback to importedAt heuristic
- Added typeof validation for loadCredentials fields
- Added ENOENT detection for sqlite3 availability in queryStateDb
- Added deleteCredentials function for cleanup
- Updated PR #526 description to remove refreshToken mention
* fix(cliproxy): migrate deprecated gemini-claude-* model names to upstream claude-* names (#515)
* fix(cliproxy): migrate deprecated gemini-claude-* model names to upstream claude-* names
CLIProxyAPI registry no longer recognizes the gemini-claude-* prefix convention.
Model names in catalog, base config, and user settings are migrated to upstream
claude-* names. Auto-migration in env-builder rewrites existing user settings on
load and persists the change.
Closes#513
* fix: address code review feedback — sync UI layer and add migration tests
- Sync UI isNativeGeminiModel() with backend (remove gemini-claude- exclusion)
- Update UI model catalog agy entries from gemini-claude-* to claude-*
- Update CI/CD workflow and code-reviewer default model names
- Add unit tests for migrateDeprecatedModelNames() logic
* fix(hooks): isolate image type check before error-prone processing (#514)
* fix(hooks): isolate image type check before error-prone processing
Restructure processHook() into two phases so non-image Read calls
never see hook error messages. Phase 1 defensively checks tool name
and file extension, exiting 0 silently on any failure. Phase 2 only
runs for confirmed image/PDF files where errors are relevant.
Closes#511
* fix(hooks): sync image analyzer hook file on every profile launch
Add installImageAnalyzerHook() call to cliproxy executor, matching
the existing installWebSearchHook() pattern. This ensures the .cjs
file in ~/.ccs/hooks/ gets refreshed from the npm package on every
launch, so users receive hook updates after npm update.
* chore(release): 7.41.0-dev.1 [skip ci]
* fix(cliproxy): add fork:true for Claude model aliases in config generator (#523)
Config generator now outputs fork:true for Claude model alias entries,
ensuring both upstream (claude-*) and aliased (gemini-claude-*) model
names appear in /v1/models listings. Also preserves fork flag when
parsing user-added aliases during config regeneration.
Bumps config version to v7 to trigger regeneration on next ccs doctor.
Closes#522
* chore(release): 7.41.0-dev.2 [skip ci]
* feat(cliproxy): add account safety guards to prevent Google account bans (#516)
* feat(cliproxy): add account safety guards to prevent Google account bans
Implements cross-provider isolation to prevent Google from flagging
concurrent OAuth usage across different client IDs (ref: #509, #512).
Three pillars:
1. Auto-pause enforcement at session launch — conflicting accounts in
other Google OAuth providers are paused so CLIProxyAPI can't use them,
restored on session exit with crash recovery via auto-paused.json
2. Ban/disable detection — error responses matching Google ban patterns
auto-pause the affected account to prevent further damage
3. Cross-provider conflict warnings during OAuth registration
Key design decisions:
- PID-based session tracking for crash recovery (dead PID = restore)
- Timestamp comparison prevents restoring ban-paused accounts on exit
- Schema validation on auto-paused.json prevents corrupted state
- Falls back to warn-only when another session is managing isolation
* fix(cliproxy): address code review feedback (attempt 1/5)
- Re-read auto-paused.json before write in enforceProviderIsolation to
reduce concurrent write race window
- Use actual email from registry for display instead of raw accountId
- Export maskEmail for testability
- Add 27 unit tests covering ban detection, email masking,
cross-provider duplicate detection, enforcement lifecycle,
crash recovery, and timestamp-guarded restore
* fix(cliproxy): address remaining review feedback (attempt 2/5)
- Add handleBanDetection test verifying account pause on ban error
- Add warnCrossProviderDuplicates tests (true/false/non-Google)
- Document PID reuse limitation in isPidAlive JSDoc comment
* chore(release): 7.41.0-dev.3 [skip ci]
* feat(cliproxy): runtime quota monitoring during active sessions (#529)
* feat(cliproxy): add runtime quota monitoring during active sessions
Adds adaptive background quota polling to detect and respond to quota
exhaustion during active CLIProxy sessions. Prevents rate-limit-driven
account bans by auto-cooling exhausted accounts and switching defaults.
- Adaptive polling: 300s normal, 60s at 20% threshold, stops at 0%
- Stderr warnings at 20%, boxed exhaustion alerts at 0%
- Cooldown + default switch on exhaustion (existing patterns)
- Configurable via quota_management.runtime_monitor in config.yaml
- Timer.unref() prevents blocking process exit
- monitorStopped guard for in-flight poll safety
Closes#524
* fix: address code review feedback (attempt 1/5)
- M1: Round quotaPercent display with Math.round() to avoid ugly floats
- M2: Rename exhaust_threshold -> exhaustion_threshold for consistency
with existing auto.exhaustion_threshold config field
- M3: Replace async not.toThrow() with direct await assertion pattern
* fix: address code review feedback (attempt 2/5)
- Remove .claude/agent-memory/ from tracking and add to .gitignore
- Unify cooldown_minutes default to 5 (was 10 in runtime_monitor, 5 in auto)
- Add threshold validation in startQuotaMonitor (warn > exhaustion)
- Document intentional post-switch monitoring gap in code comment
* chore(release): 7.41.0-dev.4 [skip ci]
* fix(cliproxy): mask email in ban detection and fix JSDoc default
- Use maskEmail() in handleBanDetection output for consistency
- Fix cooldown_minutes JSDoc: default is 5, not 10
* chore(release): 7.41.0-dev.5 [skip ci]
* fix(cliproxy): address all review feedback (Low + informational)
- Add sync constraint comment on process.exit handler (executor)
- Add TOCTOU race acceptability comment (account-safety)
- Mask email in handleQuotaExhaustion reason string
- Use realistic exhaustion_threshold (5) in test configs
* chore(release): 7.41.0-dev.6 [skip ci]
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Fix bitwise shift overflow in generateChecksum: use Math.trunc
division for >>40/>>32 which wrap modulo 32 in JS
- Add FIXED64/FIXED32 bounds checks in protobuf decoder to prevent
out-of-bounds slice on truncated buffers
- Consolidate COMPRESS_FLAG to single definition in schema (DRY):
executor now imports from schema, added GZIP_ALT/GZIP_BOTH values
- Fix token split edge case: use indexOf+slice instead of split('::')[1]
to handle tokens containing multiple :: delimiters
- Fix AbortSignal listener leak: store handler ref, use once:true,
remove listener on request end/error
* feat(cliproxy): add account safety guards to prevent Google account bans
Implements cross-provider isolation to prevent Google from flagging
concurrent OAuth usage across different client IDs (ref: #509, #512).
Three pillars:
1. Auto-pause enforcement at session launch — conflicting accounts in
other Google OAuth providers are paused so CLIProxyAPI can't use them,
restored on session exit with crash recovery via auto-paused.json
2. Ban/disable detection — error responses matching Google ban patterns
auto-pause the affected account to prevent further damage
3. Cross-provider conflict warnings during OAuth registration
Key design decisions:
- PID-based session tracking for crash recovery (dead PID = restore)
- Timestamp comparison prevents restoring ban-paused accounts on exit
- Schema validation on auto-paused.json prevents corrupted state
- Falls back to warn-only when another session is managing isolation
* fix(cliproxy): address code review feedback (attempt 1/5)
- Re-read auto-paused.json before write in enforceProviderIsolation to
reduce concurrent write race window
- Use actual email from registry for display instead of raw accountId
- Export maskEmail for testability
- Add 27 unit tests covering ban detection, email masking,
cross-provider duplicate detection, enforcement lifecycle,
crash recovery, and timestamp-guarded restore
* fix(cliproxy): address remaining review feedback (attempt 2/5)
- Add handleBanDetection test verifying account pause on ban error
- Add warnCrossProviderDuplicates tests (true/false/non-Google)
- Document PID reuse limitation in isPidAlive JSDoc comment