Kaynağa Gözat

release v0.5.7

release v0.5.7
Ding 18 saat önce
ebeveyn
işleme
c44ffe1ef1
49 değiştirilmiş dosya ile 4569 ekleme ve 289 silme
  1. 1 0
      drizzle/0067_gorgeous_mulholland_black.sql
  2. 2990 0
      drizzle/meta/0067_snapshot.json
  3. 7 0
      drizzle/meta/_journal.json
  4. 2 0
      messages/en/settings/config.json
  5. 2 0
      messages/ja/settings/config.json
  6. 2 0
      messages/ru/settings/config.json
  7. 2 0
      messages/zh-CN/settings/config.json
  8. 2 0
      messages/zh-TW/settings/config.json
  9. 4 3
      scripts/clear-session-bindings.ts
  10. 2 3
      src/actions/my-usage.ts
  11. 2 0
      src/actions/system-config.ts
  12. 23 4
      src/actions/users.ts
  13. 49 57
      src/app/[locale]/dashboard/quotas/users/page.tsx
  14. 29 0
      src/app/[locale]/settings/config/_components/system-settings-form.tsx
  15. 1 0
      src/app/[locale]/settings/config/page.tsx
  16. 78 0
      src/app/v1/_lib/proxy/billing-header-rectifier.ts
  17. 27 1
      src/app/v1/_lib/proxy/forwarder.ts
  18. 51 57
      src/app/v1/_lib/proxy/rate-limit-guard.ts
  19. 103 41
      src/app/v1/_lib/proxy/response-handler.ts
  20. 9 1
      src/app/v1/_lib/proxy/session-guard.ts
  21. 6 0
      src/drizzle/schema.ts
  22. 3 0
      src/lib/config/system-settings-cache.ts
  23. 53 15
      src/lib/proxy-agent/agent-pool.ts
  24. 21 3
      src/lib/rate-limit/concurrent-session-limit.ts
  25. 100 0
      src/lib/rate-limit/service.ts
  26. 29 0
      src/lib/redis/active-session-keys.ts
  27. 101 0
      src/lib/redis/lua-scripts.ts
  28. 28 3
      src/lib/session-manager.ts
  29. 30 19
      src/lib/session-tracker.ts
  30. 2 0
      src/lib/utils/special-settings.ts
  31. 2 0
      src/lib/validation/schemas.ts
  32. 1 0
      src/repository/_shared/transformers.ts
  33. 94 33
      src/repository/statistics.ts
  34. 7 0
      src/repository/system-config.ts
  35. 9 0
      src/types/special-settings.ts
  36. 8 0
      src/types/system-config.ts
  37. 6 0
      tests/unit/actions/system-config-save.test.ts
  38. 17 30
      tests/unit/actions/total-usage-semantics.test.ts
  39. 3 0
      tests/unit/lib/config/system-settings-cache.test.ts
  40. 8 0
      tests/unit/lib/endpoint-circuit-breaker.test.ts
  41. 8 0
      tests/unit/lib/proxy-agent/agent-pool.test.ts
  42. 42 1
      tests/unit/lib/rate-limit/concurrent-session-limit.test.ts
  43. 81 0
      tests/unit/lib/rate-limit/service-extra.test.ts
  44. 85 0
      tests/unit/lib/session-manager-terminate-session.test.ts
  45. 8 3
      tests/unit/lib/session-tracker-cleanup.test.ts
  46. 6 5
      tests/unit/lib/session-ttl-validation.test.ts
  47. 213 0
      tests/unit/proxy/billing-header-rectifier.test.ts
  48. 69 10
      tests/unit/proxy/rate-limit-guard.test.ts
  49. 143 0
      tests/unit/repository/statistics-total-cost.test.ts

+ 1 - 0
drizzle/0067_gorgeous_mulholland_black.sql

@@ -0,0 +1 @@
+ALTER TABLE "system_settings" ADD COLUMN "enable_billing_header_rectifier" boolean DEFAULT true NOT NULL;

+ 2990 - 0
drizzle/meta/0067_snapshot.json

