- P1: Thinking "off" now fully disables all tier thinking
When thinkingValue === 'off' AND no per-tier config, return early
Preserves behavior for "off" with per-tier config (skips main, processes tiers)
Fixes regression where tier defaults were applied despite explicit off setting
- P2: Handle composite-create thrown errors in POST route
Wrap createCompositeVariant() in try/catch
Returns 400 instead of generic 500 when function throws (e.g., legacy mode)
- P2: Edit dialog no longer seeds empty model string
Changed `variant.model || ''` to `variant.model ?? undefined`
Added payload filtering to exclude undefined/empty values before API call
Prevents unintended model overwrites when user only changes other fields
- P3: Composite --config guidance now uses variant name
Added profileName to ExecutorConfig interface
Error message now shows correct variant name instead of default tier provider
e.g., "ccs cliproxy edit my-mix" instead of "ccs cliproxy edit gemini"
- P1: Remove premature variant deletion in --force mode
Fix destructive overwrite path - old variant preserved if user
cancels auth or creation fails (createVariant handles overwrite)
- P2: Validate all composite tier providers at runtime
Backend gating now checks all providers in composite, not just
default tier. Prevents kiro/ghcp on non-default tiers bypassing
Plus backend requirement.
- P2: Preserve tier thinking when default tier is off
Early return skipped tier processing when default thinking was
"off". Now uses flag-based approach - only main model skipped,
tier loop continues with per-tier thinking config.
- Add requireAllTiers parameter to validateCompositeTiers (default false)
- POST/create passes true to enforce all 3 tiers required
- PUT/update passes false (default) to allow partial updates
- Guard against null tier values that would throw at runtime
P1 (Critical): Remote composite execution now correctly uses remote endpoint
- Reorder condition to check useRemoteProxy before isComposite
- Composite variants now work with remote CLIProxy instances
P2a (Medium): Tier deep-merge preserves optional fields during updates
- Use spread merge instead of ?? to preserve fallback/thinking/account
P2b (Medium): API accepts partial tier updates in PUT endpoint
- Skip validation for tiers not present in request
- Aligns with updateCompositeVariant's merge behavior
- Validate fallback doesn't point to same provider+model (circular)
- Reject empty/whitespace model strings in composite tiers
- Add TODO comment for concurrent modification race condition
- Block variant deletion when CLIProxy has active sessions
- Use temp file + rename pattern for atomic settings writes
- Prevents partial writes on crash/interruption
- Continue to remaining providers on auth failure instead of throwing
- Aggregate failures and show summary of succeeded/failed providers
- Improves UX when one provider fails in composite variant
- Add optional chaining for tier access in env-resolver
- Throw error if default tier model missing in env-builder
- Add null checks for composite.tiers in profile-detector
- 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