- Reuse coresession.ExtractSessionInfo across HTTP headers and request payloads to unify canonical session prefix namespaces with the scheduler.
- Extract hierarchical session identities in two phases: initial extraction from request headers on entry, and authoritative deep extraction once request payloads and metadata are available.
- Support Claude Code multi-level subagents (X-Claude-Code-Agent-Id, metadata.agent_id) and Codex thread fork lineages.
- Propagate SessionID and ParentSessionID across ClientRequestMetadata, UsageReporter, and coreusage.Record without root_session_id.
- Include session_id and parent_session_id in queuedUsageDetail for Home LPushUsage forwarding and Redis consumption with self-loop guards.
- Add comprehensive test coverage for canonical headers, body extraction, ghost parent elimination, and self-referential loop guards.
- Use `context.AfterFunc` and buffered delivery in websocket relay sessions to avoid per-request goroutine leaks.
- Synchronize in-flight puts and drain queued connections upon mux listener close.
- Ensure home streaming log writer goroutines terminate cleanly when the log client is unhealthy.
- Prevent integer arithmetic overflow in token breakdown validations and calculations.
- Clone request headers in logging middleware to prevent concurrent mutation issues.
- Implement standard `io.WriterTo` return signature for file body sources.
Closes: #4709
Codex and Claude already emit credential-level quota watermarks on ordinary
responses. CPA used to drop them. Keep the latest watermark in memory and
return it from the management auth-file API.
Hard rule: this is observation only. It must not change scheduling, cooldown
selection, or auth-file persistence.
Snapshot, not accumulation
- QuotaState now has ObservedAt and a bounded Signals map. MarkResult fills
them from the response headers already recorded on the request.
- Signals is the current response, not a union of earlier ones. Retry-After
and "limit reached" only appear on the response that produced them; merging
across responses would keep an expired value forever.
- A response with no quota header (transport failure, 5xx, unrelated endpoint)
leaves the previous snapshot in place.
- ObservedAt is the time of the current snapshot. It advances even when the
values did not change, so a consumer can tell a fresh reading from a stale
one.
- When two model states merge, keep the newer snapshot. Do not union keys
captured at different times.
What is observed, and what is not
- One predicate, ProviderSupportsQuotaObservation, decides the provider set.
- Keep Codex and Claude. Drop Kimi, xAI/Grok, Antigravity, and the Gemini
family (gemini/vertex/aistudio): their ordinary headers are not a reliable
credential-level remaining quota.
- Count-tokens reuses the credential but is not generation traffic.
ExecuteCount sets SkipQuotaObservation so those headers cannot replace the
last generation snapshot. Cooldown and success/failure accounting still run.
Cooldown must not overwrite the last snapshot
- Observation writes only ObservedAt and Signals.
- Cooldown writes only Exceeded, Reason, NextRecoverAt, and BackoffLevel,
through applyCooldownFields. Never assign a fresh QuotaState{...} over a
live value: that would zero the snapshot on 429, Cloudflare, credential-
scope sibling updates, and cooldown clears.
- If a credential-quota cooldown is still active, MarkResult still observes
an already-present model state. It does not create scheduler state just to
record a watermark.
- .cds files persist cooldownFieldsOf(Quota) only. Restore keeps the newer
ObservedAt, so reloading cooldown cannot clobber a newer in-memory snapshot.
- cooldownQuotaEqual still ignores observation fields, so a watermark change
cannot by itself persist cooldown or move the scheduler.
- The management payload omits every cooldown field, so it cannot be mistaken
for scheduler state or wired back into scheduling.
- Manual ResetQuota still clears the full QuotaState.
Codex websocket events
- Codex WS reports quota as codex.rate_limits frames, not HTTP headers.
ParseCodexQuotaEventHeaders turns one event into the same bounded header
shape, and MergeResponseHeaders folds it into the request-scoped holder.
additional_rate_limits is accepted as an object (websocket) or an array
(/wham/usage).
- Parse only through AppendCodexAPIWebsocketResponse. The shared
AppendAPIWebsocketResponse is also used by xAI, and xAI error frames really
do carry x-ratelimit-* headers. Parsing every frame as Codex quota would
forge Codex headers into another provider's request log.
- Also capture code_review_rate_limits.
- A malformed active-limit name drops only that one header, not the window
watermarks parsed from the same event.
- The type discriminator scans a bounded frame prefix, not every byte of
every frame.
- HTTP namespaces an extra limit by short name (x-codex-bengalfox-*); WS
namespaces it by limit name (GPT-5.3-Codex-Spark). The two paths cannot
emit the same header names. The X-Codex-Additional- prefix marks the WS
origin, and snapshot replacement keeps the two spellings from piling up.
Hardening
- Reject observed values with control characters. These strings reach the
plain-text request log, and Limit-Name is upstream-controlled, so CR/LF
could forge a header line.
- When the header cap is hit, keep plan/credits/primary ahead of
additional-limit namespaces, then sort names so truncation is deterministic.
- QuotaState.Clone deep-copies Signals and is used by Auth.Clone and
ModelState.Clone.
- Token stores still serialize credential metadata only, so observation adds
no auth-file writes.
- Added `ClientRequestMetadata` struct to capture client IP, `X-Forwarded-For` headers, and user agent.
- Implemented metadata extraction in HTTP handlers and propagation through context.
- Updated Redis queue plugin to include client request metadata in payloads.
- Added tests to validate metadata extraction and inclusion in request contexts.
- Removed deprecated interceptor and executor-related methods, including `callRequestInterceptor`, `callResponseInterceptor`, and `callStreamChunkInterceptor`.
- Consolidated unused logic and pruned redundant imports to streamline `adapters.go`.
- No functional changes.
- Implemented logging for Codex remote media forwarding start events, including detailed connection and credential metadata.
- Added `formatLogFieldValue` for quoting specific log fields and ensured newline safety in log output.
- Enhanced unit tests to validate log content, escaping, and field inclusion.
- Introduced CPA trace ID middleware to inject trace IDs in response headers.
- Added support for CPA trace ID generation and validation.
- Updated metadata handling to include stable auth indices and trace ID callbacks for enhanced request tracking.
- Enhanced tests to verify CPA trace ID propagation and reserved header handling across middleware, metadata, and handlers.
Closes: #4370
- Introduced `deferredRequestBodyCapture` for spooling and retaining large error-only request bodies for improved logging.
- Enhanced `RequestLoggingMiddleware` to capture request body based on size and error criteria.
- Implemented decompression and truncation for large request bodies with Zstd encoding.
- Added support for deferred API request logging and context tracking.
- Included extensive test coverage for error-specific body capture and serialization behavior.
- Introduced `/openai/v1/videos` endpoint to support OpenAI-specific video generation.
- Added error normalization and handling for OpenAI video resources, including detailed error propagation.
- Enhanced response structure to include OpenAI-specific fields for status, progress, and model mappings.
- Implemented new handlers for video content retrieval and error scenarios.
- Expanded test coverage to validate OpenAI video support, error handling, and backend compatibility.
- Introduced support for file-backed logging of API requests and responses to handle large payloads efficiently.
- Refactored `attachWebsocketLogSources` to `attachRequestLogSources` for broader request and response handling.
- Added new methods for appending request/response data to file-backed sources and updated existing logging workflows for compatibility.
- Improved cleanup and merge logic for file-backed sources during request processing.
- Updated tests to cover newly introduced file-backed logging functionality.
- Added `assertFileBodySourceCleaned` helper to streamline cleanup validations in tests.
- Introduced handling to recreate missing directories during file source operations.
- Enhanced tests to verify behavior after manual directory removal, ensuring robustness.
- Fixed edge cases in log file merging when parts are missing.
- Introduced `FileBodySource` to support large request log sections stored in temp files.
- Added file-backed support for WebSocket timeline and API WebSocket timeline logging.
- Updated `LogRequest` and middleware to integrate optional file-backed sources.
- Implemented clean-up mechanisms to manage temporary log files after processing.
- Included `RequestID` field in `homeRequestLogPayload` for better log categorization.
- Updated `forwardRequestLogToHome` and related components to handle `RequestID`.
- Added new test cases to validate `RequestID` propagation in streaming requests.
- Added APIs to store, retrieve, and clone upstream response headers in context for detailed logging.
- Updated `RecordAPIResponseMetadata`, `RecordAPIWebsocketHandshake`, and related methods to capture response headers.
- Extended `UsageReporter` to include response headers in published usage records.
- Enhanced payload tests to validate response headers' integrity and persistence.
- Refactored `usage.Record` to support optional `ResponseHeaders` field.
- Introduced new xAI `grok-imagine-video` model for video generation with configurable options (e.g., duration, size, resolution).
- Implemented video-specific API endpoints (`/v1/videos`, `/v1/videos/generations`, `/v1/videos/edits`, `/v1/videos/extensions`), including request validation and model handling.
- Enhanced model registry with `xaiBuiltinVideoModelID` and metadata for video capabilities.
- Added unit tests to validate video model support, request structures, and API response handling.
- Extended `XAIExecutor` to integrate video generation and retrieval via runtime requests.
- Introduced `SetHomeEnabled` to enable/disable request-log forwarding to the home control plane.
- Implemented `forwardRequestLogToHome` for non-streaming logs and `homeStreamingLogWriter` for real-time streaming logs.
- Enhanced `FileRequestLogger` to bypass local logging when home forwarding is enabled.
- Updated server configuration to dynamically toggle home request-log forwarding based on changes.
- Added corresponding unit tests to ensure correct forwarding behavior and fallback mechanisms.
- Updated all references from v6 to v7 for `github.com/router-for-me/CLIProxyAPI`.
- Ensured consistency in imports within core libraries, tests, and integration tests.
- Added missing tests for new features in Redis Protocol integration.
- Introduced reusable utilities in `requestmeta` to manage endpoint and response status in request contexts.
- Refactored plugins and handlers to use context-based metadata, removing direct dependency on `gin`.
- Updated tests to validate new context utilities and replaced `gin`-based context handling.
Fixed: #3166
- Introduced new logging functions for websocket requests, handshakes, errors, and responses in `logging_helpers.go`.
- Updated `CodexWebsocketsExecutor` to utilize the new logging functions for improved clarity and consistency in websocket operations.
- Modified the handling of websocket upgrade rejections to log relevant metadata.
- Changed the request body key to a timeline body key in `openai_responses_websocket.go` to better reflect its purpose.
- Enhanced tests to verify the correct logging of websocket events and responses, including disconnect events and error handling scenarios.
- Use util.ResolveAuthDir to properly expand ~ to user home directory
- Fixes issue where logs were created in literal "~/.cli-proxy-api" folder
- Add warning log when auth-dir resolution fails for debugging
Bug introduced in 62e2b67 (refactor(logging): centralize log directory
resolution logic), where strings.TrimSpace was used instead of
util.ResolveAuthDir to process auth-dir path.
- Add ErrorLogsMaxFiles config field with default value 10
- Support hot-reload via config file changes
- Add Management API: GET/PUT/PATCH /v0/management/error-logs-max-files
- Maintain SDK backward compatibility with NewFileRequestLogger (3 params)
- Add NewFileRequestLoggerWithOptions for custom error log retention
When request logging is disabled, forced error logs are retained up to
the configured limit. Set to 0 to disable cleanup.
- Add firstChunkTimestamp field to ResponseWriterWrapper for sync capture
- Capture TTFB in Write() and WriteString() before async channel send
- Add SetFirstChunkTimestamp() to StreamingLogWriter interface
- Make requestTimestamp/apiResponseTimestamp required in LogRequest()
- Remove timestamp capture from WriteAPIResponse() (now via setter)
- Fix Gemini handler to set API_RESPONSE_TIMESTAMP before writing response
This ensures accurate TTFB measurement for all streaming API formats
(OpenAI, Gemini, Claude) by capturing timestamp synchronously when
the first response chunk arrives, not when the stream finalizes.
Previously:
- REQUEST INFO timestamp was captured at log write time (not request arrival)
- API RESPONSE had NO timestamp at all
This fix:
- Captures REQUEST INFO timestamp when request first arrives
- Adds API RESPONSE timestamp when upstream response arrives
Changes:
- Add Timestamp field to RequestInfo, set at middleware initialization
- Set API_RESPONSE_TIMESTAMP in appendAPIResponse() and gemini handler
- Pass timestamps through logging chain to writeNonStreamingLog()
- Add timestamp output to API RESPONSE section
This enables accurate measurement of backend response latency in error logs.
- Introduced `ResolveLogDirectory` function in `logging` package to standardize log directory determination across components.
- Replaced redundant logic in `server`, `global_logger`, and `handlers` with the new utility function.