@@ -0,0 +1,2990 @@
+{
+  "id": "c4820501-71eb-4492-b9cb-dd19c5def277",
+  "prevId": "6acf6a64-a2b1-41a1-aeda-ee47a6304e29",
+  "version": "7",
+  "dialect": "postgresql",
+  "tables": {
+    "public.error_rules": {
+      "name": "error_rules",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "pattern": {
+          "name": "pattern",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "match_type": {
+          "name": "match_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'regex'"
+        },
+        "category": {
+          "name": "category",
+          "type": "varchar(50)",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "description": {
+          "name": "description",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "override_response": {
+          "name": "override_response",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "override_status_code": {
+          "name": "override_status_code",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "is_default": {
+          "name": "is_default",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "priority": {
+          "name": "priority",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 0
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {
+        "idx_error_rules_enabled": {
+          "name": "idx_error_rules_enabled",
+          "columns": [
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "priority",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "unique_pattern": {
+          "name": "unique_pattern",
+          "columns": [
+            {
+              "expression": "pattern",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": true,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_category": {
+          "name": "idx_category",
+          "columns": [
+            {
+              "expression": "category",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_match_type": {
+          "name": "idx_match_type",
+          "columns": [
+            {
+              "expression": "match_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.keys": {
+      "name": "keys",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "user_id": {
+          "name": "user_id",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "key": {
+          "name": "key",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "name": {
+          "name": "name",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": true
+        },
+        "expires_at": {
+          "name": "expires_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "can_login_web_ui": {
+          "name": "can_login_web_ui",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": false
+        },
+        "limit_5h_usd": {
+          "name": "limit_5h_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_daily_usd": {
+          "name": "limit_daily_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "daily_reset_mode": {
+          "name": "daily_reset_mode",
+          "type": "daily_reset_mode",
+          "typeSchema": "public",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'fixed'"
+        },
+        "daily_reset_time": {
+          "name": "daily_reset_time",
+          "type": "varchar(5)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'00:00'"
+        },
+        "limit_weekly_usd": {
+          "name": "limit_weekly_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_monthly_usd": {
+          "name": "limit_monthly_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_total_usd": {
+          "name": "limit_total_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_concurrent_sessions": {
+          "name": "limit_concurrent_sessions",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 0
+        },
+        "provider_group": {
+          "name": "provider_group",
+          "type": "varchar(200)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'default'"
+        },
+        "cache_ttl_preference": {
+          "name": "cache_ttl_preference",
+          "type": "varchar(10)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "deleted_at": {
+          "name": "deleted_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        }
+      },
+      "indexes": {
+        "idx_keys_user_id": {
+          "name": "idx_keys_user_id",
+          "columns": [
+            {
+              "expression": "user_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_keys_created_at": {
+          "name": "idx_keys_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_keys_deleted_at": {
+          "name": "idx_keys_deleted_at",
+          "columns": [
+            {
+              "expression": "deleted_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.message_request": {
+      "name": "message_request",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "provider_id": {
+          "name": "provider_id",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "user_id": {
+          "name": "user_id",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "key": {
+          "name": "key",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "model": {
+          "name": "model",
+          "type": "varchar(128)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "duration_ms": {
+          "name": "duration_ms",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cost_usd": {
+          "name": "cost_usd",
+          "type": "numeric(21, 15)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'0'"
+        },
+        "cost_multiplier": {
+          "name": "cost_multiplier",
+          "type": "numeric(10, 4)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "session_id": {
+          "name": "session_id",
+          "type": "varchar(64)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "request_sequence": {
+          "name": "request_sequence",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 1
+        },
+        "provider_chain": {
+          "name": "provider_chain",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "status_code": {
+          "name": "status_code",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "api_type": {
+          "name": "api_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "endpoint": {
+          "name": "endpoint",
+          "type": "varchar(256)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "original_model": {
+          "name": "original_model",
+          "type": "varchar(128)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "input_tokens": {
+          "name": "input_tokens",
+          "type": "bigint",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "output_tokens": {
+          "name": "output_tokens",
+          "type": "bigint",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "ttfb_ms": {
+          "name": "ttfb_ms",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_creation_input_tokens": {
+          "name": "cache_creation_input_tokens",
+          "type": "bigint",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_read_input_tokens": {
+          "name": "cache_read_input_tokens",
+          "type": "bigint",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_creation_5m_input_tokens": {
+          "name": "cache_creation_5m_input_tokens",
+          "type": "bigint",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_creation_1h_input_tokens": {
+          "name": "cache_creation_1h_input_tokens",
+          "type": "bigint",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_ttl_applied": {
+          "name": "cache_ttl_applied",
+          "type": "varchar(10)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "context_1m_applied": {
+          "name": "context_1m_applied",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": false
+        },
+        "special_settings": {
+          "name": "special_settings",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "error_message": {
+          "name": "error_message",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "error_stack": {
+          "name": "error_stack",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "error_cause": {
+          "name": "error_cause",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "blocked_by": {
+          "name": "blocked_by",
+          "type": "varchar(50)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "blocked_reason": {
+          "name": "blocked_reason",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "user_agent": {
+          "name": "user_agent",
+          "type": "varchar(512)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "messages_count": {
+          "name": "messages_count",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "deleted_at": {
+          "name": "deleted_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        }
+      },
+      "indexes": {
+        "idx_message_request_user_date_cost": {
+          "name": "idx_message_request_user_date_cost",
+          "columns": [
+            {
+              "expression": "user_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "cost_usd",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"message_request\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_user_query": {
+          "name": "idx_message_request_user_query",
+          "columns": [
+            {
+              "expression": "user_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"message_request\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_session_id": {
+          "name": "idx_message_request_session_id",
+          "columns": [
+            {
+              "expression": "session_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"message_request\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_session_id_prefix": {
+          "name": "idx_message_request_session_id_prefix",
+          "columns": [
+            {
+              "expression": "\"session_id\" varchar_pattern_ops",
+              "asc": true,
+              "isExpression": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"message_request\".\"deleted_at\" IS NULL AND (\"message_request\".\"blocked_by\" IS NULL OR \"message_request\".\"blocked_by\" <> 'warmup')",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_session_seq": {
+          "name": "idx_message_request_session_seq",
+          "columns": [
+            {
+              "expression": "session_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "request_sequence",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"message_request\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_endpoint": {
+          "name": "idx_message_request_endpoint",
+          "columns": [
+            {
+              "expression": "endpoint",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"message_request\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_blocked_by": {
+          "name": "idx_message_request_blocked_by",
+          "columns": [
+            {
+              "expression": "blocked_by",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"message_request\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_provider_id": {
+          "name": "idx_message_request_provider_id",
+          "columns": [
+            {
+              "expression": "provider_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_user_id": {
+          "name": "idx_message_request_user_id",
+          "columns": [
+            {
+              "expression": "user_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_key": {
+          "name": "idx_message_request_key",
+          "columns": [
+            {
+              "expression": "key",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_created_at": {
+          "name": "idx_message_request_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_message_request_deleted_at": {
+          "name": "idx_message_request_deleted_at",
+          "columns": [
+            {
+              "expression": "deleted_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.model_prices": {
+      "name": "model_prices",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "model_name": {
+          "name": "model_name",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "price_data": {
+          "name": "price_data",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "source": {
+          "name": "source",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'litellm'"
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {
+        "idx_model_prices_latest": {
+          "name": "idx_model_prices_latest",
+          "columns": [
+            {
+              "expression": "model_name",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": false,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_model_prices_model_name": {
+          "name": "idx_model_prices_model_name",
+          "columns": [
+            {
+              "expression": "model_name",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_model_prices_created_at": {
+          "name": "idx_model_prices_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": false,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_model_prices_source": {
+          "name": "idx_model_prices_source",
+          "columns": [
+            {
+              "expression": "source",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.notification_settings": {
+      "name": "notification_settings",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "enabled": {
+          "name": "enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "use_legacy_mode": {
+          "name": "use_legacy_mode",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "circuit_breaker_enabled": {
+          "name": "circuit_breaker_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "circuit_breaker_webhook": {
+          "name": "circuit_breaker_webhook",
+          "type": "varchar(512)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "daily_leaderboard_enabled": {
+          "name": "daily_leaderboard_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "daily_leaderboard_webhook": {
+          "name": "daily_leaderboard_webhook",
+          "type": "varchar(512)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "daily_leaderboard_time": {
+          "name": "daily_leaderboard_time",
+          "type": "varchar(10)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'09:00'"
+        },
+        "daily_leaderboard_top_n": {
+          "name": "daily_leaderboard_top_n",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 5
+        },
+        "cost_alert_enabled": {
+          "name": "cost_alert_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "cost_alert_webhook": {
+          "name": "cost_alert_webhook",
+          "type": "varchar(512)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cost_alert_threshold": {
+          "name": "cost_alert_threshold",
+          "type": "numeric(5, 2)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'0.80'"
+        },
+        "cost_alert_check_interval": {
+          "name": "cost_alert_check_interval",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 60
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {},
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.notification_target_bindings": {
+      "name": "notification_target_bindings",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "notification_type": {
+          "name": "notification_type",
+          "type": "notification_type",
+          "typeSchema": "public",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "target_id": {
+          "name": "target_id",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "schedule_cron": {
+          "name": "schedule_cron",
+          "type": "varchar(100)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "schedule_timezone": {
+          "name": "schedule_timezone",
+          "type": "varchar(50)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "template_override": {
+          "name": "template_override",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {
+        "unique_notification_target_binding": {
+          "name": "unique_notification_target_binding",
+          "columns": [
+            {
+              "expression": "notification_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "target_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": true,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_notification_bindings_type": {
+          "name": "idx_notification_bindings_type",
+          "columns": [
+            {
+              "expression": "notification_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_notification_bindings_target": {
+          "name": "idx_notification_bindings_target",
+          "columns": [
+            {
+              "expression": "target_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {
+        "notification_target_bindings_target_id_webhook_targets_id_fk": {
+          "name": "notification_target_bindings_target_id_webhook_targets_id_fk",
+          "tableFrom": "notification_target_bindings",
+          "tableTo": "webhook_targets",
+          "columnsFrom": [
+            "target_id"
+          ],
+          "columnsTo": [
+            "id"
+          ],
+          "onDelete": "cascade",
+          "onUpdate": "no action"
+        }
+      },
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.provider_endpoint_probe_logs": {
+      "name": "provider_endpoint_probe_logs",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "endpoint_id": {
+          "name": "endpoint_id",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "source": {
+          "name": "source",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'scheduled'"
+        },
+        "ok": {
+          "name": "ok",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "status_code": {
+          "name": "status_code",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "latency_ms": {
+          "name": "latency_ms",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "error_type": {
+          "name": "error_type",
+          "type": "varchar(64)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "error_message": {
+          "name": "error_message",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {
+        "idx_provider_endpoint_probe_logs_endpoint_created_at": {
+          "name": "idx_provider_endpoint_probe_logs_endpoint_created_at",
+          "columns": [
+            {
+              "expression": "endpoint_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": false,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_provider_endpoint_probe_logs_created_at": {
+          "name": "idx_provider_endpoint_probe_logs_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {
+        "provider_endpoint_probe_logs_endpoint_id_provider_endpoints_id_fk": {
+          "name": "provider_endpoint_probe_logs_endpoint_id_provider_endpoints_id_fk",
+          "tableFrom": "provider_endpoint_probe_logs",
+          "tableTo": "provider_endpoints",
+          "columnsFrom": [
+            "endpoint_id"
+          ],
+          "columnsTo": [
+            "id"
+          ],
+          "onDelete": "cascade",
+          "onUpdate": "no action"
+        }
+      },
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.provider_endpoints": {
+      "name": "provider_endpoints",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "vendor_id": {
+          "name": "vendor_id",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "provider_type": {
+          "name": "provider_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'claude'"
+        },
+        "url": {
+          "name": "url",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "label": {
+          "name": "label",
+          "type": "varchar(200)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "sort_order": {
+          "name": "sort_order",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 0
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "last_probed_at": {
+          "name": "last_probed_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "last_probe_ok": {
+          "name": "last_probe_ok",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "last_probe_status_code": {
+          "name": "last_probe_status_code",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "last_probe_latency_ms": {
+          "name": "last_probe_latency_ms",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "last_probe_error_type": {
+          "name": "last_probe_error_type",
+          "type": "varchar(64)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "last_probe_error_message": {
+          "name": "last_probe_error_message",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "deleted_at": {
+          "name": "deleted_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        }
+      },
+      "indexes": {
+        "uniq_provider_endpoints_vendor_type_url": {
+          "name": "uniq_provider_endpoints_vendor_type_url",
+          "columns": [
+            {
+              "expression": "vendor_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "provider_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "url",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": true,
+          "where": "\"provider_endpoints\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_provider_endpoints_vendor_type": {
+          "name": "idx_provider_endpoints_vendor_type",
+          "columns": [
+            {
+              "expression": "vendor_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "provider_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"provider_endpoints\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_provider_endpoints_enabled": {
+          "name": "idx_provider_endpoints_enabled",
+          "columns": [
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "vendor_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "provider_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"provider_endpoints\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_provider_endpoints_created_at": {
+          "name": "idx_provider_endpoints_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_provider_endpoints_deleted_at": {
+          "name": "idx_provider_endpoints_deleted_at",
+          "columns": [
+            {
+              "expression": "deleted_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {
+        "provider_endpoints_vendor_id_provider_vendors_id_fk": {
+          "name": "provider_endpoints_vendor_id_provider_vendors_id_fk",
+          "tableFrom": "provider_endpoints",
+          "tableTo": "provider_vendors",
+          "columnsFrom": [
+            "vendor_id"
+          ],
+          "columnsTo": [
+            "id"
+          ],
+          "onDelete": "cascade",
+          "onUpdate": "no action"
+        }
+      },
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.provider_vendors": {
+      "name": "provider_vendors",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "website_domain": {
+          "name": "website_domain",
+          "type": "varchar(255)",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "display_name": {
+          "name": "display_name",
+          "type": "varchar(200)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "website_url": {
+          "name": "website_url",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "favicon_url": {
+          "name": "favicon_url",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {
+        "uniq_provider_vendors_website_domain": {
+          "name": "uniq_provider_vendors_website_domain",
+          "columns": [
+            {
+              "expression": "website_domain",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": true,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_provider_vendors_created_at": {
+          "name": "idx_provider_vendors_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.providers": {
+      "name": "providers",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "name": {
+          "name": "name",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "description": {
+          "name": "description",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "url": {
+          "name": "url",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "key": {
+          "name": "key",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "provider_vendor_id": {
+          "name": "provider_vendor_id",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "weight": {
+          "name": "weight",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 1
+        },
+        "priority": {
+          "name": "priority",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 0
+        },
+        "group_priorities": {
+          "name": "group_priorities",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'null'::jsonb"
+        },
+        "cost_multiplier": {
+          "name": "cost_multiplier",
+          "type": "numeric(10, 4)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'1.0'"
+        },
+        "group_tag": {
+          "name": "group_tag",
+          "type": "varchar(50)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "provider_type": {
+          "name": "provider_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'claude'"
+        },
+        "preserve_client_ip": {
+          "name": "preserve_client_ip",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "model_redirects": {
+          "name": "model_redirects",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "allowed_models": {
+          "name": "allowed_models",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'null'::jsonb"
+        },
+        "join_claude_pool": {
+          "name": "join_claude_pool",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": false
+        },
+        "codex_instructions_strategy": {
+          "name": "codex_instructions_strategy",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'auto'"
+        },
+        "mcp_passthrough_type": {
+          "name": "mcp_passthrough_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'none'"
+        },
+        "mcp_passthrough_url": {
+          "name": "mcp_passthrough_url",
+          "type": "varchar(512)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_5h_usd": {
+          "name": "limit_5h_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_daily_usd": {
+          "name": "limit_daily_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "daily_reset_mode": {
+          "name": "daily_reset_mode",
+          "type": "daily_reset_mode",
+          "typeSchema": "public",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'fixed'"
+        },
+        "daily_reset_time": {
+          "name": "daily_reset_time",
+          "type": "varchar(5)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'00:00'"
+        },
+        "limit_weekly_usd": {
+          "name": "limit_weekly_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_monthly_usd": {
+          "name": "limit_monthly_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_total_usd": {
+          "name": "limit_total_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "total_cost_reset_at": {
+          "name": "total_cost_reset_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_concurrent_sessions": {
+          "name": "limit_concurrent_sessions",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 0
+        },
+        "max_retry_attempts": {
+          "name": "max_retry_attempts",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "circuit_breaker_failure_threshold": {
+          "name": "circuit_breaker_failure_threshold",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 5
+        },
+        "circuit_breaker_open_duration": {
+          "name": "circuit_breaker_open_duration",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 1800000
+        },
+        "circuit_breaker_half_open_success_threshold": {
+          "name": "circuit_breaker_half_open_success_threshold",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 2
+        },
+        "proxy_url": {
+          "name": "proxy_url",
+          "type": "varchar(512)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "proxy_fallback_to_direct": {
+          "name": "proxy_fallback_to_direct",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": false
+        },
+        "first_byte_timeout_streaming_ms": {
+          "name": "first_byte_timeout_streaming_ms",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 0
+        },
+        "streaming_idle_timeout_ms": {
+          "name": "streaming_idle_timeout_ms",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 0
+        },
+        "request_timeout_non_streaming_ms": {
+          "name": "request_timeout_non_streaming_ms",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 0
+        },
+        "website_url": {
+          "name": "website_url",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "favicon_url": {
+          "name": "favicon_url",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "cache_ttl_preference": {
+          "name": "cache_ttl_preference",
+          "type": "varchar(10)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "context_1m_preference": {
+          "name": "context_1m_preference",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "codex_reasoning_effort_preference": {
+          "name": "codex_reasoning_effort_preference",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "codex_reasoning_summary_preference": {
+          "name": "codex_reasoning_summary_preference",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "codex_text_verbosity_preference": {
+          "name": "codex_text_verbosity_preference",
+          "type": "varchar(10)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "codex_parallel_tool_calls_preference": {
+          "name": "codex_parallel_tool_calls_preference",
+          "type": "varchar(10)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "anthropic_max_tokens_preference": {
+          "name": "anthropic_max_tokens_preference",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "anthropic_thinking_budget_preference": {
+          "name": "anthropic_thinking_budget_preference",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "anthropic_adaptive_thinking": {
+          "name": "anthropic_adaptive_thinking",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'null'::jsonb"
+        },
+        "gemini_google_search_preference": {
+          "name": "gemini_google_search_preference",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "tpm": {
+          "name": "tpm",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 0
+        },
+        "rpm": {
+          "name": "rpm",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 0
+        },
+        "rpd": {
+          "name": "rpd",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 0
+        },
+        "cc": {
+          "name": "cc",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 0
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "deleted_at": {
+          "name": "deleted_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        }
+      },
+      "indexes": {
+        "idx_providers_enabled_priority": {
+          "name": "idx_providers_enabled_priority",
+          "columns": [
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "priority",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "weight",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"providers\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_providers_group": {
+          "name": "idx_providers_group",
+          "columns": [
+            {
+              "expression": "group_tag",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"providers\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_providers_created_at": {
+          "name": "idx_providers_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_providers_deleted_at": {
+          "name": "idx_providers_deleted_at",
+          "columns": [
+            {
+              "expression": "deleted_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_providers_vendor_type": {
+          "name": "idx_providers_vendor_type",
+          "columns": [
+            {
+              "expression": "provider_vendor_id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "provider_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"providers\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {
+        "providers_provider_vendor_id_provider_vendors_id_fk": {
+          "name": "providers_provider_vendor_id_provider_vendors_id_fk",
+          "tableFrom": "providers",
+          "tableTo": "provider_vendors",
+          "columnsFrom": [
+            "provider_vendor_id"
+          ],
+          "columnsTo": [
+            "id"
+          ],
+          "onDelete": "restrict",
+          "onUpdate": "no action"
+        }
+      },
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.request_filters": {
+      "name": "request_filters",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "name": {
+          "name": "name",
+          "type": "varchar(100)",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "description": {
+          "name": "description",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "scope": {
+          "name": "scope",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "action": {
+          "name": "action",
+          "type": "varchar(30)",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "match_type": {
+          "name": "match_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "target": {
+          "name": "target",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "replacement": {
+          "name": "replacement",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "priority": {
+          "name": "priority",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": true,
+          "default": 0
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "binding_type": {
+          "name": "binding_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'global'"
+        },
+        "provider_ids": {
+          "name": "provider_ids",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "group_tags": {
+          "name": "group_tags",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {
+        "idx_request_filters_enabled": {
+          "name": "idx_request_filters_enabled",
+          "columns": [
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "priority",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_request_filters_scope": {
+          "name": "idx_request_filters_scope",
+          "columns": [
+            {
+              "expression": "scope",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_request_filters_action": {
+          "name": "idx_request_filters_action",
+          "columns": [
+            {
+              "expression": "action",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_request_filters_binding": {
+          "name": "idx_request_filters_binding",
+          "columns": [
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "binding_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.sensitive_words": {
+      "name": "sensitive_words",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "word": {
+          "name": "word",
+          "type": "varchar(255)",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "match_type": {
+          "name": "match_type",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'contains'"
+        },
+        "description": {
+          "name": "description",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {
+        "idx_sensitive_words_enabled": {
+          "name": "idx_sensitive_words_enabled",
+          "columns": [
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "match_type",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_sensitive_words_created_at": {
+          "name": "idx_sensitive_words_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.system_settings": {
+      "name": "system_settings",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "site_title": {
+          "name": "site_title",
+          "type": "varchar(128)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'Claude Code Hub'"
+        },
+        "allow_global_usage_view": {
+          "name": "allow_global_usage_view",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "currency_display": {
+          "name": "currency_display",
+          "type": "varchar(10)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'USD'"
+        },
+        "billing_model_source": {
+          "name": "billing_model_source",
+          "type": "varchar(20)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'original'"
+        },
+        "timezone": {
+          "name": "timezone",
+          "type": "varchar(64)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "enable_auto_cleanup": {
+          "name": "enable_auto_cleanup",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": false
+        },
+        "cleanup_retention_days": {
+          "name": "cleanup_retention_days",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 30
+        },
+        "cleanup_schedule": {
+          "name": "cleanup_schedule",
+          "type": "varchar(50)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'0 2 * * *'"
+        },
+        "cleanup_batch_size": {
+          "name": "cleanup_batch_size",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 10000
+        },
+        "enable_client_version_check": {
+          "name": "enable_client_version_check",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "verbose_provider_error": {
+          "name": "verbose_provider_error",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "enable_http2": {
+          "name": "enable_http2",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "intercept_anthropic_warmup_requests": {
+          "name": "intercept_anthropic_warmup_requests",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": false
+        },
+        "enable_thinking_signature_rectifier": {
+          "name": "enable_thinking_signature_rectifier",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "enable_thinking_budget_rectifier": {
+          "name": "enable_thinking_budget_rectifier",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "enable_billing_header_rectifier": {
+          "name": "enable_billing_header_rectifier",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "enable_codex_session_id_completion": {
+          "name": "enable_codex_session_id_completion",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "enable_claude_metadata_user_id_injection": {
+          "name": "enable_claude_metadata_user_id_injection",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "enable_response_fixer": {
+          "name": "enable_response_fixer",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "response_fixer_config": {
+          "name": "response_fixer_config",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'{\"fixTruncatedJson\":true,\"fixSseFormat\":true,\"fixEncoding\":true,\"maxJsonDepth\":200,\"maxFixSize\":1048576}'::jsonb"
+        },
+        "quota_db_refresh_interval_seconds": {
+          "name": "quota_db_refresh_interval_seconds",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false,
+          "default": 10
+        },
+        "quota_lease_percent_5h": {
+          "name": "quota_lease_percent_5h",
+          "type": "numeric(5, 4)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'0.05'"
+        },
+        "quota_lease_percent_daily": {
+          "name": "quota_lease_percent_daily",
+          "type": "numeric(5, 4)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'0.05'"
+        },
+        "quota_lease_percent_weekly": {
+          "name": "quota_lease_percent_weekly",
+          "type": "numeric(5, 4)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'0.05'"
+        },
+        "quota_lease_percent_monthly": {
+          "name": "quota_lease_percent_monthly",
+          "type": "numeric(5, 4)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'0.05'"
+        },
+        "quota_lease_cap_usd": {
+          "name": "quota_lease_cap_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {},
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.users": {
+      "name": "users",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "name": {
+          "name": "name",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "description": {
+          "name": "description",
+          "type": "text",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "role": {
+          "name": "role",
+          "type": "varchar",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'user'"
+        },
+        "rpm_limit": {
+          "name": "rpm_limit",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "daily_limit_usd": {
+          "name": "daily_limit_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "provider_group": {
+          "name": "provider_group",
+          "type": "varchar(200)",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'default'"
+        },
+        "tags": {
+          "name": "tags",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'[]'::jsonb"
+        },
+        "limit_5h_usd": {
+          "name": "limit_5h_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_weekly_usd": {
+          "name": "limit_weekly_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_monthly_usd": {
+          "name": "limit_monthly_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_total_usd": {
+          "name": "limit_total_usd",
+          "type": "numeric(10, 2)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "limit_concurrent_sessions": {
+          "name": "limit_concurrent_sessions",
+          "type": "integer",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "daily_reset_mode": {
+          "name": "daily_reset_mode",
+          "type": "daily_reset_mode",
+          "typeSchema": "public",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'fixed'"
+        },
+        "daily_reset_time": {
+          "name": "daily_reset_time",
+          "type": "varchar(5)",
+          "primaryKey": false,
+          "notNull": true,
+          "default": "'00:00'"
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "expires_at": {
+          "name": "expires_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "allowed_clients": {
+          "name": "allowed_clients",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'[]'::jsonb"
+        },
+        "allowed_models": {
+          "name": "allowed_models",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "'[]'::jsonb"
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "deleted_at": {
+          "name": "deleted_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        }
+      },
+      "indexes": {
+        "idx_users_active_role_sort": {
+          "name": "idx_users_active_role_sort",
+          "columns": [
+            {
+              "expression": "deleted_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "role",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "id",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"users\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_users_enabled_expires_at": {
+          "name": "idx_users_enabled_expires_at",
+          "columns": [
+            {
+              "expression": "is_enabled",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            },
+            {
+              "expression": "expires_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "where": "\"users\".\"deleted_at\" IS NULL",
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_users_created_at": {
+          "name": "idx_users_created_at",
+          "columns": [
+            {
+              "expression": "created_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        },
+        "idx_users_deleted_at": {
+          "name": "idx_users_deleted_at",
+          "columns": [
+            {
+              "expression": "deleted_at",
+              "isExpression": false,
+              "asc": true,
+              "nulls": "last"
+            }
+          ],
+          "isUnique": false,
+          "concurrently": false,
+          "method": "btree",
+          "with": {}
+        }
+      },
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    },
+    "public.webhook_targets": {
+      "name": "webhook_targets",
+      "schema": "",
+      "columns": {
+        "id": {
+          "name": "id",
+          "type": "serial",
+          "primaryKey": true,
+          "notNull": true
+        },
+        "name": {
+          "name": "name",
+          "type": "varchar(100)",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "provider_type": {
+          "name": "provider_type",
+          "type": "webhook_provider_type",
+          "typeSchema": "public",
+          "primaryKey": false,
+          "notNull": true
+        },
+        "webhook_url": {
+          "name": "webhook_url",
+          "type": "varchar(1024)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "telegram_bot_token": {
+          "name": "telegram_bot_token",
+          "type": "varchar(256)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "telegram_chat_id": {
+          "name": "telegram_chat_id",
+          "type": "varchar(64)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "dingtalk_secret": {
+          "name": "dingtalk_secret",
+          "type": "varchar(256)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "custom_template": {
+          "name": "custom_template",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "custom_headers": {
+          "name": "custom_headers",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "proxy_url": {
+          "name": "proxy_url",
+          "type": "varchar(512)",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "proxy_fallback_to_direct": {
+          "name": "proxy_fallback_to_direct",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": false,
+          "default": false
+        },
+        "is_enabled": {
+          "name": "is_enabled",
+          "type": "boolean",
+          "primaryKey": false,
+          "notNull": true,
+          "default": true
+        },
+        "last_test_at": {
+          "name": "last_test_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "last_test_result": {
+          "name": "last_test_result",
+          "type": "jsonb",
+          "primaryKey": false,
+          "notNull": false
+        },
+        "created_at": {
+          "name": "created_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        },
+        "updated_at": {
+          "name": "updated_at",
+          "type": "timestamp with time zone",
+          "primaryKey": false,
+          "notNull": false,
+          "default": "now()"
+        }
+      },
+      "indexes": {},
+      "foreignKeys": {},
+      "compositePrimaryKeys": {},
+      "uniqueConstraints": {},
+      "policies": {},
+      "checkConstraints": {},
+      "isRLSEnabled": false
+    }
+  },
+  "enums": {
+    "public.daily_reset_mode": {
+      "name": "daily_reset_mode",
+      "schema": "public",
+      "values": [
+        "fixed",
+        "rolling"
+      ]
+    },
+    "public.notification_type": {
+      "name": "notification_type",
+      "schema": "public",
+      "values": [
+        "circuit_breaker",
+        "daily_leaderboard",
+        "cost_alert"
+      ]
+    },
+    "public.webhook_provider_type": {
+      "name": "webhook_provider_type",
+      "schema": "public",
+      "values": [
+        "wechat",
+        "feishu",
+        "dingtalk",
+        "telegram",
+        "custom"
+      ]
+    }
+  },
+  "schemas": {},
+  "sequences": {},
+  "roles": {},
+  "policies": {},
+  "views": {},
+  "_meta": {
+    "columns": {},
+    "schemas": {},
+    "tables": {}
+  }
+}

+ 7 - 0
drizzle/meta/_journal.json

@@ -470,6 +470,13 @@
       "when": 1770728835628,
       "tag": "0066_hot_mauler",
       "breakpoints": true
+    },
+    {
+      "idx": 67,
+      "version": "7",
+      "when": 1771045274665,
+      "tag": "0067_gorgeous_mulholland_black",
+      "breakpoints": true
     }
   ]
 }

+ 2 - 0
messages/en/settings/config.json

@@ -53,6 +53,8 @@
     "enableThinkingSignatureRectifierDesc": "When Anthropic providers return thinking signature incompatibility or invalid request errors, automatically removes incompatible thinking blocks and retries once against the same provider (enabled by default).",
     "enableThinkingBudgetRectifier": "Enable Thinking Budget Rectifier",
     "enableThinkingBudgetRectifierDesc": "When Anthropic providers return budget_tokens < 1024 errors, automatically sets thinking budget to maximum (32000) and max_tokens to 64000 if needed, then retries once (enabled by default).",
+    "enableBillingHeaderRectifier": "Enable Billing Header Rectifier",
+    "enableBillingHeaderRectifierDesc": "Proactively removes x-anthropic-billing-header text blocks injected by Claude Code client into the system prompt, preventing Amazon Bedrock and other non-native Anthropic upstreams from returning 400 errors (enabled by default).",
     "enableCodexSessionIdCompletion": "Enable Codex Session ID Completion",
     "enableCodexSessionIdCompletionDesc": "When Codex requests provide only one of session_id (header) or prompt_cache_key (body), automatically completes the other. If both are missing, generates a UUID v7 session id and reuses it stably within the same conversation.",
     "enableClaudeMetadataUserIdInjection": "Enable Claude metadata.user_id Injection",

+ 2 - 0
messages/ja/settings/config.json

@@ -53,6 +53,8 @@
     "enableThinkingSignatureRectifierDesc": "Anthropic プロバイダーで thinking 署名の不整合や不正なリクエストエラーが発生した場合、thinking 関連ブロックを削除して同一プロバイダーへ1回だけ再試行します(既定で有効)。",
     "enableThinkingBudgetRectifier": "thinking 予算整流を有効化",
     "enableThinkingBudgetRectifierDesc": "Anthropic プロバイダーで budget_tokens < 1024 エラーが発生した場合、thinking 予算を最大値(32000)に設定し、必要に応じて max_tokens を 64000 に設定して1回だけ再試行します(既定で有効)。",
+    "enableBillingHeaderRectifier": "課金ヘッダー整流を有効化",
+    "enableBillingHeaderRectifierDesc": "Claude Code クライアントが system プロンプトに注入する x-anthropic-billing-header テキストブロックを事前に削除し、Amazon Bedrock などの非ネイティブ Anthropic 上流による 400 エラーを防止します(既定で有効)。",
     "enableCodexSessionIdCompletion": "Codex セッションID補完を有効化",
     "enableCodexSessionIdCompletionDesc": "Codex リクエストで session_id(ヘッダー)または prompt_cache_key(ボディ)のどちらか一方しか提供されない場合に、欠けている方を自動補完します。両方ない場合は UUID v7 のセッションIDを生成し、同一対話内で安定して再利用します。",
     "enableClaudeMetadataUserIdInjection": "Claude metadata.user_id 注入を有効化",

+ 2 - 0
messages/ru/settings/config.json

@@ -53,6 +53,8 @@
     "enableThinkingSignatureRectifierDesc": "Если Anthropic-провайдер возвращает ошибку несовместимой подписи thinking или некорректного запроса, автоматически удаляет несовместимые thinking-блоки и повторяет запрос один раз к тому же провайдеру (включено по умолчанию).",
     "enableThinkingBudgetRectifier": "Включить исправление thinking-budget",
     "enableThinkingBudgetRectifierDesc": "Если Anthropic-провайдер возвращает ошибку budget_tokens < 1024, автоматически устанавливает thinking budget на максимум (32000) и при необходимости max_tokens на 64000, затем повторяет запрос один раз (включено по умолчанию).",
+    "enableBillingHeaderRectifier": "Включить исправление billing-заголовка",
+    "enableBillingHeaderRectifierDesc": "Проактивно удаляет текстовые блоки x-anthropic-billing-header, добавленные клиентом Claude Code в системный промпт, предотвращая ошибки 400 от Amazon Bedrock и других не-Anthropic провайдеров (включено по умолчанию).",
     "enableCodexSessionIdCompletion": "Включить дополнение Session ID для Codex",
     "enableCodexSessionIdCompletionDesc": "Если в Codex-запросе присутствует только session_id (в заголовках) или prompt_cache_key (в теле), автоматически дополняет отсутствующее поле. Если оба отсутствуют, генерирует UUID v7 и стабильно переиспользует его в рамках одного диалога.",
     "enableClaudeMetadataUserIdInjection": "Включить инъекцию Claude metadata.user_id",

+ 2 - 0
messages/zh-CN/settings/config.json

@@ -42,6 +42,8 @@
     "enableThinkingSignatureRectifierDesc": "当 Anthropic 类型供应商返回 thinking 签名不兼容或非法请求等错误时,自动移除不兼容的 thinking 相关块并对同一供应商重试一次(默认开启)。",
     "enableThinkingBudgetRectifier": "启用 thinking 预算整流器",
     "enableThinkingBudgetRectifierDesc": "当 Anthropic 类型供应商返回 budget_tokens < 1024 错误时,自动将 thinking 预算设为最大值(32000),并在需要时将 max_tokens 设为 64000,然后重试一次(默认开启)。",
+    "enableBillingHeaderRectifier": "启用计费标头整流器",
+    "enableBillingHeaderRectifierDesc": "主动移除 Claude Code 客户端注入到 system 提示中的 x-anthropic-billing-header 文本块,防止 Amazon Bedrock 等非原生 Anthropic 上游返回 400 错误(默认开启)。",
     "enableCodexSessionIdCompletion": "启用 Codex Session ID 补全",
     "enableCodexSessionIdCompletionDesc": "当 Codex 请求仅提供 session_id(请求头)或 prompt_cache_key(请求体)之一时,自动补全另一个;若两者均缺失,则生成 UUID v7 会话 ID,并在同一对话内稳定复用。",
     "enableClaudeMetadataUserIdInjection": "启用 Claude metadata.user_id 注入",

+ 2 - 0
messages/zh-TW/settings/config.json

@@ -53,6 +53,8 @@
     "enableThinkingSignatureRectifierDesc": "當 Anthropic 類型供應商返回 thinking 簽名不相容或非法請求等錯誤時,自動移除不相容的 thinking 相關區塊並對同一供應商重試一次(預設開啟)。",
     "enableThinkingBudgetRectifier": "啟用 thinking 預算整流器",
     "enableThinkingBudgetRectifierDesc": "當 Anthropic 類型供應商返回 budget_tokens < 1024 錯誤時,自動將 thinking 預算設為最大值(32000),並在需要時將 max_tokens 設為 64000,然後重試一次(預設開啟)。",
+    "enableBillingHeaderRectifier": "啟用計費標頭整流器",
+    "enableBillingHeaderRectifierDesc": "主動移除 Claude Code 客戶端注入到 system 提示中的 x-anthropic-billing-header 文字區塊,防止 Amazon Bedrock 等非原生 Anthropic 上游回傳 400 錯誤(預設開啟)。",
     "enableCodexSessionIdCompletion": "啟用 Codex Session ID 補全",
     "enableCodexSessionIdCompletionDesc": "當 Codex 請求僅提供 session_id(請求頭)或 prompt_cache_key(請求體)之一時,自動補全另一個;若兩者皆缺失,則產生 UUID v7 會話 ID,並在同一對話內穩定複用。",
     "enableClaudeMetadataUserIdInjection": "啟用 Claude metadata.user_id 注入",

+ 4 - 3
scripts/clear-session-bindings.ts

@@ -32,7 +32,8 @@ import { drizzle, type PostgresJsDatabase } from "drizzle-orm/postgres-js";
 import Redis, { type RedisOptions } from "ioredis";
 import postgres from "postgres";
 
-import * as schema from "../src/drizzle/schema";
+import * as schema from "@/drizzle/schema";
+import { getGlobalActiveSessionsKey, getKeyActiveSessionsKey } from "@/lib/redis/active-session-keys";
 
 // ============================================================================
 // 常量配置
@@ -585,14 +586,14 @@ async function clearSessionBindings(
       pipeline.del(...keysToDelete);
       commandTypes.push("del");
 
-      pipeline.zrem("global:active_sessions", sessionId);
+      pipeline.zrem(getGlobalActiveSessionsKey(), sessionId);
       commandTypes.push("zrem");
 
       pipeline.zrem(`provider:${binding.providerId}:active_sessions`, sessionId);
       commandTypes.push("zrem");
 
       if (binding.keyId != null) {
-        pipeline.zrem(`key:${binding.keyId}:active_sessions`, sessionId);
+        pipeline.zrem(getKeyActiveSessionsKey(binding.keyId), sessionId);
         commandTypes.push("zrem");
       } else {
         missingKeyRefs += 1;

+ 2 - 3
src/actions/my-usage.ts

@@ -176,9 +176,8 @@ export interface MyUsageLogsResult {
   billingModelSource: BillingModelSource;
 }
 
-// All-time max age for total usage queries (~100 years in days)
-// This ensures "total" displays all-time usage, not just the last 365 days
-const ALL_TIME_MAX_AGE_DAYS = 36500;
+// Infinity means "all time" - no date filter applied to the query
+const ALL_TIME_MAX_AGE_DAYS = Infinity;
 
 /**
  * 查询用户在指定周期内的消费

+ 2 - 0
src/actions/system-config.ts

@@ -58,6 +58,7 @@ export async function saveSystemSettings(formData: {
   interceptAnthropicWarmupRequests?: boolean;
   enableThinkingSignatureRectifier?: boolean;
   enableThinkingBudgetRectifier?: boolean;
+  enableBillingHeaderRectifier?: boolean;
   enableCodexSessionIdCompletion?: boolean;
   enableClaudeMetadataUserIdInjection?: boolean;
   enableResponseFixer?: boolean;
@@ -93,6 +94,7 @@ export async function saveSystemSettings(formData: {
       interceptAnthropicWarmupRequests: validated.interceptAnthropicWarmupRequests,
       enableThinkingSignatureRectifier: validated.enableThinkingSignatureRectifier,
       enableThinkingBudgetRectifier: validated.enableThinkingBudgetRectifier,
+      enableBillingHeaderRectifier: validated.enableBillingHeaderRectifier,
       enableCodexSessionIdCompletion: validated.enableCodexSessionIdCompletion,
       enableClaudeMetadataUserIdInjection: validated.enableClaudeMetadataUserIdInjection,
       enableResponseFixer: validated.enableResponseFixer,

+ 23 - 4
src/actions/users.ts

@@ -38,6 +38,9 @@ import {
 import type { User, UserDisplay } from "@/types/user";
 import type { ActionResult } from "./types";
 
+/**
+ * 批量获取用户列表的查询参数(用于用户管理列表页)。
+ */
 export interface GetUsersBatchParams {
   cursor?: number;
   limit?: number;
@@ -58,18 +61,27 @@ export interface GetUsersBatchParams {
   sortOrder?: "asc" | "desc";
 }
 
+/**
+ * 批量获取用户列表的返回结果。
+ */
 export interface GetUsersBatchResult {
   users: UserDisplay[];
   nextCursor: number | null;
   hasMore: boolean;
 }
 
+/**
+ * 批量更新的结果统计(便于前端展示成功/失败数量)。
+ */
 export interface BatchUpdateResult {
   requestedCount: number;
   updatedCount: number;
   updatedIds: number[];
 }
 
+/**
+ * 批量更新用户的请求参数。
+ */
 export interface BatchUpdateUsersParams {
   userIds: number[];
   updates: {
@@ -83,6 +95,9 @@ export interface BatchUpdateUsersParams {
   };
 }
 
+/**
+ * 批量更新用户时的结构化错误(携带 errorCode 便于前端区分提示)。
+ */
 class BatchUpdateError extends Error {
   readonly errorCode: string;
 
@@ -1457,8 +1472,8 @@ export async function getUserAllLimitUsage(userId: number): Promise<
     limitTotal: { usage: number; limit: number | null };
   }>
 > {
-  // All-time max age for total usage queries (~100 years in days)
-  const ALL_TIME_MAX_AGE_DAYS = 36500;
+  // Infinity means "all time" - no date filter applied to the query
+  const ALL_TIME_MAX_AGE_DAYS = Infinity;
 
   try {
     const tError = await getTranslations("errors");
@@ -1560,6 +1575,9 @@ export async function resetUserAllStatistics(userId: number): Promise<ActionResu
     // 2. Clear Redis cache
     const { getRedisClient } = await import("@/lib/redis");
     const { scanPattern } = await import("@/lib/redis/scan-helper");
+    const { getKeyActiveSessionsKey, getUserActiveSessionsKey } = await import(
+      "@/lib/redis/active-session-keys"
+    );
     const redis = getRedisClient();
 
     if (redis && redis.status === "ready") {
@@ -1587,8 +1605,9 @@ export async function resetUserAllStatistics(userId: number): Promise<ActionResu
 
         // Active sessions
         for (const keyId of keyIds) {
-          pipeline.del(`key:${keyId}:active_sessions`);
+          pipeline.del(getKeyActiveSessionsKey(keyId));
         }
+        pipeline.del(getUserActiveSessionsKey(userId));
 
         // Cost keys
         for (const key of allCostKeys) {
@@ -1611,7 +1630,7 @@ export async function resetUserAllStatistics(userId: number): Promise<ActionResu
           userId,
           keyCount: keyIds.length,
           costKeysDeleted: allCostKeys.length,
-          activeSessionsDeleted: keyIds.length,
+          activeSessionsDeleted: keyIds.length + 1,
           durationMs: duration,
         });
       } catch (error) {

+ 49 - 57
src/app/[locale]/dashboard/quotas/users/page.tsx

@@ -6,7 +6,7 @@ import { QuotaToolbar } from "@/components/quota/quota-toolbar";
 import { Alert, AlertDescription } from "@/components/ui/alert";
 import { Link, redirect } from "@/i18n/routing";
 import { getSession } from "@/lib/auth";
-import { sumKeyTotalCostById, sumUserTotalCost } from "@/repository/statistics";
+import { sumKeyTotalCostBatchByIds, sumUserTotalCostBatch } from "@/repository/statistics";
 import { getSystemSettings } from "@/repository/system-config";
 import { UsersQuotaSkeleton } from "../_components/users-quota-skeleton";
 import type { UserKeyWithUsage, UserQuotaWithUsage } from "./_components/types";
@@ -15,71 +15,63 @@ import { UsersQuotaClient } from "./_components/users-quota-client";
 // Force dynamic rendering (this page needs real-time data and auth)
 export const dynamic = "force-dynamic";
 
-// Max age for "all time" total usage query (100 years in days)
-const ALL_TIME_MAX_AGE_DAYS = 36500;
-
 async function getUsersWithQuotas(): Promise<UserQuotaWithUsage[]> {
   const users = await getUsers();
 
-  const usersWithQuotas = await Promise.all(
-    users.map(async (user) => {
-      // Fetch quota usage and total cost in parallel
-      const [quotaResult, totalUsage] = await Promise.all([
-        getUserLimitUsage(user.id),
-        sumUserTotalCost(user.id, ALL_TIME_MAX_AGE_DAYS),
-      ]);
-
-      // Map keys with their total usage
-      const keysWithUsage: UserKeyWithUsage[] = await Promise.all(
-        user.keys.map(async (key) => {
-          const keyTotalUsage = await sumKeyTotalCostById(key.id, ALL_TIME_MAX_AGE_DAYS);
-          return {
-            id: key.id,
-            name: key.name,
-            status: key.status,
-            todayUsage: key.todayUsage,
-            totalUsage: keyTotalUsage,
-            limit5hUsd: key.limit5hUsd,
-            limitDailyUsd: key.limitDailyUsd,
-            limitWeeklyUsd: key.limitWeeklyUsd,
-            limitMonthlyUsd: key.limitMonthlyUsd,
-            limitTotalUsd: key.limitTotalUsd ?? null,
-            limitConcurrentSessions: key.limitConcurrentSessions,
-            dailyResetMode: key.dailyResetMode,
-            dailyResetTime: key.dailyResetTime,
-          };
-        })
-      );
-
-      return {
-        id: user.id,
-        name: user.name,
-        note: user.note,
-        role: user.role,
-        isEnabled: user.isEnabled,
-        expiresAt: user.expiresAt ?? null,
-        providerGroup: user.providerGroup,
-        tags: user.tags,
-        quota: quotaResult.ok ? quotaResult.data : null,
-        limit5hUsd: user.limit5hUsd ?? null,
-        limitWeeklyUsd: user.limitWeeklyUsd ?? null,
-        limitMonthlyUsd: user.limitMonthlyUsd ?? null,
-        limitTotalUsd: user.limitTotalUsd ?? null,
-        limitConcurrentSessions: user.limitConcurrentSessions ?? null,
-        totalUsage,
-        keys: keysWithUsage,
-      };
-    })
-  );
-
-  return usersWithQuotas;
+  const allUserIds = users.map((u) => u.id);
+  const allKeyIds = users.flatMap((u) => u.keys.map((k) => k.id));
+
+  // 3 queries total instead of N+M individual SUM queries
+  const [quotaResults, userCostMap, keyCostMap] = await Promise.all([
+    Promise.all(users.map((u) => getUserLimitUsage(u.id))),
+    sumUserTotalCostBatch(allUserIds),
+    sumKeyTotalCostBatchByIds(allKeyIds),
+  ]);
+
+  return users.map((user, idx) => {
+    const quotaResult = quotaResults[idx];
+
+    const keysWithUsage: UserKeyWithUsage[] = user.keys.map((key) => ({
+      id: key.id,
+      name: key.name,
+      status: key.status,
+      todayUsage: key.todayUsage,
+      totalUsage: keyCostMap.get(key.id) ?? 0,
+      limit5hUsd: key.limit5hUsd,
+      limitDailyUsd: key.limitDailyUsd,
+      limitWeeklyUsd: key.limitWeeklyUsd,
+      limitMonthlyUsd: key.limitMonthlyUsd,
+      limitTotalUsd: key.limitTotalUsd ?? null,
+      limitConcurrentSessions: key.limitConcurrentSessions,
+      dailyResetMode: key.dailyResetMode,
+      dailyResetTime: key.dailyResetTime,
+    }));
+
+    return {
+      id: user.id,
+      name: user.name,
+      note: user.note,
+      role: user.role,
+      isEnabled: user.isEnabled,
+      expiresAt: user.expiresAt ?? null,
+      providerGroup: user.providerGroup,
+      tags: user.tags,
+      quota: quotaResult.ok ? quotaResult.data : null,
+      limit5hUsd: user.limit5hUsd ?? null,
+      limitWeeklyUsd: user.limitWeeklyUsd ?? null,
+      limitMonthlyUsd: user.limitMonthlyUsd ?? null,
+      limitTotalUsd: user.limitTotalUsd ?? null,
+      limitConcurrentSessions: user.limitConcurrentSessions ?? null,
+      totalUsage: userCostMap.get(user.id) ?? 0,
+      keys: keysWithUsage,
+    };
+  });
 }
 
 export default async function UsersQuotaPage({ params }: { params: Promise<{ locale: string }> }) {
   const { locale } = await params;
   const session = await getSession();
 
-  // 权限检查:仅 admin 用户可访问
   if (!session || session.user.role !== "admin") {
     return redirect({ href: session ? "/dashboard/my-quota" : "/login", locale });
   }

+ 29 - 0
src/app/[locale]/settings/config/_components/system-settings-form.tsx

@@ -55,6 +55,7 @@ interface SystemSettingsFormProps {
     | "enableHttp2"
     | "interceptAnthropicWarmupRequests"
     | "enableThinkingSignatureRectifier"
+    | "enableBillingHeaderRectifier"
     | "enableThinkingBudgetRectifier"
     | "enableCodexSessionIdCompletion"
     | "enableClaudeMetadataUserIdInjection"
@@ -101,6 +102,9 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps)
   const [enableThinkingSignatureRectifier, setEnableThinkingSignatureRectifier] = useState(
     initialSettings.enableThinkingSignatureRectifier
   );
+  const [enableBillingHeaderRectifier, setEnableBillingHeaderRectifier] = useState(
+    initialSettings.enableBillingHeaderRectifier
+  );
   const [enableThinkingBudgetRectifier, setEnableThinkingBudgetRectifier] = useState(
     initialSettings.enableThinkingBudgetRectifier
   );
@@ -167,6 +171,7 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps)
         enableHttp2,
         interceptAnthropicWarmupRequests,
         enableThinkingSignatureRectifier,
+        enableBillingHeaderRectifier,
         enableThinkingBudgetRectifier,
         enableCodexSessionIdCompletion,
         enableClaudeMetadataUserIdInjection,
@@ -195,6 +200,7 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps)
         setEnableHttp2(result.data.enableHttp2);
         setInterceptAnthropicWarmupRequests(result.data.interceptAnthropicWarmupRequests);
         setEnableThinkingSignatureRectifier(result.data.enableThinkingSignatureRectifier);
+        setEnableBillingHeaderRectifier(result.data.enableBillingHeaderRectifier);
         setEnableThinkingBudgetRectifier(result.data.enableThinkingBudgetRectifier);
         setEnableCodexSessionIdCompletion(result.data.enableCodexSessionIdCompletion);
         setEnableClaudeMetadataUserIdInjection(result.data.enableClaudeMetadataUserIdInjection);
@@ -447,6 +453,29 @@ export function SystemSettingsForm({ initialSettings }: SystemSettingsFormProps)
           />
         </div>
 
+        {/* Enable Billing Header Rectifier */}
+        <div className="p-4 rounded-xl bg-white/[0.02] border border-white/5 flex items-center justify-between hover:bg-white/[0.04] transition-colors">
+          <div className="flex items-start gap-3">
+            <div className="w-8 h-8 flex items-center justify-center rounded-lg bg-amber-500/10 text-amber-400 shrink-0">
+              <FileCode className="h-4 w-4" />
+            </div>
+            <div>
+              <p className="text-sm font-medium text-foreground">
+                {t("enableBillingHeaderRectifier")}
+              </p>
+              <p className="text-xs text-muted-foreground mt-0.5">
+                {t("enableBillingHeaderRectifierDesc")}
+              </p>
+            </div>
+          </div>
+          <Switch
+            id="enable-billing-header-rectifier"
+            checked={enableBillingHeaderRectifier}
+            onCheckedChange={(checked) => setEnableBillingHeaderRectifier(checked)}
+            disabled={isPending}
+          />
+        </div>
+
         {/* Enable Codex Session ID Completion */}
         <div className="p-4 rounded-xl bg-white/[0.02] border border-white/5 flex items-center justify-between hover:bg-white/[0.04] transition-colors">
           <div className="flex items-start gap-3">

+ 1 - 0
src/app/[locale]/settings/config/page.tsx

@@ -50,6 +50,7 @@ async function SettingsConfigContent() {
             interceptAnthropicWarmupRequests: settings.interceptAnthropicWarmupRequests,
             enableThinkingSignatureRectifier: settings.enableThinkingSignatureRectifier,
             enableThinkingBudgetRectifier: settings.enableThinkingBudgetRectifier,
+            enableBillingHeaderRectifier: settings.enableBillingHeaderRectifier,
             enableCodexSessionIdCompletion: settings.enableCodexSessionIdCompletion,
             enableClaudeMetadataUserIdInjection: settings.enableClaudeMetadataUserIdInjection,
             enableResponseFixer: settings.enableResponseFixer,

+ 78 - 0
src/app/v1/_lib/proxy/billing-header-rectifier.ts

@@ -0,0 +1,78 @@
+/**
+ * Billing Header Rectifier - Proactive (pre-send) rectifier for Claude Code client
+ * billing header injection.
+ *
+ * Problem: Claude Code client v2.1.36+ injects `x-anthropic-billing-header: ...`
+ * as a text block inside the request body's `system` content array. Non-native
+ * Anthropic upstreams (e.g. Amazon Bedrock) reject this with 400:
+ * "x-anthropic-billing-header is a reserved keyword and may not be used in the system prompt."
+ *
+ * Solution: Strip these blocks before forwarding to upstream.
+ */
+
+export type BillingHeaderRectifierResult = {
+  applied: boolean;
+  removedCount: number;
+  extractedValues: string[];
+};
+
+const BILLING_HEADER_PATTERN = /^\s*x-anthropic-billing-header\s*:/i;
+
+/**
+ * Remove x-anthropic-billing-header text blocks from the request system prompt.
+ * Mutates the message object in place (matches existing rectifier conventions).
+ */
+export function rectifyBillingHeader(
+  message: Record<string, unknown>
+): BillingHeaderRectifierResult {
+  const system = message.system;
+
+  // Case 1: system is undefined/null/missing
+  if (system === undefined || system === null) {
+    return { applied: false, removedCount: 0, extractedValues: [] };
+  }
+
+  // Case 2: system is a plain string
+  if (typeof system === "string") {
+    if (BILLING_HEADER_PATTERN.test(system)) {
+      const extractedValues = [system.trim()];
+      delete message.system;
+      return { applied: true, removedCount: 1, extractedValues };
+    }
+    return { applied: false, removedCount: 0, extractedValues: [] };
+  }
+
+  // Case 3: system is an array of content blocks
+  if (Array.isArray(system)) {
+    const extractedValues: string[] = [];
+    const filtered: unknown[] = [];
+
+    for (const block of system) {
+      if (
+        block &&
+        typeof block === "object" &&
+        (block as Record<string, unknown>).type === "text" &&
+        typeof (block as Record<string, unknown>).text === "string" &&
+        BILLING_HEADER_PATTERN.test((block as Record<string, unknown>).text as string)
+      ) {
+        extractedValues.push(((block as Record<string, unknown>).text as string).trim());
+      } else {
+        filtered.push(block);
+      }
+    }
+
+    if (extractedValues.length > 0) {
+      // Mutate in place: replace system array contents
+      system.length = 0;
+      for (const item of filtered) {
+        system.push(item);
+      }
+      return { applied: true, removedCount: extractedValues.length, extractedValues };
+    }
+
+    return { applied: false, removedCount: 0, extractedValues: [] };
+  }
+
+  // Unknown type: no-op
+  return { applied: false, removedCount: 0, extractedValues: [] };
+}

+ 27 - 1
src/app/v1/_lib/proxy/forwarder.ts

@@ -38,6 +38,7 @@ import { GeminiAuth } from "../gemini/auth";
 import { GEMINI_PROTOCOL } from "../gemini/protocol";
 import { HeaderProcessor } from "../headers";
 import { buildProxyUrl } from "../url";
+import { rectifyBillingHeader } from "./billing-header-rectifier";
 import {
   buildRequestDetails,
   categorizeErrorAsync,
@@ -51,7 +52,6 @@ import {
   ProxyError,
   sanitizeUrl,
 } from "./errors";
-
 import { ModelRedirector } from "./model-redirector";
 import { ProxyProviderResolver } from "./provider-selector";
 import type { ProxySession } from "./session";
@@ -1766,6 +1766,32 @@ export class ProxyForwarder {
       // Anthropic 供应商级参数覆写(默认 inherit=遵循客户端)
       // 说明:允许管理员在供应商层面强制覆写 max_tokens 和 thinking.budget_tokens
       if (provider.providerType === "claude" || provider.providerType === "claude-auth") {
+        // Billing header rectifier: proactively strip x-anthropic-billing-header from system prompt
+        {
+          const settings = await getCachedSystemSettings();
+          const billingRectifierEnabled = settings.enableBillingHeaderRectifier ?? true;
+          if (billingRectifierEnabled) {
+            const billingResult = rectifyBillingHeader(
+              session.request.message as Record<string, unknown>
+            );
+            if (billingResult.applied) {
+              session.addSpecialSetting({
+                type: "billing_header_rectifier",
+                scope: "request",
+                hit: true,
+                removedCount: billingResult.removedCount,
+                extractedValues: billingResult.extractedValues,
+              });
+              logger.info("ProxyForwarder: Billing header rectifier applied", {
+                providerId: provider.id,
+                providerName: provider.name,
+                removedCount: billingResult.removedCount,
+              });
+              await persistSpecialSettings(session);
+            }
+          }
+        }
+
         const { request: anthropicOverridden, audit: anthropicAudit } =
           applyAnthropicProviderOverridesWithAudit(
             provider,

+ 51 - 57
src/app/v1/_lib/proxy/rate-limit-guard.ts

@@ -1,7 +1,8 @@
 import { logger } from "@/lib/logger";
 import { RateLimitService } from "@/lib/rate-limit";
-import { resolveKeyConcurrentSessionLimit } from "@/lib/rate-limit/concurrent-session-limit";
+import { resolveKeyUserConcurrentSessionLimits } from "@/lib/rate-limit/concurrent-session-limit";
 import { getResetInfo, getResetInfoWithMode } from "@/lib/rate-limit/time-utils";
+import { SessionManager } from "@/lib/session-manager";
 import { ERROR_CODES, getErrorMessageServer } from "@/lib/utils/error-messages";
 import { RateLimitError } from "./errors";
 import type { ProxySession } from "./session";
@@ -29,15 +30,20 @@ function parseLimitInfo(reason: string): { currentUsage: number; limitValue: num
   return { currentUsage: 0, limitValue: 0 };
 }
 
+/**
+ * 限流守卫:集中执行 Key/User 各维度限额校验(含并发 Session / RPM 等资源保护)。
+ *
+ * 调用时机:`ProxySessionGuard` 分配 sessionId 之后、转发到上游之前。
+ */
 export class ProxyRateLimitGuard {
   /**
    * 检查限流(Key 层 + User 层)
    *
    * 检查顺序(基于 Codex 专业分析):
    * 1-2. 永久硬限制:Key 总限额 → User 总限额
-   * 3-5. 资源/频率保护:Key 并发 → User 并发 → User RPM
-   * 6-9. 短期周期限额:Key 5h → User 5h → Key 每日 → User 每日
-   * 10-13. 中长期周期限额:Key 周 → User 周 → Key 月 → User 月
+   * 3-4. 资源/频率保护:Key/User 并发 → User RPM
+   * 5-8. 短期周期限额:Key 5h → User 5h → Key 每日 → User 每日
+   * 9-12. 中长期周期限额:Key 周 → User 周 → Key 月 → User 月
    *
    * 设计原则:
    * - 硬上限优先于周期上限
@@ -120,20 +126,48 @@ export class ProxyRateLimitGuard {
 
     // 3. Key 并发 Session(避免创建上游连接)
     // Key 未设置时,继承 User 并发上限(避免 UI/心智模型不一致:User 设置了并发,但 Key 仍显示“无限制”)
-    const effectiveKeyConcurrentLimit = resolveKeyConcurrentSessionLimit(
+    const {
+      effectiveKeyLimit: effectiveKeyConcurrentLimit,
+      normalizedUserLimit: normalizedUserConcurrentLimit,
+    } = resolveKeyUserConcurrentSessionLimits(
       key.limitConcurrentSessions ?? 0,
       user.limitConcurrentSessions
     );
-    const sessionCheck = await RateLimitService.checkSessionLimit(
+
+    // 注意:并发 Session 限制必须“原子性检查 + 追踪”,否则会被并发击穿(尤其是多 Key 同时使用时)
+    // 理论上 session guard 一定会分配 sessionId;这里兜底生成,避免降级回非原子路径
+    const ensuredSessionId = session.sessionId || SessionManager.generateSessionId();
+    if (!session.sessionId) {
+      logger.warn(
+        `[RateLimit] SessionId missing in rate-limit-guard, using fallback: key=${key.id}, user=${user.id} (potential atomicity gap)`
+      );
+      session.setSessionId(ensuredSessionId);
+    }
+
+    const concurrentCheck = await RateLimitService.checkAndTrackKeyUserSession(
       key.id,
-      "key",
-      effectiveKeyConcurrentLimit
+      user.id,
+      ensuredSessionId,
+      effectiveKeyConcurrentLimit,
+      normalizedUserConcurrentLimit
     );
 
-    if (!sessionCheck.allowed) {
-      logger.warn(`[RateLimit] Key session limit exceeded: key=${key.id}, ${sessionCheck.reason}`);
+    if (!concurrentCheck.allowed) {
+      const rejectedBy = concurrentCheck.rejectedBy ?? "key";
+      const fallbackCurrentUsage =
+        rejectedBy === "user" ? concurrentCheck.userCount : concurrentCheck.keyCount;
+      const fallbackLimitValue =
+        rejectedBy === "user" ? normalizedUserConcurrentLimit : effectiveKeyConcurrentLimit;
+      const currentUsage = Number(concurrentCheck.reasonParams?.current);
+      const limitValue = Number(concurrentCheck.reasonParams?.limit);
+      const resolvedCurrentUsage = Number.isFinite(currentUsage)
+        ? currentUsage
+        : fallbackCurrentUsage;
+      const resolvedLimitValue = Number.isFinite(limitValue) ? limitValue : fallbackLimitValue;
 
-      const { currentUsage, limitValue } = parseLimitInfo(sessionCheck.reason!);
+      logger.warn(
+        `[RateLimit] ${rejectedBy === "user" ? "User" : "Key"} session limit exceeded: key=${key.id}, user=${user.id}, current=${resolvedCurrentUsage}, limit=${resolvedLimitValue}`
+      );
 
       const resetTime = new Date().toISOString();
 
@@ -141,10 +175,10 @@ export class ProxyRateLimitGuard {
       const locale = await getLocale();
       const message = await getErrorMessageServer(
         locale,
-        ERROR_CODES.RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED,
+        concurrentCheck.reasonCode ?? ERROR_CODES.RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED,
         {
-          current: String(currentUsage),
-          limit: String(limitValue),
+          current: String(resolvedCurrentUsage),
+          limit: String(resolvedLimitValue),
         }
       );
 
@@ -152,54 +186,14 @@ export class ProxyRateLimitGuard {
         "rate_limit_error",
         message,
         "concurrent_sessions",
-        currentUsage,
-        limitValue,
+        resolvedCurrentUsage,
+        resolvedLimitValue,
         resetTime,
         null
       );
     }
 
-    // 4. User 并发 Session(账号级并发保护)
-    if (user.limitConcurrentSessions != null && user.limitConcurrentSessions > 0) {
-      const userSessionCheck = await RateLimitService.checkSessionLimit(
-        user.id,
-        "user",
-        user.limitConcurrentSessions
-      );
-
-      if (!userSessionCheck.allowed) {
-        logger.warn(
-          `[RateLimit] User session limit exceeded: user=${user.id}, ${userSessionCheck.reason}`
-        );
-
-        const { currentUsage, limitValue } = parseLimitInfo(userSessionCheck.reason!);
-
-        const resetTime = new Date().toISOString();
-
-        const { getLocale } = await import("next-intl/server");
-        const locale = await getLocale();
-        const message = await getErrorMessageServer(
-          locale,
-          ERROR_CODES.RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED,
-          {
-            current: String(currentUsage),
-            limit: String(limitValue),
-          }
-        );
-
-        throw new RateLimitError(
-          "rate_limit_error",
-          message,
-          "concurrent_sessions",
-          currentUsage,
-          limitValue,
-          resetTime,
-          null
-        );
-      }
-    }
-
-    // 5. User RPM(频率闸门,挡住高频噪声)- null/0 表示无限制
+    // 4. User RPM(频率闸门,挡住高频噪声)- null/0 表示无限制
     if (user.rpm != null && user.rpm > 0) {
       const rpmCheck = await RateLimitService.checkRpmLimit(user.id, "user", user.rpm);
       if (!rpmCheck.allowed) {

+ 103 - 41
src/app/v1/_lib/proxy/response-handler.ts

@@ -10,7 +10,7 @@ import { SessionManager } from "@/lib/session-manager";
 import { SessionTracker } from "@/lib/session-tracker";
 import { calculateRequestCost } from "@/lib/utils/cost-calculation";
 import { hasValidPriceData } from "@/lib/utils/price-data";
-import { parseSSEData } from "@/lib/utils/sse";
+import { isSSEText, parseSSEData } from "@/lib/utils/sse";
 import { detectUpstreamErrorFromSseOrJsonText } from "@/lib/utils/upstream-error-detection";
 import {
   updateMessageRequestCost,
@@ -961,53 +961,95 @@ export class ProxyResponseHandler {
 
           let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
           // 保护:避免透传 stats 任务把超大响应体无界缓存在内存中(DoS/OOM 风险)
-          // 说明:这里用于统计/结算的内容仅保留“尾部窗口”(最近 MAX_STATS_BUFFER_BYTES),用于尽可能解析 usage/假200。
-          // 若响应体极大,仍会完整 drain 上游(reader.read),但不再累计完整字符串。
+          // 说明:用于统计/结算的内容采用“头部 + 尾部窗口”:
+          // - 头部保留前 MAX_STATS_HEAD_BYTES(便于解析可能前置的 metadata)
+          // - 尾部保留最近 MAX_STATS_TAIL_BYTES(便于解析结尾 usage/假 200 等)
+          // - 中间部分会被丢弃(wasTruncated=true),统计将退化为 best-effort
           const MAX_STATS_BUFFER_BYTES = 10 * 1024 * 1024; // 10MB
-          const MAX_STATS_BUFFER_CHUNKS = 8192;
-          const chunks: string[] = [];
-          const chunkBytes: number[] = [];
-          let chunkHead = 0;
-          let bufferedBytes = 0;
+          const MAX_STATS_HEAD_BYTES = 1024 * 1024; // 1MB
+          const MAX_STATS_TAIL_BYTES = MAX_STATS_BUFFER_BYTES - MAX_STATS_HEAD_BYTES;
+          const MAX_STATS_TAIL_CHUNKS = 8192;
+
+          const headChunks: string[] = [];
+          let headBufferedBytes = 0;
+
+          const tailChunks: string[] = [];
+          const tailChunkBytes: number[] = [];
+          let tailHead = 0;
+          let tailBufferedBytes = 0;
           let wasTruncated = false;
+          let inTailMode = false;
+
+          const joinTailChunks = (): string => {
+            if (tailHead <= 0) return tailChunks.join("");
+            return tailChunks.slice(tailHead).join("");
+          };
 
           const joinChunks = (): string => {
-            if (chunkHead <= 0) return chunks.join("");
-            return chunks.slice(chunkHead).join("");
+            const headText = headChunks.join("");
+            if (!inTailMode) {
+              return headText;
+            }
+
+            const tailText = joinTailChunks();
+
+            // 用 SSE comment 标记被截断的中间段;parseSSEData 会忽略 ":" 开头的行
+            if (wasTruncated) {
+              // 插入空行强制 flush event,避免“头+尾”拼接后跨 event 误拼接数据行
+              return `${headText}\n\n: [cch_truncated]\n\n${tailText}`;
+            }
+
+            return `${headText}${tailText}`;
           };
 
           const pushChunk = (text: string, bytes: number) => {
             if (!text) return;
-            chunks.push(text);
-            chunkBytes.push(bytes);
-            bufferedBytes += bytes;
-
-            // 仅保留尾部窗口,避免内存无界增长
-            while (bufferedBytes > MAX_STATS_BUFFER_BYTES && chunkHead < chunkBytes.length) {
-              bufferedBytes -= chunkBytes[chunkHead] ?? 0;
-              chunks[chunkHead] = "";
-              chunkBytes[chunkHead] = 0;
-              chunkHead += 1;
-              wasTruncated = true;
-            }
 
-            // 定期压缩数组,避免 head 指针过大导致 slice/join 性能退化
-            if (chunkHead > 4096) {
-              chunks.splice(0, chunkHead);
-              chunkBytes.splice(0, chunkHead);
-              chunkHead = 0;
-            }
+            const pushToTail = () => {
+              tailChunks.push(text);
+              tailChunkBytes.push(bytes);
+              tailBufferedBytes += bytes;
+
+              // 仅保留尾部窗口,避免内存无界增长
+              while (tailBufferedBytes > MAX_STATS_TAIL_BYTES && tailHead < tailChunkBytes.length) {
+                tailBufferedBytes -= tailChunkBytes[tailHead] ?? 0;
+                tailChunks[tailHead] = "";
+                tailChunkBytes[tailHead] = 0;
+                tailHead += 1;
+                wasTruncated = true;
+              }
+
+              // 定期压缩数组,避免 head 指针过大导致 slice/join 性能退化
+              if (tailHead > 4096) {
+                tailChunks.splice(0, tailHead);
+                tailChunkBytes.splice(0, tailHead);
+                tailHead = 0;
+              }
+
+              // 防御:限制 chunk 数量,避免大量超小 chunk 导致对象/数组膨胀(即使总字节数已受限)
+              const keptCount = tailChunks.length - tailHead;
+              if (keptCount > MAX_STATS_TAIL_CHUNKS) {
+                const joined = joinTailChunks();
+                tailChunks.length = 0;
+                tailChunkBytes.length = 0;
+                tailHead = 0;
+                tailChunks.push(joined);
+                tailChunkBytes.push(tailBufferedBytes);
+              }
+            };
 
-            // 防御:限制 chunk 数量,避免大量超小 chunk 导致对象/数组膨胀(即使总字节数已受限)
-            const keptCount = chunks.length - chunkHead;
-            if (keptCount > MAX_STATS_BUFFER_CHUNKS) {
-              const joined = joinChunks();
-              chunks.length = 0;
-              chunkBytes.length = 0;
-              chunkHead = 0;
-              chunks.push(joined);
-              chunkBytes.push(bufferedBytes);
+            // 优先填充 head;超过 head 上限后切到 tail(但不代表一定发生截断,只有 tail 溢出才算截断
+            if (!inTailMode) {
+              if (headBufferedBytes + bytes <= MAX_STATS_HEAD_BYTES) {
+                headChunks.push(text);
+                headBufferedBytes += bytes;
+                return;
+              }
+
+              inTailMode = true;
             }
+
+            pushToTail();
           };
           const decoder = new TextDecoder();
           let isFirstChunk = true;
@@ -1035,8 +1077,10 @@ export class ProxyResponseHandler {
                 providerId: provider.id,
                 providerName: provider.name,
                 idleTimeoutMs,
-                chunksCollected: Math.max(0, chunks.length - chunkHead),
-                bufferedBytes,
+                chunksCollected: headChunks.length + Math.max(0, tailChunks.length - tailHead),
+                headBufferedBytes,
+                tailBufferedBytes,
+                bufferedBytes: headBufferedBytes + tailBufferedBytes,
                 wasTruncated,
               });
               // 终止上游连接:让透传到客户端的连接也尽快结束,避免永久悬挂占用资源
@@ -1109,7 +1153,25 @@ export class ProxyResponseHandler {
                   session.recordTtfb();
                   clearResponseTimeoutOnce(chunkSize);
                 }
-                pushChunk(decoder.decode(value, { stream: true }), chunkSize);
+
+                // 尽量填满 head:边界 chunk 可能跨过 head 上限,按 byte 切分以避免 head 少于 1MB
+                if (!inTailMode && headBufferedBytes < MAX_STATS_HEAD_BYTES) {
+                  const remainingHeadBytes = MAX_STATS_HEAD_BYTES - headBufferedBytes;
+                  if (remainingHeadBytes > 0 && chunkSize > remainingHeadBytes) {
+                    const headPart = value.subarray(0, remainingHeadBytes);
+                    const tailPart = value.subarray(remainingHeadBytes);
+
+                    const headText = decoder.decode(headPart, { stream: true });
+                    pushChunk(headText, remainingHeadBytes);
+
+                    const tailText = decoder.decode(tailPart, { stream: true });
+                    pushChunk(tailText, chunkSize - remainingHeadBytes);
+                  } else {
+                    pushChunk(decoder.decode(value, { stream: true }), chunkSize);
+                  }
+                } else {
+                  pushChunk(decoder.decode(value, { stream: true }), chunkSize);
+                }
               }
 
               // 首块数据到达后才启动 idle timer(避免与首字节超时职责重叠)
@@ -2215,7 +2277,7 @@ export function parseUsageFromResponseText(
   // SSE 解析:支持两种格式
   // 1. 标准 SSE (event: + data:) - Claude/OpenAI
   // 2. 纯 data: 格式 - Gemini
-  if (!usageMetrics && responseText.includes("data:")) {
+  if (!usageMetrics && isSSEText(responseText)) {
     const events = parseSSEData(responseText);
 
     // Claude SSE 特殊处理:

+ 9 - 1
src/app/v1/_lib/proxy/session-guard.ts

@@ -1,5 +1,6 @@
 import { getCachedSystemSettings } from "@/lib/config";
 import { logger } from "@/lib/logger";
+import { resolveKeyUserConcurrentSessionLimits } from "@/lib/rate-limit/concurrent-session-limit";
 import { SessionManager } from "@/lib/session-manager";
 import { SessionTracker } from "@/lib/session-tracker";
 import { completeCodexSessionIdentifiers } from "../codex/session-completer";
@@ -137,7 +138,14 @@ export class ProxySessionGuard {
 
       // 5. 追踪 session(添加到活跃集合)
       // Warmup 拦截请求不应计入并发会话(避免影响后续真实请求的限额判断)
-      if (!warmupMaybeIntercepted) {
+      // 注意:当启用 Key/User 并发 Session 上限时,必须在 RateLimitGuard 中做“原子性检查+追踪”。
+      // 否则先追踪再检查会导致所有新 session 都被视为“已追踪”,从而击穿并发上限。
+      const { enabled: hasConcurrentSessionLimit } = resolveKeyUserConcurrentSessionLimits(
+        session.authState?.key?.limitConcurrentSessions,
+        session.authState?.user?.limitConcurrentSessions
+      );
+
+      if (!warmupMaybeIntercepted && !hasConcurrentSessionLimit) {
         void SessionTracker.trackSession(sessionId, keyId, session.authState?.user?.id).catch(
           (err) => {
             logger.error("[ProxySessionGuard] Failed to track session:", err);

+ 6 - 0
src/drizzle/schema.ts

@@ -620,6 +620,12 @@ export const systemSettings = pgTable('system_settings', {
     .notNull()
     .default(true),
 
+  // billing header 整流器(默认开启)
+  // 开启后:主动移除 Claude Code 客户端注入到 system 提示中的 x-anthropic-billing-header 文本块
+  enableBillingHeaderRectifier: boolean('enable_billing_header_rectifier')
+    .notNull()
+    .default(true),
+
   // Codex Session ID 补全(默认开启)
   // 开启后:当 Codex 请求缺少 session_id / prompt_cache_key 时,自动补全或生成稳定的会话标识
   enableCodexSessionIdCompletion: boolean('enable_codex_session_id_completion')

+ 3 - 0
src/lib/config/system-settings-cache.ts

@@ -30,6 +30,7 @@ const DEFAULT_SETTINGS: Pick<
   | "interceptAnthropicWarmupRequests"
   | "enableThinkingSignatureRectifier"
   | "enableThinkingBudgetRectifier"
+  | "enableBillingHeaderRectifier"
   | "enableCodexSessionIdCompletion"
   | "enableClaudeMetadataUserIdInjection"
   | "enableResponseFixer"
@@ -39,6 +40,7 @@ const DEFAULT_SETTINGS: Pick<
   interceptAnthropicWarmupRequests: false,
   enableThinkingSignatureRectifier: true,
   enableThinkingBudgetRectifier: true,
+  enableBillingHeaderRectifier: true,
   enableCodexSessionIdCompletion: true,
   enableClaudeMetadataUserIdInjection: true,
   enableResponseFixer: true,
@@ -111,6 +113,7 @@ export async function getCachedSystemSettings(): Promise<SystemSettings> {
       interceptAnthropicWarmupRequests: DEFAULT_SETTINGS.interceptAnthropicWarmupRequests,
       enableThinkingSignatureRectifier: DEFAULT_SETTINGS.enableThinkingSignatureRectifier,
       enableThinkingBudgetRectifier: DEFAULT_SETTINGS.enableThinkingBudgetRectifier,
+      enableBillingHeaderRectifier: DEFAULT_SETTINGS.enableBillingHeaderRectifier,
       enableCodexSessionIdCompletion: DEFAULT_SETTINGS.enableCodexSessionIdCompletion,
       enableClaudeMetadataUserIdInjection: DEFAULT_SETTINGS.enableClaudeMetadataUserIdInjection,
       enableResponseFixer: DEFAULT_SETTINGS.enableResponseFixer,

+ 53 - 15
src/lib/proxy-agent/agent-pool.ts

@@ -165,6 +165,15 @@ export class AgentPoolImpl implements AgentPool {
   };
   /** Pending agent creation promises to prevent race conditions */
   private pendingCreations: Map<string, Promise<GetAgentResult>> = new Map();
+  /**
+   * Pending destroy/close promises (best-effort).
+   *
+   * 说明:
+   * - 驱逐/清理路径为了避免全局卡死,必须 fire-and-forget(不 await)。
+   * - 但在 shutdown() 中我们仍希望尽量“优雅收尾”,因此在这里追踪 pending 的关闭任务。
+   * - 若某些 dispatcher 永不 settle,这里会在超时后丢弃引用,避免内存泄漏。
+   */
+  private pendingCleanups: Set<Promise<void>> = new Set();
 
   constructor(config: Partial<AgentPoolConfig> = {}) {
     this.config = { ...DEFAULT_CONFIG, ...config };
@@ -329,13 +338,31 @@ export class AgentPoolImpl implements AgentPool {
       this.cleanupTimer = null;
     }
 
-    const closePromises: Promise<void>[] = [];
+    // closeAgent 本身是 fire-and-forget(不 await destroy/close),这里并行触发即可。
+    await Promise.allSettled(
+      Array.from(this.cache.entries()).map(([key, cached]) => this.closeAgent(cached.agent, key))
+    );
 
-    for (const [key, cached] of this.cache.entries()) {
-      closePromises.push(this.closeAgent(cached.agent, key));
+    // Best-effort:等待部分 pending cleanup 完成,但永不无限等待(避免重蹈 “close() 等待 in-flight” 的覆辙)
+    if (this.pendingCleanups.size > 0) {
+      const pending = Array.from(this.pendingCleanups);
+      const WAIT_MS = 2000;
+      let timeoutId: NodeJS.Timeout | null = null;
+      try {
+        await Promise.race([
+          Promise.allSettled(pending).then(() => {}),
+          new Promise<void>((resolve) => {
+            timeoutId = setTimeout(resolve, WAIT_MS);
+            timeoutId.unref();
+          }),
+        ]);
+      } finally {
+        if (timeoutId) clearTimeout(timeoutId);
+      }
+
+      this.pendingCleanups.clear();
     }
 
-    await Promise.all(closePromises);
     this.cache.clear();
     this.unhealthyKeys.clear();
 
@@ -372,23 +399,34 @@ export class AgentPoolImpl implements AgentPool {
 
       // 关键点:驱逐/清理路径不能等待 in-flight(否则会把 getAgent() 也阻塞住,导致全局“requesting”)
       // 因此这里发起 destroy/close 后不 await,仅记录异常,确保 eviction 始终快速返回。
-      if (operation === "destroy") {
-        agent.destroy().catch((error) => {
-          logger.warn("AgentPool: Error closing agent", {
-            key,
-            operation,
-            error: error instanceof Error ? error.message : String(error),
-          });
-        });
-      } else if (operation === "close") {
-        agent.close().catch((error) => {
+      // 同时将 promise 纳入 pendingCleanups,便于 shutdown() 做 best-effort 的“优雅收尾”。
+      const cleanupPromise =
+        operation === "destroy" ? agent.destroy() : operation === "close" ? agent.close() : null;
+
+      if (!cleanupPromise) return;
+
+      let dropRefTimeoutId: NodeJS.Timeout | null = null;
+
+      const trackedPromise: Promise<void> = cleanupPromise
+        .catch((error) => {
           logger.warn("AgentPool: Error closing agent", {
             key,
             operation,
             error: error instanceof Error ? error.message : String(error),
           });
+        })
+        .finally(() => {
+          if (dropRefTimeoutId) clearTimeout(dropRefTimeoutId);
+          this.pendingCleanups.delete(trackedPromise);
         });
-      }
+
+      this.pendingCleanups.add(trackedPromise);
+
+      // 避免某些 dispatcher 永不 settle 导致 pendingCleanups 长期持有引用
+      dropRefTimeoutId = setTimeout(() => {
+        this.pendingCleanups.delete(trackedPromise);
+      }, 60000);
+      dropRefTimeoutId.unref();
     } catch (error) {
       logger.warn("AgentPool: Error closing agent", {
         key,

+ 21 - 3
src/lib/rate-limit/concurrent-session-limit.ts

@@ -4,7 +4,7 @@
  * - 非数字 / 非有限值 / <= 0 视为 0(无限制)
  * - > 0 时向下取整
  */
-function normalizePositiveLimit(value: unknown): number {
+export function normalizeConcurrentSessionLimit(value: number | null | undefined): number {
   if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
     return 0;
   }
@@ -12,6 +12,24 @@ function normalizePositiveLimit(value: unknown): number {
   return Math.floor(value);
 }
 
+/**
+ * 同时解析 Key/User 的并发 Session 上限(供 proxy guards 统一复用)。
+ *
+ * - `effectiveKeyLimit`:Key 的有效上限(Key>0 优先,否则回退到 User>0;都未设置则为 0)
+ * - `normalizedUserLimit`:User 上限的归一化结果(<=0 视为 0)
+ * - `enabled`:任一维度上限 >0 即为 true
+ */
+export function resolveKeyUserConcurrentSessionLimits(
+  keyLimit: number | null | undefined,
+  userLimit: number | null | undefined
+): { effectiveKeyLimit: number; normalizedUserLimit: number; enabled: boolean } {
+  const normalizedUserLimit = normalizeConcurrentSessionLimit(userLimit);
+  const effectiveKeyLimit = resolveKeyConcurrentSessionLimit(keyLimit, userLimit);
+  const enabled = effectiveKeyLimit > 0 || normalizedUserLimit > 0;
+
+  return { effectiveKeyLimit, normalizedUserLimit, enabled };
+}
+
 /**
  * 解析 Key 的“有效并发 Session 上限”。
  *
@@ -24,10 +42,10 @@ export function resolveKeyConcurrentSessionLimit(
   keyLimit: number | null | undefined,
   userLimit: number | null | undefined
 ): number {
-  const normalizedKeyLimit = normalizePositiveLimit(keyLimit);
+  const normalizedKeyLimit = normalizeConcurrentSessionLimit(keyLimit);
   if (normalizedKeyLimit > 0) {
     return normalizedKeyLimit;
   }
 
-  return normalizePositiveLimit(userLimit);
+  return normalizeConcurrentSessionLimit(userLimit);
 }

+ 100 - 0
src/lib/rate-limit/service.ts

@@ -68,6 +68,12 @@
 import { logger } from "@/lib/logger";
 import { getRedisClient } from "@/lib/redis";
 import {
+  getGlobalActiveSessionsKey,
+  getKeyActiveSessionsKey,
+  getUserActiveSessionsKey,
+} from "@/lib/redis/active-session-keys";
+import {
+  CHECK_AND_TRACK_KEY_USER_SESSION,
   CHECK_AND_TRACK_SESSION,
   GET_COST_5H_ROLLING_WINDOW,
   GET_COST_DAILY_ROLLING_WINDOW,
@@ -75,6 +81,7 @@ import {
   TRACK_COST_DAILY_ROLLING_WINDOW,
 } from "@/lib/redis/lua-scripts";
 import { SessionTracker } from "@/lib/session-tracker";
+import { ERROR_CODES } from "@/lib/utils/error-messages";
 import {
   sumKeyTotalCost,
   sumProviderTotalCost,
@@ -105,6 +112,12 @@ interface CostLimit {
   resetMode?: DailyResetMode; // 日限额重置模式(仅 daily 使用)
 }
 
+/**
+ * 限流/配额服务:统一封装 Redis + DB 的限额检查与消费追踪。
+ *
+ * 设计约束:
+ * - Redis 不可用时默认 Fail Open,避免误伤正常请求(仍会在日志中记录)。
+ */
 export class RateLimitService {
   // 使用 getter 实现懒加载,避免模块加载时立即连接 Redis(构建阶段触发)
   private static get redis() {
@@ -544,6 +557,93 @@ export class RateLimitService {
     }
   }
 
+  /**
+   * 原子性检查并追踪 Key/User 并发 Session(解决竞态条件)
+   *
+   * 与 checkSessionLimit 的区别:
+   * - checkSessionLimit:只读检查(可能被并发击穿),且无法区分“新 session”与“已存在 session”
+   * - 本方法:使用 Lua 脚本原子性完成“检查 + 追踪”,并允许已存在的 session 在达到上限时继续请求
+   *
+   * 注意:
+   * - keyLimit/userLimit 均 <=0 时表示无限制,直接放行且不追踪(由 SessionTracker.refreshSession 等路径负责观测)
+   * - Redis 不可用时 Fail Open
+   */
+  static async checkAndTrackKeyUserSession(
+    keyId: number,
+    userId: number,
+    sessionId: string,
+    keyLimit: number,
+    userLimit: number
+  ): Promise<{
+    allowed: boolean;
+    keyCount: number;
+    userCount: number;
+    trackedKey: boolean;
+    trackedUser: boolean;
+    rejectedBy?: "key" | "user";
+    reasonCode?: string;
+    reasonParams?: Record<string, string | number>;
+  }> {
+    if (keyLimit <= 0 && userLimit <= 0) {
+      return { allowed: true, keyCount: 0, userCount: 0, trackedKey: false, trackedUser: false };
+    }
+
+    if (!RateLimitService.redis || RateLimitService.redis.status !== "ready") {
+      logger.warn("[RateLimit] Redis not ready, Fail Open");
+      return { allowed: true, keyCount: 0, userCount: 0, trackedKey: false, trackedUser: false };
+    }
+
+    try {
+      const globalKey = getGlobalActiveSessionsKey();
+      const keyKey = getKeyActiveSessionsKey(keyId);
+      const userKey = getUserActiveSessionsKey(userId);
+      const now = Date.now();
+
+      const result = (await RateLimitService.redis.eval(
+        CHECK_AND_TRACK_KEY_USER_SESSION,
+        3, // KEYS count
+        globalKey, // KEYS[1]
+        keyKey, // KEYS[2]
+        userKey, // KEYS[3]
+        sessionId, // ARGV[1]
+        keyLimit.toString(), // ARGV[2]
+        userLimit.toString(), // ARGV[3]
+        now.toString(), // ARGV[4]
+        SESSION_TTL_MS.toString() // ARGV[5]
+      )) as [number, number, number, number, number, number];
+
+      const [allowed, rejectedBy, keyCount, keyTracked, userCount, userTracked] = result;
+
+      if (allowed === 0) {
+        const rejectTarget: "key" | "user" = rejectedBy === 1 ? "key" : "user";
+        const limit = rejectTarget === "key" ? keyLimit : userLimit;
+        const count = rejectTarget === "key" ? keyCount : userCount;
+
+        return {
+          allowed: false,
+          keyCount,
+          userCount,
+          trackedKey: false,
+          trackedUser: false,
+          rejectedBy: rejectTarget,
+          reasonCode: ERROR_CODES.RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED,
+          reasonParams: { current: count, limit, target: rejectTarget },
+        };
+      }
+
+      return {
+        allowed: true,
+        keyCount,
+        userCount,
+        trackedKey: keyTracked === 1,
+        trackedUser: userTracked === 1,
+      };
+    } catch (error) {
+      logger.error("[RateLimit] Key/User session check+track failed:", error);
+      return { allowed: true, keyCount: 0, userCount: 0, trackedKey: false, trackedUser: false };
+    }
+  }
+
   /**
    * 原子性检查并追踪供应商 Session(解决竞态条件)
    *

+ 29 - 0
src/lib/redis/active-session-keys.ts

@@ -0,0 +1,29 @@
+/**
+ * Active sessions 相关 Redis key 生成器。
+ *
+ * 说明:
+ * - 为兼容 Redis Cluster 下的 Lua 脚本多 key 操作,需要相关 key 共享相同 hash tag,避免 CROSSSLOT。
+ * - 目前仅对 global/key/user 三类 active_sessions key 统一加 hash tag;provider 维度不需要。
+ */
+const ACTIVE_SESSIONS_HASH_TAG = "{active_sessions}";
+
+/**
+ * 全局活跃 Session ZSET(仅用于观测 / Sessions 页面)。
+ */
+export function getGlobalActiveSessionsKey(): string {
+  return `${ACTIVE_SESSIONS_HASH_TAG}:global:active_sessions`;
+}
+
+/**
+ * Key 维度活跃 Session ZSET(用于 Key 并发上限判断)。
+ */
+export function getKeyActiveSessionsKey(keyId: number): string {
+  return `${ACTIVE_SESSIONS_HASH_TAG}:key:${keyId}:active_sessions`;
+}
+
+/**
+ * User 维度活跃 Session ZSET(用于跨多 Key 的 User 并发上限判断)。
+ */
+export function getUserActiveSessionsKey(userId: number): string {
+  return `${ACTIVE_SESSIONS_HASH_TAG}:user:${userId}:active_sessions`;
+}

+ 101 - 0
src/lib/redis/lua-scripts.ts

@@ -69,6 +69,107 @@ else
 end
 `;
 
+/**
+ * Key/User 并发:原子性检查 + 追踪(修复竞态条件)
+ *
+ * 目标:
+ * - 解决 key/user 并发检查与追踪分离导致的竞态条件(可能短时间超过用户并发上限)
+ * - 允许已存在的 session 在达到上限时继续请求(仅阻止“新 session”进入)
+ *
+ * 注意:
+ * - global 仅用于观测(Sessions 页面),不参与并发判断;但当启用并发上限时,SessionGuard 会跳过
+ *   SessionTracker.trackSession,因此此脚本也负责更新 global,保证 Sessions 页面可见性。
+ * - key/user 使用 ZSET 分别追踪活跃 sessionId(score=timestamp)
+ *
+ * Redis Cluster 注意:
+ * - 该脚本同时操作多个 key,因此 KEYS[1..3] 必须共享相同 hash tag(例如 {active_sessions}),否则会触发 CROSSSLOT。
+ *
+ * KEYS[1]: {active_sessions}:global:active_sessions
+ * KEYS[2]: {active_sessions}:key:${keyId}:active_sessions
+ * KEYS[3]: {active_sessions}:user:${userId}:active_sessions
+ * ARGV[1]: sessionId
+ * ARGV[2]: keyLimit
+ * ARGV[3]: userLimit
+ * ARGV[4]: now(毫秒时间戳)
+ * ARGV[5]: ttlMs(可选,清理窗口,默认 300000ms)
+ *
+ * Return: {allowed, rejectedBy, keyCount, keyTracked, userCount, userTracked}
+ * - allowed=1: 放行
+ * - allowed=0: 拒绝(rejectedBy=1 表示 Key 超限,=2 表示 User 超限)
+ * - key/user Count:返回“最终计数”(若未追踪则为当前计数)
+ * - key/user Tracked:1 表示本次为新追踪,0 表示已存在
+ */
+export const CHECK_AND_TRACK_KEY_USER_SESSION = `
+local global_key = KEYS[1]
+local key_key = KEYS[2]
+local user_key = KEYS[3]
+
+local session_id = ARGV[1]
+local key_limit = tonumber(ARGV[2])
+local user_limit = tonumber(ARGV[3])
+local now = tonumber(ARGV[4])
+local ttl = tonumber(ARGV[5]) or 300000
+
+-- Guard against invalid TTL (prevents clearing all sessions)
+if ttl <= 0 then
+  ttl = 300000
+end
+
+-- 1. Cleanup expired sessions (TTL window ago)
+local cutoff = now - ttl
+redis.call('ZREMRANGEBYSCORE', global_key, '-inf', cutoff)
+redis.call('ZREMRANGEBYSCORE', key_key, '-inf', cutoff)
+redis.call('ZREMRANGEBYSCORE', user_key, '-inf', cutoff)
+
+-- 2. Check if session is already tracked
+local is_tracked_key = redis.call('ZSCORE', key_key, session_id)
+local is_tracked_user = redis.call('ZSCORE', user_key, session_id)
+
+-- 3. Get current concurrency counts
+local current_key_count = redis.call('ZCARD', key_key)
+local current_user_count = redis.call('ZCARD', user_key)
+
+-- 4. Check Key limit (exclude already tracked session)
+if key_limit > 0 and not is_tracked_key and current_key_count >= key_limit then
+  return {0, 1, current_key_count, 0, current_user_count, 0}
+end
+
+-- 5. Check User limit (exclude already tracked session)
+-- 说明:User 上限以 user ZSET 为准;key ZSET 不参与 user 维度的“已追踪”判定,避免绕过 user 并发限制。
+if user_limit > 0 and not is_tracked_user and current_user_count >= user_limit then
+  return {0, 2, current_key_count, 0, current_user_count, 0}
+end
+
+-- 6. Track session (ZADD updates timestamp for existing members)
+redis.call('ZADD', global_key, now, session_id)
+redis.call('ZADD', key_key, now, session_id)
+redis.call('ZADD', user_key, now, session_id)
+
+-- 7. Set TTL based on session TTL (at least 1h to cover active sessions)
+local ttl_seconds = math.floor(ttl / 1000)
+local expire_ttl = math.max(3600, ttl_seconds)
+redis.call('EXPIRE', global_key, expire_ttl)
+redis.call('EXPIRE', key_key, expire_ttl)
+redis.call('EXPIRE', user_key, expire_ttl)
+
+-- 8. Return success (compute counts)
+local key_count = current_key_count
+local key_tracked = 0
+if not is_tracked_key then
+  key_count = key_count + 1
+  key_tracked = 1
+end
+
+local user_count = current_user_count
+local user_tracked = 0
+if not is_tracked_user then
+  user_count = user_count + 1
+  user_tracked = 1
+end
+
+return {1, 0, key_count, key_tracked, user_count, user_tracked}
+`;
+
 /**
  * 批量检查多个供应商的并发限制
  *

+ 28 - 3
src/lib/session-manager.ts

@@ -19,8 +19,16 @@ import type {
 } from "@/types/session";
 import type { SpecialSetting } from "@/types/special-settings";
 import { getRedisClient } from "./redis";
+import {
+  getGlobalActiveSessionsKey,
+  getKeyActiveSessionsKey,
+  getUserActiveSessionsKey,
+} from "./redis/active-session-keys";
 import { SessionTracker } from "./session-tracker";
 
+/**
+ * 将已脱敏的 header 文本解析为可序列化对象(用于写入 Session 元信息)。
+ */
 function headersToSanitizedObject(headers: Headers): Record<string, string> {
   const sanitizedText = sanitizeHeaders(headers);
   if (!sanitizedText || sanitizedText === "(empty)") {
@@ -46,6 +54,12 @@ function headersToSanitizedObject(headers: Headers): Record<string, string> {
   return obj;
 }
 
+/**
+ * 解析存储在 Redis 中的 header JSON 字符串。
+ *
+ * - 成功返回 `{ [name]: value }`
+ * - 解析失败/结构不合法则返回 null
+ */
 function parseHeaderRecord(value: string): Record<string, string> | null {
   try {
     const parsed: unknown = JSON.parse(value);
@@ -1930,15 +1944,22 @@ export class SessionManager {
       // 1. 先查询绑定信息(用于从 ZSET 中移除)
       let providerId: number | null = null;
       let keyId: number | null = null;
+      let userId: number | null = null;
 
       try {
-        const [providerIdStr, keyIdStr] = await Promise.all([
+        const [providerIdStr, keyIdStr, userIdStr] = await Promise.all([
           redis.get(`session:${sessionId}:provider`),
           redis.get(`session:${sessionId}:key`),
+          redis.hget(`session:${sessionId}:info`, "userId"),
         ]);
 
         providerId = providerIdStr ? parseInt(providerIdStr, 10) : null;
         keyId = keyIdStr ? parseInt(keyIdStr, 10) : null;
+        userId = userIdStr ? parseInt(userIdStr, 10) : null;
+
+        if (!Number.isFinite(userId)) {
+          userId = null;
+        }
       } catch (lookupError) {
         // Redis 查询失败不应阻止清理操作,继续执行删除
         logger.warn(
@@ -1965,14 +1986,18 @@ export class SessionManager {
       pipeline.del(`session:${sessionId}:response`);
 
       // 3. 从 ZSET 中移除(始终尝试,即使查询失败)
-      pipeline.zrem("global:active_sessions", sessionId);
+      pipeline.zrem(getGlobalActiveSessionsKey(), sessionId);
 
       if (providerId) {
         pipeline.zrem(`provider:${providerId}:active_sessions`, sessionId);
       }
 
       if (keyId) {
-        pipeline.zrem(`key:${keyId}:active_sessions`, sessionId);
+        pipeline.zrem(getKeyActiveSessionsKey(keyId), sessionId);
+      }
+
+      if (userId) {
+        pipeline.zrem(getUserActiveSessionsKey(userId), sessionId);
       }
 
       // 4. 删除 hash 映射(如果存在)

+ 30 - 19
src/lib/session-tracker.ts

@@ -1,4 +1,9 @@
 import { logger } from "@/lib/logger";
+import {
+  getGlobalActiveSessionsKey,
+  getKeyActiveSessionsKey,
+  getUserActiveSessionsKey,
+} from "@/lib/redis/active-session-keys";
 import { getRedisClient } from "./redis";
 
 /**
@@ -11,10 +16,10 @@ import { getRedisClient } from "./redis";
  * 4. 兼容旧格式(Set)实现零停机迁移
  *
  * 数据结构:
- * - global:active_sessions (ZSET): score = timestamp, member = sessionId
- * - key:${keyId}:active_sessions (ZSET): 同上
+ * - {active_sessions}:global:active_sessions (ZSET): score = timestamp, member = sessionId
+ * - {active_sessions}:key:${keyId}:active_sessions (ZSET): 同上
  * - provider:${providerId}:active_sessions (ZSET): 同上
- * - user:${userId}:active_sessions (ZSET): 同上
+ * - {active_sessions}:user:${userId}:active_sessions (ZSET): 同上
  */
 export class SessionTracker {
   private static readonly SESSION_TTL_SECONDS = (() => {
@@ -27,7 +32,7 @@ export class SessionTracker {
   /**
    * 初始化 SessionTracker,自动清理旧格式数据
    *
-   * 应在应用启动时调用一次,清理 global:active_sessions 的旧 Set 数据。
+   * 应在应用启动时调用一次,清理 {active_sessions}:global:active_sessions 的旧 Set 数据。
    * 其他 key(provider:*、key:*)在运行时自动清理。
    */
   static async initialize(): Promise<void> {
@@ -38,7 +43,7 @@ export class SessionTracker {
     }
 
     try {
-      const key = "global:active_sessions";
+      const key = getGlobalActiveSessionsKey();
       const exists = await redis.exists(key);
 
       if (exists === 1) {
@@ -75,18 +80,21 @@ export class SessionTracker {
     try {
       const now = Date.now();
       const pipeline = redis.pipeline();
+      const globalKey = getGlobalActiveSessionsKey();
+      const keyZSetKey = getKeyActiveSessionsKey(keyId);
 
       // 添加到全局集合(ZSET)
-      pipeline.zadd("global:active_sessions", now, sessionId);
-      pipeline.expire("global:active_sessions", 3600); // 1 小时兜底 TTL
+      pipeline.zadd(globalKey, now, sessionId);
+      pipeline.expire(globalKey, 3600); // 1 小时兜底 TTL
 
       // 添加到 key 级集合(ZSET)
-      pipeline.zadd(`key:${keyId}:active_sessions`, now, sessionId);
-      pipeline.expire(`key:${keyId}:active_sessions`, 3600);
+      pipeline.zadd(keyZSetKey, now, sessionId);
+      pipeline.expire(keyZSetKey, 3600);
 
       if (userId !== undefined) {
-        pipeline.zadd(`user:${userId}:active_sessions`, now, sessionId);
-        pipeline.expire(`user:${userId}:active_sessions`, 3600);
+        const userZSetKey = getUserActiveSessionsKey(userId);
+        pipeline.zadd(userZSetKey, now, sessionId);
+        pipeline.expire(userZSetKey, 3600);
       }
 
       const results = await pipeline.exec();
@@ -127,9 +135,10 @@ export class SessionTracker {
     try {
       const now = Date.now();
       const pipeline = redis.pipeline();
+      const globalKey = getGlobalActiveSessionsKey();
 
       // 更新全局集合时间戳
-      pipeline.zadd("global:active_sessions", now, sessionId);
+      pipeline.zadd(globalKey, now, sessionId);
 
       // 添加到 provider 级集合(ZSET)
       pipeline.zadd(`provider:${providerId}:active_sessions`, now, sessionId);
@@ -181,14 +190,16 @@ export class SessionTracker {
       const pipeline = redis.pipeline();
       const ttlSeconds = SessionTracker.SESSION_TTL_SECONDS;
       const providerZSetKey = `provider:${providerId}:active_sessions`;
+      const globalKey = getGlobalActiveSessionsKey();
+      const keyZSetKey = getKeyActiveSessionsKey(keyId);
 
-      pipeline.zadd("global:active_sessions", now, sessionId);
-      pipeline.zadd(`key:${keyId}:active_sessions`, now, sessionId);
+      pipeline.zadd(globalKey, now, sessionId);
+      pipeline.zadd(keyZSetKey, now, sessionId);
       pipeline.zadd(providerZSetKey, now, sessionId);
       // Use dynamic TTL based on session TTL (at least 1h to cover active sessions)
       pipeline.expire(providerZSetKey, Math.max(3600, ttlSeconds));
       if (userId !== undefined) {
-        pipeline.zadd(`user:${userId}:active_sessions`, now, sessionId);
+        pipeline.zadd(getUserActiveSessionsKey(userId), now, sessionId);
       }
 
       pipeline.expire(`session:${sessionId}:provider`, ttlSeconds);
@@ -232,7 +243,7 @@ export class SessionTracker {
     if (!redis || redis.status !== "ready") return 0;
 
     try {
-      const key = "global:active_sessions";
+      const key = getGlobalActiveSessionsKey();
       const exists = await redis.exists(key);
 
       if (exists === 1) {
@@ -265,7 +276,7 @@ export class SessionTracker {
     if (!redis || redis.status !== "ready") return 0;
 
     try {
-      const key = `key:${keyId}:active_sessions`;
+      const key = getKeyActiveSessionsKey(keyId);
       const exists = await redis.exists(key);
 
       if (exists === 1) {
@@ -331,7 +342,7 @@ export class SessionTracker {
     if (!redis || redis.status !== "ready") return 0;
 
     try {
-      const key = `user:${userId}:active_sessions`;
+      const key = getUserActiveSessionsKey(userId);
       const exists = await redis.exists(key);
 
       if (exists === 1) {
@@ -472,7 +483,7 @@ export class SessionTracker {
     if (!redis || redis.status !== "ready") return [];
 
     try {
-      const key = "global:active_sessions";
+      const key = getGlobalActiveSessionsKey();
       const exists = await redis.exists(key);
 
       if (exists === 1) {

+ 2 - 0
src/lib/utils/special-settings.ts

@@ -97,6 +97,8 @@ function buildSettingKey(setting: SpecialSetting): string {
         setting.after.maxTokens,
         setting.after.thinkingBudgetTokens,
       ]);
+    case "billing_header_rectifier":
+      return JSON.stringify([setting.type, setting.hit, setting.removedCount]);
     case "gemini_google_search_override":
       return JSON.stringify([
         setting.type,

+ 2 - 0
src/lib/validation/schemas.ts

@@ -864,6 +864,8 @@ export const UpdateSystemSettingsSchema = z.object({
   enableThinkingSignatureRectifier: z.boolean().optional(),
   // thinking budget 整流器(可选)
   enableThinkingBudgetRectifier: z.boolean().optional(),
+  // billing header 整流器(可选)
+  enableBillingHeaderRectifier: z.boolean().optional(),
   // Codex Session ID 补全(可选)
   enableCodexSessionIdCompletion: z.boolean().optional(),
   // Claude metadata.user_id 注入(可选)

+ 1 - 0
src/repository/_shared/transformers.ts

@@ -194,6 +194,7 @@ export function toSystemSettings(dbSettings: any): SystemSettings {
     interceptAnthropicWarmupRequests: dbSettings?.interceptAnthropicWarmupRequests ?? false,
     enableThinkingSignatureRectifier: dbSettings?.enableThinkingSignatureRectifier ?? true,
     enableThinkingBudgetRectifier: dbSettings?.enableThinkingBudgetRectifier ?? true,
+    enableBillingHeaderRectifier: dbSettings?.enableBillingHeaderRectifier ?? true,
     enableCodexSessionIdCompletion: dbSettings?.enableCodexSessionIdCompletion ?? true,
     enableClaudeMetadataUserIdInjection: dbSettings?.enableClaudeMetadataUserIdInjection ?? true,
     enableResponseFixer: dbSettings?.enableResponseFixer ?? true,

+ 94 - 33
src/repository/statistics.ts

@@ -1,6 +1,6 @@
 "use server";
 
-import { and, eq, gte, isNull, lt, sql } from "drizzle-orm";
+import { and, eq, gte, inArray, isNull, lt, sql } from "drizzle-orm";
 import { db } from "@/drizzle/db";
 import { keys, messageRequest } from "@/drizzle/schema";
 import { resolveSystemTimezone } from "@/lib/utils/timezone";
@@ -760,65 +760,126 @@ export async function sumKeyTotalCostById(
 }
 
 /**
- * 查询 Key 历史总消费(带时间边界优化)
- * 用于总消费限额检查
- * @param keyHash - API Key 的哈希值
- * @param maxAgeDays - 最大查询天数,默认 365 天(避免全表扫描)
+ * Query Key total cost (with optional time boundary)
+ * @param keyHash - API Key hash
+ * @param maxAgeDays - Max query days (default 365). Use Infinity for all-time.
  */
 export async function sumKeyTotalCost(keyHash: string, maxAgeDays: number = 365): Promise<number> {
-  // Validate maxAgeDays - use default 365 for invalid values
-  const validMaxAgeDays =
-    Number.isFinite(maxAgeDays) && maxAgeDays > 0 ? Math.floor(maxAgeDays) : 365;
+  const conditions = [
+    eq(messageRequest.key, keyHash),
+    isNull(messageRequest.deletedAt),
+    EXCLUDE_WARMUP_CONDITION,
+  ];
 
-  const cutoffDate = new Date();
-  cutoffDate.setDate(cutoffDate.getDate() - validMaxAgeDays);
+  // Finite positive maxAgeDays adds a date filter; Infinity/0/negative means all-time
+  if (Number.isFinite(maxAgeDays) && maxAgeDays > 0) {
+    const cutoffDate = new Date(Date.now() - Math.floor(maxAgeDays) * 24 * 60 * 60 * 1000);
+    conditions.push(gte(messageRequest.createdAt, cutoffDate));
+  }
 
   const result = await db
     .select({ total: sql<number>`COALESCE(SUM(${messageRequest.costUsd}), 0)` })
     .from(messageRequest)
-    .where(
-      and(
-        eq(messageRequest.key, keyHash),
-        isNull(messageRequest.deletedAt),
-        EXCLUDE_WARMUP_CONDITION,
-        gte(messageRequest.createdAt, cutoffDate)
-      )
-    );
+    .where(and(...conditions));
 
   return Number(result[0]?.total || 0);
 }
 
 /**
- * 查询用户历史总消费(所有 Key 累计,带时间边界优化)
- * 用于总消费限额检查
- * @param userId - 用户 ID
- * @param maxAgeDays - 最大查询天数,默认 365 天(避免全表扫描)
+ * Query user total cost across all keys (with optional time boundary)
+ * @param userId - User ID
+ * @param maxAgeDays - Max query days (default 365). Use Infinity for all-time.
  */
 export async function sumUserTotalCost(userId: number, maxAgeDays: number = 365): Promise<number> {
-  // Validate maxAgeDays - use default 365 for invalid values
-  const validMaxAgeDays =
-    Number.isFinite(maxAgeDays) && maxAgeDays > 0 ? Math.floor(maxAgeDays) : 365;
+  const conditions = [
+    eq(messageRequest.userId, userId),
+    isNull(messageRequest.deletedAt),
+    EXCLUDE_WARMUP_CONDITION,
+  ];
 
-  const cutoffDate = new Date();
-  cutoffDate.setDate(cutoffDate.getDate() - validMaxAgeDays);
+  // Finite positive maxAgeDays adds a date filter; Infinity/0/negative means all-time
+  if (Number.isFinite(maxAgeDays) && maxAgeDays > 0) {
+    const cutoffDate = new Date(Date.now() - Math.floor(maxAgeDays) * 24 * 60 * 60 * 1000);
+    conditions.push(gte(messageRequest.createdAt, cutoffDate));
+  }
 
   const result = await db
     .select({ total: sql<number>`COALESCE(SUM(${messageRequest.costUsd}), 0)` })
     .from(messageRequest)
+    .where(and(...conditions));
+
+  return Number(result[0]?.total || 0);
+}
+
+/**
+ * Batch query: all-time total cost grouped by user_id (single SQL query)
+ * @param userIds - Array of user IDs
+ * @returns Map of userId -> totalCost
+ */
+export async function sumUserTotalCostBatch(userIds: number[]): Promise<Map<number, number>> {
+  const result = new Map<number, number>();
+  if (userIds.length === 0) return result;
+
+  const rows = await db
+    .select({
+      userId: messageRequest.userId,
+      total: sql<number>`COALESCE(SUM(${messageRequest.costUsd}), 0)`,
+    })
+    .from(messageRequest)
     .where(
       and(
-        eq(messageRequest.userId, userId),
+        inArray(messageRequest.userId, userIds),
         isNull(messageRequest.deletedAt),
-        EXCLUDE_WARMUP_CONDITION,
-        gte(messageRequest.createdAt, cutoffDate)
+        EXCLUDE_WARMUP_CONDITION
       )
-    );
+    )
+    .groupBy(messageRequest.userId);
 
-  return Number(result[0]?.total || 0);
+  for (const id of userIds) {
+    result.set(id, 0);
+  }
+  for (const row of rows) {
+    result.set(row.userId, Number(row.total || 0));
+  }
+  return result;
+}
+
+/**
+ * Batch query: all-time total cost grouped by key_id (single SQL query via JOIN)
+ * @param keyIds - Array of key IDs
+ * @returns Map of keyId -> totalCost
+ */
+export async function sumKeyTotalCostBatchByIds(keyIds: number[]): Promise<Map<number, number>> {
+  const result = new Map<number, number>();
+  if (keyIds.length === 0) return result;
+
+  const rows = await db
+    .select({
+      keyId: keys.id,
+      total: sql<number>`COALESCE(SUM(${messageRequest.costUsd}), 0)`,
+    })
+    .from(keys)
+    .leftJoin(
+      messageRequest,
+      and(
+        eq(messageRequest.key, keys.key),
+        isNull(messageRequest.deletedAt),
+        EXCLUDE_WARMUP_CONDITION
+      )
+    )
+    .where(inArray(keys.id, keyIds))
+    .groupBy(keys.id);
+
+  for (const id of keyIds) {
+    result.set(id, 0);
+  }
+  for (const row of rows) {
+    result.set(row.keyId, Number(row.total || 0));
+  }
+  return result;
 }
 
 /**
- * 查询供应商历史总消费
  * 用于供应商总消费限额检查(limit_total_usd)。
  *
  * 重要语义:

+ 7 - 0
src/repository/system-config.ts

@@ -151,6 +151,7 @@ function createFallbackSettings(): SystemSettings {
     interceptAnthropicWarmupRequests: false,
     enableThinkingSignatureRectifier: true,
     enableThinkingBudgetRectifier: true,
+    enableBillingHeaderRectifier: true,
     enableCodexSessionIdCompletion: true,
     enableClaudeMetadataUserIdInjection: true,
     enableResponseFixer: true,
@@ -194,6 +195,7 @@ export async function getSystemSettings(): Promise<SystemSettings> {
       interceptAnthropicWarmupRequests: systemSettings.interceptAnthropicWarmupRequests,
       enableThinkingSignatureRectifier: systemSettings.enableThinkingSignatureRectifier,
       enableThinkingBudgetRectifier: systemSettings.enableThinkingBudgetRectifier,
+      enableBillingHeaderRectifier: systemSettings.enableBillingHeaderRectifier,
       enableCodexSessionIdCompletion: systemSettings.enableCodexSessionIdCompletion,
       enableClaudeMetadataUserIdInjection: systemSettings.enableClaudeMetadataUserIdInjection,
       enableResponseFixer: systemSettings.enableResponseFixer,
@@ -349,6 +351,11 @@ export async function updateSystemSettings(
       updates.enableThinkingBudgetRectifier = payload.enableThinkingBudgetRectifier;
     }
 
+    // billing header 整流器开关(如果提供)
+    if (payload.enableBillingHeaderRectifier !== undefined) {
+      updates.enableBillingHeaderRectifier = payload.enableBillingHeaderRectifier;
+    }
+
     // Codex Session ID 补全开关(如果提供)
     if (payload.enableCodexSessionIdCompletion !== undefined) {
       updates.enableCodexSessionIdCompletion = payload.enableCodexSessionIdCompletion;

+ 9 - 0
src/types/special-settings.ts

@@ -11,6 +11,7 @@ export type SpecialSetting =
   | GuardInterceptSpecialSetting
   | ThinkingSignatureRectifierSpecialSetting
   | ThinkingBudgetRectifierSpecialSetting
+  | BillingHeaderRectifierSpecialSetting
   | CodexSessionIdCompletionSpecialSetting
   | ClaudeMetadataUserIdInjectionSpecialSetting
   | AnthropicCacheTtlHeaderOverrideSpecialSetting
@@ -152,6 +153,14 @@ export type ClaudeMetadataUserIdInjectionSpecialSetting = {
   sessionId: string | null;
 };
 
+export type BillingHeaderRectifierSpecialSetting = {
+  type: "billing_header_rectifier";
+  scope: "request";
+  hit: boolean;
+  removedCount: number;
+  extractedValues: string[];
+};
+
 export type ThinkingBudgetRectifierSpecialSetting = {
   type: "thinking_budget_rectifier";
   scope: "request";

+ 8 - 0
src/types/system-config.ts

@@ -53,6 +53,11 @@ export interface SystemSettings {
   // 目标:当 Anthropic 类型供应商出现 budget_tokens < 1024 错误时,自动整流并重试一次
   enableThinkingBudgetRectifier: boolean;
 
+  // billing header 整流器(默认开启)
+  // 目标:主动移除 Claude Code 客户端注入到 system 提示中的 x-anthropic-billing-header 文本块,
+  // 防止 Amazon Bedrock 等非原生 Anthropic 上游返回 400 错误
+  enableBillingHeaderRectifier: boolean;
+
   // Codex Session ID 补全(默认开启)
   // 目标:当 Codex 请求缺少 session_id / prompt_cache_key 时,自动补全或生成稳定的会话标识
   enableCodexSessionIdCompletion: boolean;
@@ -115,6 +120,9 @@ export interface UpdateSystemSettingsInput {
   // thinking budget 整流器(可选)
   enableThinkingBudgetRectifier?: boolean;
 
+  // billing header 整流器(可选)
+  enableBillingHeaderRectifier?: boolean;
+
   // Codex Session ID 补全(可选)
   enableCodexSessionIdCompletion?: boolean;
 

+ 6 - 0
tests/unit/actions/system-config-save.test.ts

@@ -65,7 +65,10 @@ describe("saveSystemSettings", () => {
       enableHttp2: false,
       interceptAnthropicWarmupRequests: false,
       enableThinkingSignatureRectifier: false,
+      enableThinkingBudgetRectifier: false,
+      enableBillingHeaderRectifier: true,
       enableCodexSessionIdCompletion: false,
+      enableClaudeMetadataUserIdInjection: false,
       enableResponseFixer: false,
       responseFixerConfig: {
         fixEncoding: false,
@@ -186,7 +189,10 @@ describe("saveSystemSettings", () => {
       enableHttp2: true,
       interceptAnthropicWarmupRequests: false,
       enableThinkingSignatureRectifier: false,
+      enableThinkingBudgetRectifier: false,
+      enableBillingHeaderRectifier: true,
       enableCodexSessionIdCompletion: false,
+      enableClaudeMetadataUserIdInjection: false,
       enableResponseFixer: false,
       responseFixerConfig: {
         fixEncoding: false,

+ 17 - 30
tests/unit/actions/total-usage-semantics.test.ts

@@ -1,12 +1,12 @@
 /**
  * total-usage-semantics tests
  *
- * Verify that total usage reads in display paths use ALL_TIME_MAX_AGE_DAYS (36500)
- * instead of the default 365 days.
+ * Verify that total usage reads in display paths use ALL_TIME_MAX_AGE_DAYS (Infinity)
+ * to skip the date filter entirely, querying all-time data.
  *
  * Key insight: The functions sumKeyTotalCostById and sumUserTotalCost have a default
  * maxAgeDays of 365. For display purposes (showing "total" usage), we want all-time
- * semantics, which means passing 36500 days (~100 years).
+ * semantics, which means passing Infinity to skip the date filter.
  *
  * IMPORTANT: This test only covers DISPLAY paths. Enforcement paths (RateLimitService)
  * are intentionally NOT modified.
@@ -14,8 +14,8 @@
 
 import { beforeEach, describe, expect, it, vi } from "vitest";
 
-// All-time max age constant (100 years in days)
-const ALL_TIME_MAX_AGE_DAYS = 36500;
+// All-time max age constant - Infinity means no date filter
+const ALL_TIME_MAX_AGE_DAYS = Infinity;
 
 // Mock functions
 const getSessionMock = vi.fn();
@@ -131,8 +131,8 @@ describe("total-usage-semantics", () => {
       const { getMyQuota } = await import("@/actions/my-usage");
       await getMyQuota();
 
-      // Verify sumKeyTotalCostById was called with ALL_TIME_MAX_AGE_DAYS
-      expect(sumKeyTotalCostByIdMock).toHaveBeenCalledWith(1, ALL_TIME_MAX_AGE_DAYS);
+      // Verify sumKeyTotalCostById was called with Infinity (all-time)
+      expect(sumKeyTotalCostByIdMock).toHaveBeenCalledWith(1, Infinity);
     });
 
     it.skip("should call sumUserTotalCost with ALL_TIME_MAX_AGE_DAYS for user total cost (via sumUserCost)", async () => {
@@ -182,10 +182,8 @@ describe("total-usage-semantics", () => {
       const { getMyQuota } = await import("@/actions/my-usage");
       await getMyQuota();
 
-      // Verify sumUserTotalCost was called with ALL_TIME_MAX_AGE_DAYS
-      // Note: getMyQuota calls sumUserCost(user.id, "total") which internally calls sumUserTotalCost
-      // The dynamic import in sumUserCost should use our mocked module
-      expect(sumUserTotalCostMock).toHaveBeenCalledWith(1, ALL_TIME_MAX_AGE_DAYS);
+      // Verify sumUserTotalCost was called with Infinity (all-time)
+      expect(sumUserTotalCostMock).toHaveBeenCalledWith(1, Infinity);
     });
   });
 
@@ -216,36 +214,27 @@ describe("total-usage-semantics", () => {
       const { getUserAllLimitUsage } = await import("@/actions/users");
       await getUserAllLimitUsage(1);
 
-      // Verify sumUserTotalCost was called with ALL_TIME_MAX_AGE_DAYS
-      expect(sumUserTotalCostMock).toHaveBeenCalledWith(1, ALL_TIME_MAX_AGE_DAYS);
+      // Verify sumUserTotalCost was called with Infinity (all-time)
+      expect(sumUserTotalCostMock).toHaveBeenCalledWith(1, Infinity);
     });
   });
 
   describe("ALL_TIME_MAX_AGE_DAYS constant value", () => {
-    it("should be 36500 days (~100 years)", () => {
-      // This ensures the constant is correctly defined as 100 years
-      expect(ALL_TIME_MAX_AGE_DAYS).toBe(36500);
-
-      // Verify it represents approximately 100 years
-      const yearsApprox = ALL_TIME_MAX_AGE_DAYS / 365;
-      expect(yearsApprox).toBe(100);
+    it("should be Infinity for all-time semantics", () => {
+      expect(ALL_TIME_MAX_AGE_DAYS).toBe(Infinity);
     });
   });
 
   describe("source code verification", () => {
     it("should verify sumUserCost passes ALL_TIME_MAX_AGE_DAYS when period is total", async () => {
-      // This test verifies the implementation by reading the source code pattern
-      // The sumUserCost function should call sumUserTotalCost(userId, ALL_TIME_MAX_AGE_DAYS)
-      // when period === "total"
-
       const fs = await import("node:fs/promises");
       const path = await import("node:path");
 
       const myUsagePath = path.join(process.cwd(), "src/actions/my-usage.ts");
       const content = await fs.readFile(myUsagePath, "utf-8");
 
-      // Verify the constant is defined
-      expect(content).toContain("const ALL_TIME_MAX_AGE_DAYS = 36500");
+      // Verify the constant is defined as Infinity
+      expect(content).toContain("const ALL_TIME_MAX_AGE_DAYS = Infinity");
 
       // Verify sumUserTotalCost is called with the constant when period is total
       expect(content).toContain("sumUserTotalCost(userId, ALL_TIME_MAX_AGE_DAYS)");
@@ -255,16 +244,14 @@ describe("total-usage-semantics", () => {
     });
 
     it("should verify getUserAllLimitUsage passes ALL_TIME_MAX_AGE_DAYS", async () => {
-      // This test verifies the implementation by reading the source code pattern
-
       const fs = await import("node:fs/promises");
       const path = await import("node:path");
 
       const usersPath = path.join(process.cwd(), "src/actions/users.ts");
       const content = await fs.readFile(usersPath, "utf-8");
 
-      // Verify the constant is defined in getUserAllLimitUsage
-      expect(content).toContain("const ALL_TIME_MAX_AGE_DAYS = 36500");
+      // Verify the constant is defined as Infinity
+      expect(content).toContain("const ALL_TIME_MAX_AGE_DAYS = Infinity");
 
       // Verify sumUserTotalCost is called with the constant
       expect(content).toContain("sumUserTotalCost(userId, ALL_TIME_MAX_AGE_DAYS)");

+ 3 - 0
tests/unit/lib/config/system-settings-cache.test.ts

@@ -40,7 +40,10 @@ function createSettings(overrides: Partial<SystemSettings> = {}): SystemSettings
     enableHttp2: false,
     interceptAnthropicWarmupRequests: false,
     enableThinkingSignatureRectifier: true,
+    enableThinkingBudgetRectifier: true,
+    enableBillingHeaderRectifier: true,
     enableCodexSessionIdCompletion: true,
+    enableClaudeMetadataUserIdInjection: true,
     enableResponseFixer: true,
     responseFixerConfig: {
       fixTruncatedJson: true,

+ 8 - 0
tests/unit/lib/endpoint-circuit-breaker.test.ts

@@ -170,6 +170,10 @@ describe("endpoint-circuit-breaker", () => {
     await flushPromises();
     sendAlertMock.mockClear();
 
+    // Prime module cache for dynamic import() consumers
+    await import("@/lib/config/env.schema");
+    await import("@/lib/notification/notifier");
+
     const { triggerEndpointCircuitBreakerAlert } = await import("@/lib/endpoint-circuit-breaker");
 
     await triggerEndpointCircuitBreakerAlert(
@@ -227,6 +231,10 @@ describe("endpoint-circuit-breaker", () => {
     await flushPromises();
     sendAlertMock.mockClear();
 
+    // Prime module cache for dynamic import() consumers
+    await import("@/lib/config/env.schema");
+    await import("@/lib/notification/notifier");
+
     const { triggerEndpointCircuitBreakerAlert } = await import("@/lib/endpoint-circuit-breaker");
 
     await triggerEndpointCircuitBreakerAlert(10, 3, "2026-01-01T00:05:00.000Z", "timeout");

+ 8 - 0
tests/unit/lib/proxy-agent/agent-pool.test.ts

@@ -514,6 +514,14 @@ describe("AgentPool", () => {
         destroy?: () => Promise<void>;
       };
 
+      // 说明:本文件顶部已 mock undici Agent/ProxyAgent,因此 destroy/close 应为 vi.fn,断言才有意义
+      if (typeof agent.destroy === "function") {
+        expect(vi.isMockFunction(agent.destroy)).toBe(true);
+      }
+      if (typeof agent.close === "function") {
+        expect(vi.isMockFunction(agent.close)).toBe(true);
+      }
+
       // 模拟:close 可能因等待 in-flight 请求结束而长期不返回
       if (typeof agent.close === "function") {
         vi.mocked(agent.close).mockImplementation(() => new Promise<void>(() => {}));

+ 42 - 1
tests/unit/lib/rate-limit/concurrent-session-limit.test.ts

@@ -1,5 +1,9 @@
 import { describe, expect, it } from "vitest";
-import { resolveKeyConcurrentSessionLimit } from "@/lib/rate-limit/concurrent-session-limit";
+import {
+  normalizeConcurrentSessionLimit,
+  resolveKeyConcurrentSessionLimit,
+  resolveKeyUserConcurrentSessionLimits,
+} from "@/lib/rate-limit/concurrent-session-limit";
 
 describe("resolveKeyConcurrentSessionLimit", () => {
   const cases: Array<{
@@ -50,3 +54,40 @@ describe("resolveKeyConcurrentSessionLimit", () => {
     });
   }
 });
+
+describe("normalizeConcurrentSessionLimit", () => {
+  const cases: Array<{ title: string; input: number | null | undefined; expected: number }> = [
+    { title: "null 应归一化为 0", input: null, expected: 0 },
+    { title: "undefined 应归一化为 0", input: undefined, expected: 0 },
+    { title: "0 应归一化为 0", input: 0, expected: 0 },
+    { title: "负数应归一化为 0", input: -1, expected: 0 },
+    { title: "NaN 应归一化为 0", input: Number.NaN, expected: 0 },
+    { title: "Infinity 应归一化为 0", input: Number.POSITIVE_INFINITY, expected: 0 },
+    { title: "正整数应保持不变", input: 15, expected: 15 },
+    { title: "小数应向下取整", input: 7.9, expected: 7 },
+    { title: "小数 < 1 应向下取整为 0", input: 0.9, expected: 0 },
+  ];
+
+  for (const testCase of cases) {
+    it(testCase.title, () => {
+      expect(normalizeConcurrentSessionLimit(testCase.input)).toBe(testCase.expected);
+    });
+  }
+});
+
+describe("resolveKeyUserConcurrentSessionLimits", () => {
+  it("Key 未设置且 User 已设置时:effectiveKeyLimit 应继承 User,且 enabled=true", () => {
+    const result = resolveKeyUserConcurrentSessionLimits(0, 15);
+    expect(result).toEqual({ effectiveKeyLimit: 15, normalizedUserLimit: 15, enabled: true });
+  });
+
+  it("Key 已设置且 User 已设置时:Key 优先,User 保留为 normalizedUserLimit", () => {
+    const result = resolveKeyUserConcurrentSessionLimits(10, 15);
+    expect(result).toEqual({ effectiveKeyLimit: 10, normalizedUserLimit: 15, enabled: true });
+  });
+
+  it("Key/User 均未设置时:enabled=false", () => {
+    const result = resolveKeyUserConcurrentSessionLimits(0, null);
+    expect(result).toEqual({ effectiveKeyLimit: 0, normalizedUserLimit: 0, enabled: false });
+  });
+});

+ 81 - 0
tests/unit/lib/rate-limit/service-extra.test.ts

@@ -190,6 +190,87 @@ describe("RateLimitService - other quota paths", () => {
     expect(ttlMsArg).toBe("300000");
   });
 
+  it("checkAndTrackKeyUserSession:keyLimit/userLimit 均 <=0 时应放行且不追踪", async () => {
+    const { RateLimitService } = await import("@/lib/rate-limit");
+
+    const result = await RateLimitService.checkAndTrackKeyUserSession(2, 1, "sess", 0, 0);
+    expect(result).toEqual({
+      allowed: true,
+      keyCount: 0,
+      userCount: 0,
+      trackedKey: false,
+      trackedUser: false,
+    });
+    expect(redisClientRef.eval).not.toHaveBeenCalled();
+  });
+
+  it("checkAndTrackKeyUserSession:Redis 非 ready 时应 Fail Open", async () => {
+    const { RateLimitService } = await import("@/lib/rate-limit");
+
+    redisClientRef.status = "end";
+    const result = await RateLimitService.checkAndTrackKeyUserSession(2, 1, "sess", 2, 2);
+    expect(result).toEqual({
+      allowed: true,
+      keyCount: 0,
+      userCount: 0,
+      trackedKey: false,
+      trackedUser: false,
+    });
+  });
+
+  it("checkAndTrackKeyUserSession:Key 超限时应返回 not allowed", async () => {
+    const { RateLimitService } = await import("@/lib/rate-limit");
+
+    redisClientRef.eval.mockResolvedValueOnce([0, 1, 2, 0, 1, 0]);
+    const result = await RateLimitService.checkAndTrackKeyUserSession(2, 1, "sess", 2, 10);
+    expect(result.allowed).toBe(false);
+    expect(result.rejectedBy).toBe("key");
+    expect(result.reasonCode).toBe("RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED");
+    expect(result.reasonParams).toEqual({ current: 2, limit: 2, target: "key" });
+  });
+
+  it("checkAndTrackKeyUserSession:User 超限时应返回 not allowed", async () => {
+    const { RateLimitService } = await import("@/lib/rate-limit");
+
+    redisClientRef.eval.mockResolvedValueOnce([0, 2, 1, 0, 2, 0]);
+    const result = await RateLimitService.checkAndTrackKeyUserSession(2, 1, "sess", 10, 2);
+    expect(result.allowed).toBe(false);
+    expect(result.rejectedBy).toBe("user");
+    expect(result.reasonCode).toBe("RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED");
+    expect(result.reasonParams).toEqual({ current: 2, limit: 2, target: "user" });
+  });
+
+  it("checkAndTrackKeyUserSession:未超限时应返回 allowed 且可标记 tracked", async () => {
+    const { RateLimitService } = await import("@/lib/rate-limit");
+
+    redisClientRef.eval.mockResolvedValueOnce([1, 0, 2, 1, 2, 1]);
+    const result = await RateLimitService.checkAndTrackKeyUserSession(2, 1, "sess", 2, 2);
+    expect(result).toEqual({
+      allowed: true,
+      keyCount: 2,
+      userCount: 2,
+      trackedKey: true,
+      trackedUser: true,
+    });
+  });
+
+  it("checkAndTrackKeyUserSession: should pass SESSION_TTL_MS as ARGV[5] to Lua script", async () => {
+    const { RateLimitService } = await import("@/lib/rate-limit");
+
+    redisClientRef.eval.mockResolvedValueOnce([1, 0, 1, 1, 1, 1]);
+    await RateLimitService.checkAndTrackKeyUserSession(2, 1, "sess", 2, 2);
+
+    expect(redisClientRef.eval).toHaveBeenCalledTimes(1);
+
+    const evalCall = redisClientRef.eval.mock.calls[0];
+    // evalCall: [script, numkeys, globalKey, keyKey, userKey, sessionId, keyLimit, userLimit, now, ttlMs]
+    // Indices:   0        1        2          3      4       5         6        7         8    9
+    expect(evalCall.length).toBe(10);
+
+    const ttlMsArg = evalCall[9];
+    expect(ttlMsArg).toBe("300000");
+  });
+
   it("trackUserDailyCost:fixed 模式应使用 STRING + TTL", async () => {
     const { RateLimitService } = await import("@/lib/rate-limit");
 

+ 85 - 0
tests/unit/lib/session-manager-terminate-session.test.ts

@@ -0,0 +1,85 @@
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+let redisClientRef: any;
+let pipelineRef: any;
+
+vi.mock("server-only", () => ({}));
+
+vi.mock("@/lib/logger", () => ({
+  logger: {
+    warn: vi.fn(),
+    info: vi.fn(),
+    error: vi.fn(),
+    debug: vi.fn(),
+    trace: vi.fn(),
+  },
+}));
+
+vi.mock("@/lib/redis", () => ({
+  getRedisClient: () => redisClientRef,
+}));
+
+describe("SessionManager.terminateSession", () => {
+  beforeEach(() => {
+    vi.resetAllMocks();
+    vi.resetModules();
+
+    pipelineRef = {
+      del: vi.fn(() => pipelineRef),
+      zrem: vi.fn(() => pipelineRef),
+      exec: vi.fn(async () => [[null, 1]]),
+    };
+
+    redisClientRef = {
+      status: "ready",
+      get: vi.fn(async () => null),
+      hget: vi.fn(async () => null),
+      pipeline: vi.fn(() => pipelineRef),
+    };
+  });
+
+  it("应同时从 global/key/user 的 active_sessions ZSET 中移除 sessionId(若可解析到 userId)", async () => {
+    const sessionId = "sess_test";
+    redisClientRef.get.mockImplementation(async (key: string) => {
+      if (key === `session:${sessionId}:provider`) return "42";
+      if (key === `session:${sessionId}:key`) return "7";
+      return null;
+    });
+    redisClientRef.hget.mockImplementation(async (key: string, field: string) => {
+      if (key === `session:${sessionId}:info` && field === "userId") return "123";
+      return null;
+    });
+
+    const { getGlobalActiveSessionsKey, getKeyActiveSessionsKey, getUserActiveSessionsKey } =
+      await import("@/lib/redis/active-session-keys");
+    const { SessionManager } = await import("@/lib/session-manager");
+
+    const ok = await SessionManager.terminateSession(sessionId);
+    expect(ok).toBe(true);
+
+    expect(redisClientRef.hget).toHaveBeenCalledWith(`session:${sessionId}:info`, "userId");
+
+    expect(pipelineRef.zrem).toHaveBeenCalledWith(getGlobalActiveSessionsKey(), sessionId);
+    expect(pipelineRef.zrem).toHaveBeenCalledWith("provider:42:active_sessions", sessionId);
+    expect(pipelineRef.zrem).toHaveBeenCalledWith(getKeyActiveSessionsKey(7), sessionId);
+    expect(pipelineRef.zrem).toHaveBeenCalledWith(getUserActiveSessionsKey(123), sessionId);
+  });
+
+  it("当 userId 不可用时,不应尝试 zrem user active_sessions key", async () => {
+    const sessionId = "sess_test";
+    redisClientRef.get.mockImplementation(async (key: string) => {
+      if (key === `session:${sessionId}:provider`) return "42";
+      if (key === `session:${sessionId}:key`) return "7";
+      return null;
+    });
+    redisClientRef.hget.mockResolvedValue(null);
+
+    const { getUserActiveSessionsKey } = await import("@/lib/redis/active-session-keys");
+    const { SessionManager } = await import("@/lib/session-manager");
+
+    const ok = await SessionManager.terminateSession(sessionId);
+    expect(ok).toBe(true);
+
+    expect(pipelineRef.zrem).not.toHaveBeenCalledWith(getUserActiveSessionsKey(123), sessionId);
+  });
+});

+ 8 - 3
tests/unit/lib/session-tracker-cleanup.test.ts

@@ -1,8 +1,12 @@
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { getGlobalActiveSessionsKey } from "@/lib/redis/active-session-keys";
 
 let redisClientRef: any;
 const pipelineCalls: Array<unknown[]> = [];
 
+/**
+ * 构造一个可记录调用的 Redis pipeline mock(用于断言 cleanup/expire 等行为)。
+ */
 const makePipeline = () => {
   const pipeline = {
     zadd: vi.fn((...args: unknown[]) => {
@@ -53,6 +57,7 @@ vi.mock("@/lib/redis", () => ({
 
 describe("SessionTracker - TTL and cleanup", () => {
   const nowMs = 1_700_000_000_000;
+  const globalKey = getGlobalActiveSessionsKey();
   const ORIGINAL_SESSION_TTL = process.env.SESSION_TTL;
 
   beforeEach(() => {
@@ -94,7 +99,7 @@ describe("SessionTracker - TTL and cleanup", () => {
       // Should call zremrangebyscore with cutoff = now - 600*1000 = now - 600000
       const expectedCutoff = nowMs - 600 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        globalKey,
         "-inf",
         expectedCutoff
       );
@@ -110,7 +115,7 @@ describe("SessionTracker - TTL and cleanup", () => {
       // Default: 300 seconds = 300000 ms
       const expectedCutoff = nowMs - 300 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        globalKey,
         "-inf",
         expectedCutoff
       );
@@ -267,7 +272,7 @@ describe("SessionTracker - TTL and cleanup", () => {
 
       const expectedCutoff = nowMs - 600 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        globalKey,
         "-inf",
         expectedCutoff
       );

+ 6 - 5
tests/unit/lib/session-ttl-validation.test.ts

@@ -1,4 +1,5 @@
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { getGlobalActiveSessionsKey } from "@/lib/redis/active-session-keys";
 
 /**
  * Tests for SESSION_TTL environment variable validation
@@ -99,7 +100,7 @@ describe("SESSION_TTL environment variable validation", () => {
       // Default: 300 seconds = 300000 ms
       const expectedCutoff = nowMs - 300 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        getGlobalActiveSessionsKey(),
         "-inf",
         expectedCutoff
       );
@@ -113,7 +114,7 @@ describe("SESSION_TTL environment variable validation", () => {
 
       const expectedCutoff = nowMs - 300 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        getGlobalActiveSessionsKey(),
         "-inf",
         expectedCutoff
       );
@@ -127,7 +128,7 @@ describe("SESSION_TTL environment variable validation", () => {
 
       const expectedCutoff = nowMs - 300 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        getGlobalActiveSessionsKey(),
         "-inf",
         expectedCutoff
       );
@@ -141,7 +142,7 @@ describe("SESSION_TTL environment variable validation", () => {
 
       const expectedCutoff = nowMs - 300 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        getGlobalActiveSessionsKey(),
         "-inf",
         expectedCutoff
       );
@@ -156,7 +157,7 @@ describe("SESSION_TTL environment variable validation", () => {
       // Custom: 600 seconds = 600000 ms
       const expectedCutoff = nowMs - 600 * 1000;
       expect(redisClientRef.zremrangebyscore).toHaveBeenCalledWith(
-        "global:active_sessions",
+        getGlobalActiveSessionsKey(),
         "-inf",
         expectedCutoff
       );

+ 213 - 0
tests/unit/proxy/billing-header-rectifier.test.ts

@@ -0,0 +1,213 @@
+import { describe, expect, test } from "vitest";
+import { rectifyBillingHeader } from "@/app/v1/_lib/proxy/billing-header-rectifier";
+
+describe("rectifyBillingHeader", () => {
+  test("system array with single billing header block - removes it and returns extractedValues", () => {
+    const message: Record<string, unknown> = {
+      system: [
+        {
+          type: "text",
+          text: "x-anthropic-billing-header: cc_version=2.1.36; cc_entrypoint=cli; cch=1;",
+        },
+      ],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(1);
+    expect(result.extractedValues).toEqual([
+      "x-anthropic-billing-header: cc_version=2.1.36; cc_entrypoint=cli; cch=1;",
+    ]);
+    expect(message.system).toEqual([]);
+  });
+
+  test("system array with multiple billing header blocks - removes all", () => {
+    const message: Record<string, unknown> = {
+      system: [
+        { type: "text", text: "x-anthropic-billing-header: cc_version=2.1.36;" },
+        { type: "text", text: "x-anthropic-billing-header: cc_entrypoint=cli;" },
+      ],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(2);
+    expect(result.extractedValues).toHaveLength(2);
+    expect(message.system).toEqual([]);
+  });
+
+  test("system array with no billing header - applied=false, array unchanged", () => {
+    const blocks = [
+      { type: "text", text: "You are a helpful assistant." },
+      { type: "text", text: "Follow instructions carefully." },
+    ];
+    const message: Record<string, unknown> = {
+      system: [...blocks],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(false);
+    expect(result.removedCount).toBe(0);
+    expect(result.extractedValues).toEqual([]);
+    expect(message.system).toEqual(blocks);
+  });
+
+  test("system array with billing header mixed with real prompts - only removes billing header blocks", () => {
+    const message: Record<string, unknown> = {
+      system: [
+        { type: "text", text: "You are a helpful assistant." },
+        { type: "text", text: "x-anthropic-billing-header: cc_version=2.1.36; cch=1;" },
+        { type: "text", text: "Follow instructions carefully." },
+      ],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(1);
+    expect(message.system).toEqual([
+      { type: "text", text: "You are a helpful assistant." },
+      { type: "text", text: "Follow instructions carefully." },
+    ]);
+  });
+
+  test("system as plain string that IS a billing header - deletes system field", () => {
+    const message: Record<string, unknown> = {
+      system: "x-anthropic-billing-header: cc_version=2.1.36;",
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(1);
+    expect(result.extractedValues).toEqual(["x-anthropic-billing-header: cc_version=2.1.36;"]);
+    expect(message.system).toBeUndefined();
+  });
+
+  test("system as plain string that is NOT a billing header - applied=false", () => {
+    const message: Record<string, unknown> = {
+      system: "You are a helpful assistant.",
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(false);
+    expect(result.removedCount).toBe(0);
+    expect(message.system).toBe("You are a helpful assistant.");
+  });
+
+  test("system undefined/missing - applied=false", () => {
+    const message: Record<string, unknown> = { model: "claude-3" };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(false);
+    expect(result.removedCount).toBe(0);
+    expect(result.extractedValues).toEqual([]);
+  });
+
+  test("system null - applied=false", () => {
+    const message: Record<string, unknown> = { system: null };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(false);
+    expect(result.removedCount).toBe(0);
+  });
+
+  test("billing header mid-string (not at start) - should NOT remove", () => {
+    const message: Record<string, unknown> = {
+      system: [
+        {
+          type: "text",
+          text: "Some preamble text x-anthropic-billing-header: cc_version=2.1.36;",
+        },
+      ],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(false);
+    expect(result.removedCount).toBe(0);
+    expect((message.system as unknown[]).length).toBe(1);
+  });
+
+  test("case insensitivity (X-Anthropic-Billing-Header:)", () => {
+    const message: Record<string, unknown> = {
+      system: [{ type: "text", text: "X-Anthropic-Billing-Header: cc_version=2.1.36;" }],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(1);
+    expect(message.system).toEqual([]);
+  });
+
+  test("system array becoming empty after removal - remains empty array", () => {
+    const message: Record<string, unknown> = {
+      system: [
+        { type: "text", text: "x-anthropic-billing-header: val1" },
+        { type: "text", text: "x-anthropic-billing-header: val2" },
+      ],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(2);
+    expect(message.system).toEqual([]);
+  });
+
+  test("various billing header value formats", () => {
+    const message: Record<string, unknown> = {
+      system: [
+        { type: "text", text: "x-anthropic-billing-header:cc_version=2.1.36" },
+        {
+          type: "text",
+          text: "  x-anthropic-billing-header: cc_version=2.2.0; cc_entrypoint=vscode;",
+        },
+        { type: "text", text: "x-anthropic-billing-header:  " },
+      ],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(3);
+    expect(result.extractedValues).toHaveLength(3);
+    expect(message.system).toEqual([]);
+  });
+
+  test("non-text type blocks are preserved", () => {
+    const message: Record<string, unknown> = {
+      system: [
+        { type: "image", source: { type: "base64" } },
+        { type: "text", text: "x-anthropic-billing-header: val" },
+        { type: "text", text: "Keep this" },
+      ],
+    };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(true);
+    expect(result.removedCount).toBe(1);
+    expect(message.system).toEqual([
+      { type: "image", source: { type: "base64" } },
+      { type: "text", text: "Keep this" },
+    ]);
+  });
+
+  test("system as non-string non-array type (e.g. number) - no-op", () => {
+    const message: Record<string, unknown> = { system: 42 };
+
+    const result = rectifyBillingHeader(message);
+
+    expect(result.applied).toBe(false);
+    expect(result.removedCount).toBe(0);
+    expect(message.system).toBe(42);
+  });
+});

+ 69 - 10
tests/unit/proxy/rate-limit-guard.test.ts

@@ -1,8 +1,10 @@
 import { beforeEach, describe, expect, it, vi } from "vitest";
 
+const generateSessionIdMock = vi.hoisted(() => vi.fn(() => "sess_generated"));
+
 const rateLimitServiceMock = {
   checkTotalCostLimit: vi.fn(),
-  checkSessionLimit: vi.fn(),
+  checkAndTrackKeyUserSession: vi.fn(),
   checkRpmLimit: vi.fn(),
   checkCostLimitsWithLease: vi.fn(),
   checkUserDailyCost: vi.fn(),
@@ -12,6 +14,12 @@ vi.mock("@/lib/rate-limit", () => ({
   RateLimitService: rateLimitServiceMock,
 }));
 
+vi.mock("@/lib/session-manager", () => ({
+  SessionManager: {
+    generateSessionId: generateSessionIdMock,
+  },
+}));
+
 vi.mock("@/lib/logger", () => ({
   logger: {
     warn: vi.fn(),
@@ -67,7 +75,8 @@ describe("ProxyRateLimitGuard - key daily limit enforcement", () => {
       limitConcurrentSessions: number;
     }>;
   }) => {
-    return {
+    const session = {
+      sessionId: "sess_test",
       authState: {
         user: {
           id: 1,
@@ -97,13 +106,26 @@ describe("ProxyRateLimitGuard - key daily limit enforcement", () => {
         },
       },
     } as any;
+
+    session.setSessionId = (id: string) => {
+      session.sessionId = id;
+    };
+
+    return session;
   };
 
   beforeEach(() => {
     vi.clearAllMocks();
+    generateSessionIdMock.mockReturnValue("sess_generated");
 
     rateLimitServiceMock.checkTotalCostLimit.mockResolvedValue({ allowed: true });
-    rateLimitServiceMock.checkSessionLimit.mockResolvedValue({ allowed: true });
+    rateLimitServiceMock.checkAndTrackKeyUserSession.mockResolvedValue({
+      allowed: true,
+      keyCount: 0,
+      userCount: 0,
+      trackedKey: false,
+      trackedUser: false,
+    });
     rateLimitServiceMock.checkRpmLimit.mockResolvedValue({ allowed: true });
     rateLimitServiceMock.checkUserDailyCost.mockResolvedValue({ allowed: true });
     rateLimitServiceMock.checkCostLimitsWithLease.mockResolvedValue({ allowed: true });
@@ -247,9 +269,15 @@ describe("ProxyRateLimitGuard - key daily limit enforcement", () => {
   it("Key 并发 Session 超限应拦截(concurrent_sessions)", async () => {
     const { ProxyRateLimitGuard } = await import("@/app/v1/_lib/proxy/rate-limit-guard");
 
-    rateLimitServiceMock.checkSessionLimit.mockResolvedValueOnce({
+    rateLimitServiceMock.checkAndTrackKeyUserSession.mockResolvedValueOnce({
       allowed: false,
-      reason: "Key并发 Session 上限已达到(2/1)",
+      rejectedBy: "key",
+      reasonCode: "RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED",
+      reasonParams: { current: 2, limit: 1, target: "key" },
+      keyCount: 2,
+      userCount: 0,
+      trackedKey: false,
+      trackedUser: false,
     });
 
     const session = createSession({
@@ -267,9 +295,16 @@ describe("ProxyRateLimitGuard - key daily limit enforcement", () => {
   it("User 并发 Session 超限应拦截(concurrent_sessions)", async () => {
     const { ProxyRateLimitGuard } = await import("@/app/v1/_lib/proxy/rate-limit-guard");
 
-    rateLimitServiceMock.checkSessionLimit
-      .mockResolvedValueOnce({ allowed: true }) // key session
-      .mockResolvedValueOnce({ allowed: false, reason: "User并发 Session 上限已达到(2/1)" });
+    rateLimitServiceMock.checkAndTrackKeyUserSession.mockResolvedValueOnce({
+      allowed: false,
+      rejectedBy: "user",
+      reasonCode: "RATE_LIMIT_CONCURRENT_SESSIONS_EXCEEDED",
+      reasonParams: { current: 2, limit: 1, target: "user" },
+      keyCount: 0,
+      userCount: 2,
+      trackedKey: false,
+      trackedUser: false,
+    });
 
     const session = createSession({
       user: { limitConcurrentSessions: 1 },
@@ -294,8 +329,13 @@ describe("ProxyRateLimitGuard - key daily limit enforcement", () => {
 
     await expect(ProxyRateLimitGuard.ensure(session)).resolves.toBeUndefined();
 
-    expect(rateLimitServiceMock.checkSessionLimit).toHaveBeenNthCalledWith(1, 2, "key", 15);
-    expect(rateLimitServiceMock.checkSessionLimit).toHaveBeenNthCalledWith(2, 1, "user", 15);
+    expect(rateLimitServiceMock.checkAndTrackKeyUserSession).toHaveBeenCalledWith(
+      2,
+      1,
+      "sess_test",
+      15,
+      15
+    );
   });
 
   it("User RPM 超限应拦截(rpm)", async () => {
@@ -474,6 +514,25 @@ describe("ProxyRateLimitGuard - key daily limit enforcement", () => {
     await expect(ProxyRateLimitGuard.ensure(session)).resolves.toBeUndefined();
   });
 
+  it("当 sessionId 缺失时,应兜底生成并继续并发检查", async () => {
+    const { ProxyRateLimitGuard } = await import("@/app/v1/_lib/proxy/rate-limit-guard");
+
+    const session = createSession() as any;
+    session.sessionId = undefined;
+
+    await expect(ProxyRateLimitGuard.ensure(session)).resolves.toBeUndefined();
+
+    expect(generateSessionIdMock).toHaveBeenCalledTimes(1);
+    expect(session.sessionId).toBe("sess_generated");
+    expect(rateLimitServiceMock.checkAndTrackKeyUserSession).toHaveBeenCalledWith(
+      2,
+      1,
+      "sess_generated",
+      expect.any(Number),
+      expect.any(Number)
+    );
+  });
+
   it("User daily (rolling mode) 超限应使用 checkCostLimitsWithLease", async () => {
     const { ProxyRateLimitGuard } = await import("@/app/v1/_lib/proxy/rate-limit-guard");
 

+ 143 - 0
tests/unit/repository/statistics-total-cost.test.ts

@@ -0,0 +1,143 @@
+import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
+
+describe("sumUserTotalCost & sumKeyTotalCost - all-time query support", () => {
+  beforeEach(() => {
+    vi.resetModules();
+  });
+
+  afterEach(() => {
+    vi.restoreAllMocks();
+  });
+
+  describe("All-time query (Infinity maxAgeDays)", () => {
+    it("should not add date filter when maxAgeDays is Infinity", async () => {
+      let capturedConditions: unknown;
+
+      vi.doMock("@/drizzle/db", () => ({
+        db: {
+          select: vi.fn().mockReturnValue({
+            from: vi.fn().mockReturnValue({
+              where: vi.fn().mockImplementation((conditions) => {
+                capturedConditions = conditions;
+                return Promise.resolve([{ total: 100 }]);
+              }),
+            }),
+          }),
+        },
+      }));
+
+      const { sumUserTotalCost } = await import("@/repository/statistics");
+      const result = await sumUserTotalCost(1, Infinity);
+
+      expect(result).toBe(100);
+      // The conditions should not contain a date filter (gte on createdAt)
+      // With Infinity, we expect only 3 conditions: userId eq, deletedAt isNull, warmup exclude
+      expect(capturedConditions).toBeDefined();
+    });
+
+    it("should not add date filter for sumKeyTotalCost with Infinity", async () => {
+      vi.doMock("@/drizzle/db", () => ({
+        db: {
+          select: vi.fn().mockReturnValue({
+            from: vi.fn().mockReturnValue({
+              where: vi.fn().mockResolvedValue([{ total: 200 }]),
+            }),
+          }),
+        },
+      }));
+
+      const { sumKeyTotalCost } = await import("@/repository/statistics");
+      const result = await sumKeyTotalCost("test-key-hash", Infinity);
+
+      expect(result).toBe(200);
+    });
+  });
+
+  describe("Finite maxAgeDays still adds date filter", () => {
+    it("should produce reasonable cutoff dates for standard periods", () => {
+      const testCases = [
+        { days: 1, expectedYearsAgo: 0 },
+        { days: 365, expectedYearsAgo: 1 },
+      ];
+
+      for (const { days, expectedYearsAgo } of testCases) {
+        const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
+        const now = new Date();
+        const yearDiff = now.getFullYear() - cutoffDate.getFullYear();
+
+        expect(yearDiff).toBeGreaterThanOrEqual(expectedYearsAgo - 1);
+        expect(yearDiff).toBeLessThanOrEqual(expectedYearsAgo + 1);
+      }
+    });
+
+    it("should add date filter for finite maxAgeDays", async () => {
+      vi.doMock("@/drizzle/db", () => ({
+        db: {
+          select: vi.fn().mockReturnValue({
+            from: vi.fn().mockReturnValue({
+              where: vi.fn().mockResolvedValue([{ total: 50 }]),
+            }),
+          }),
+        },
+      }));
+
+      const { sumUserTotalCost } = await import("@/repository/statistics");
+      const result = await sumUserTotalCost(1, 365);
+
+      expect(result).toBe(50);
+    });
+  });
+
+  describe("Edge cases", () => {
+    it("should skip date filter for maxAgeDays = 0", async () => {
+      vi.doMock("@/drizzle/db", () => ({
+        db: {
+          select: vi.fn().mockReturnValue({
+            from: vi.fn().mockReturnValue({
+              where: vi.fn().mockResolvedValue([{ total: 50 }]),
+            }),
+          }),
+        },
+      }));
+
+      const { sumUserTotalCost } = await import("@/repository/statistics");
+      const result = await sumUserTotalCost(1, 0);
+
+      expect(result).toBe(50);
+    });
+
+    it("should skip date filter for negative maxAgeDays", async () => {
+      vi.doMock("@/drizzle/db", () => ({
+        db: {
+          select: vi.fn().mockReturnValue({
+            from: vi.fn().mockReturnValue({
+              where: vi.fn().mockResolvedValue([{ total: 75 }]),
+            }),
+          }),
+        },
+      }));
+
+      const { sumUserTotalCost } = await import("@/repository/statistics");
+      const result = await sumUserTotalCost(1, -100);
+
+      expect(result).toBe(75);
+    });
+
+    it("should skip date filter for NaN maxAgeDays", async () => {
+      vi.doMock("@/drizzle/db", () => ({
+        db: {
+          select: vi.fn().mockReturnValue({
+            from: vi.fn().mockReturnValue({
+              where: vi.fn().mockResolvedValue([{ total: 30 }]),
+            }),
+          }),
+        },
+      }));
+
+      const { sumUserTotalCost } = await import("@/repository/statistics");
+      const result = await sumUserTotalCost(1, NaN);
+
+      expect(result).toBe(30);
+    });
+  });
+});