feat(providers): support Codex OAuth pools with inherited routing defaults

* feat(auth): support named chatgpt oauth providers

- add provider-scoped ChatGPT OAuth routes and CLI support

- persist refresh tokens per provider and reject provider-type collisions

- wire provider OAuth setup flows in the dashboard and setup UI

Refs #448

* feat(agent): add chatgpt oauth account routing

- add agent other_config routing for manual and round-robin selection

- reuse routed provider resolution across resolver and pending loaders

- add router, parser, and agent advanced dialog coverage for multi-account use

Refs #448

* docs(api): describe chatgpt oauth routing

- document named-provider ChatGPT OAuth auth routes

- describe agent-side account routing and round-robin behavior

- update OpenAPI agent config schema and provider type enum

Refs #448

* fix(store): add missing agent key context helpers

* feat(ui): clarify chatgpt oauth account setup and routing

* docs(providers): align chatgpt oauth alias examples

* feat(agent): add codex pool activity dashboard

* fix(providers): harden codex oauth alias setup

* feat(codex-pool): improve routing dashboard UX

- redesign the Codex/OpenAI pool page around saved-pool checkpoints and live evidence

- add clearer selection, attention, and recent-proof states for pool members

- make the lower panels fill the remaining desktop viewport while staying responsive

* fix(store): resolve context helper merge duplication

* feat(oauth): add codex pool quota and observation APIs

- add quota inspection and observation endpoints for ChatGPT Subscription (OAuth) providers

- teach codex routing to surface pool activity, observation metadata, and quota-aware readiness

- extend tests and HTTP docs/OpenAPI for the new pool monitoring flows

* feat(web): add codex pool quota monitor and controls

- add provider quota fetching, readiness badges, and live routing evidence on the account pool page

- redesign pool setup and activity panels for multi-account management with localized copy updates

- keep the live monitor internally scrollable and compact the account cards for better viewport fit

* fix(web): clarify pool routing labels

- rename the recent request badge from Direct to Selected

- restore compact quota bars in the live pool cards

* feat(codex-pool): add runtime health dashboard

- derive per-provider success and failure health from routed Codex traces

- surface routing, quota, and recent request evidence in the pool UI

- align provider alias guidance and owner access with the dashboard role model

* docs(auth): document tenant scoping and key roles

* fix(auth): harden tenant and codex pool access control

* fix(providers): align codex pool runtime defaults

* feat(ui): tighten codex pool responsive layout

* feat(chatgpt-oauth): refine codex pool management UX

* feat(chatgpt-oauth): surface quota bars on provider pages

- add compact quota bars to Codex provider rows and provider detail

- fetch quota only for ready visible provider rows and ready detail aliases

- fix managed-member detail visibility and tighten provider locale copy
This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-03-27 09:35:57 +07:00
1 parent e183b459c9
commit 30708ae79d
113 files changed
+11816 -536

No files matched your search

+218 -2
View File
@@ -21,6 +21,7 @@
{ "name": "Agents", "description": "Agent CRUD and configuration" },
{ "name": "Sessions", "description": "Chat session management (via WebSocket RPC)" },
{ "name": "Providers", "description": "LLM provider configuration" },
{ "name": "OAuth", "description": "Provider-scoped OAuth status and quota endpoints" },
{ "name": "Skills", "description": "Skill management and grants" },
{ "name": "MCP Servers", "description": "MCP server configuration and grants" },
{ "name": "Custom Tools", "description": "Custom tool definitions" },
@@ -196,6 +197,112 @@
}
}
},
"/v1/auth/chatgpt/{provider}/quota": {
"get": {
"tags": ["OAuth"],
"summary": "Fetch OpenAI Codex OAuth quota for a provider",
"description": "Returns structured Codex/OpenAI quota state for a specific OpenAI Codex OAuth (`chatgpt_oauth`) provider. Missing provider returns `404`, provider type conflicts return `409`, and upstream quota problems return `200` with `success=false` so dashboards can render inline action states.",
"parameters": [
{
"name": "provider",
"in": "path",
"required": true,
"schema": { "type": "string" },
"description": "Provider alias (for example `openai-codex` or `codex-work`)."
}
],
"responses": {
"200": {
"description": "Provider quota payload",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["provider_name", "success", "windows", "last_updated"],
"properties": {
"provider_name": { "type": "string", "example": "openai-codex" },
"success": { "type": "boolean" },
"plan_type": { "type": "string", "nullable": true, "example": "team" },
"windows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": { "type": "string", "example": "Primary" },
"used_percent": { "type": "integer", "example": 24 },
"remaining_percent": { "type": "integer", "example": 76 },
"reset_after_seconds": { "type": "integer", "nullable": true, "example": 3600 },
"reset_at": { "type": "string", "format": "date-time", "nullable": true }
}
}
},
"core_usage": {
"type": "object",
"nullable": true,
"properties": {
"five_hour": {
"type": "object",
"nullable": true,
"properties": {
"label": { "type": "string" },
"remaining_percent": { "type": "integer" },
"reset_after_seconds": { "type": "integer", "nullable": true },
"reset_at": { "type": "string", "format": "date-time", "nullable": true }
}
},
"weekly": {
"type": "object",
"nullable": true,
"properties": {
"label": { "type": "string" },
"remaining_percent": { "type": "integer" },
"reset_after_seconds": { "type": "integer", "nullable": true },
"reset_at": { "type": "string", "format": "date-time", "nullable": true }
}
}
}
},
"last_updated": { "type": "string", "format": "date-time" },
"error": { "type": "string" },
"error_code": { "type": "string" },
"action_hint": { "type": "string" },
"needs_reauth": { "type": "boolean" },
"is_forbidden": { "type": "boolean" },
"retryable": { "type": "boolean" }
}
}
}
}
},
"400": { "$ref": "#/components/responses/BadRequest" },
"401": { "$ref": "#/components/responses/Unauthorized" },
"404": { "$ref": "#/components/responses/NotFound" },
"409": { "$ref": "#/components/responses/Conflict" }
}
}
},
"/v1/auth/openai/quota": {
"get": {
"tags": ["OAuth"],
"summary": "Fetch quota for the default OpenAI Codex OAuth provider",
"description": "Compatibility alias for `/v1/auth/chatgpt/openai-codex/quota`.",
"responses": {
"200": {
"description": "Provider quota payload",
"content": {
"application/json": {
"schema": {
"$ref": "#/paths/~1v1~1auth~1chatgpt~1{provider}~1quota/get/responses/200/content/application~1json/schema"
}
}
}
},
"401": { "$ref": "#/components/responses/Unauthorized" },
"404": { "$ref": "#/components/responses/NotFound" },
"409": { "$ref": "#/components/responses/Conflict" }
}
}
},
"/v1/agents": {
"get": {
"tags": ["Agents"],
@@ -242,6 +349,96 @@
"responses": { "200": { "description": "Agent deleted" }, "404": { "$ref": "#/components/responses/NotFound" } }
}
},
"/v1/agents/{id}/codex-pool-activity": {
"get": {
"tags": ["Agents"],
"summary": "Summarize recent OpenAI Codex OAuth pool activity for an agent",
"description": "Returns explicit router-backed evidence for recent OpenAI Codex OAuth calls in the configured provider pool. Direct router selections are reported separately from failover serves, and each recent request shows both the selected alias and the alias that actually served the call.",
"parameters": [
{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
{
"name": "limit",
"in": "query",
"required": false,
"schema": { "type": "integer", "default": 18, "minimum": 1, "maximum": 50 },
"description": "Maximum number of recent routed Codex LLM calls to include."
}
],
"responses": {
"200": {
"description": "Codex pool activity payload",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["strategy", "pool_providers", "stats_sample_size", "provider_counts", "recent_requests"],
"properties": {
"strategy": { "type": "string", "enum": ["primary_first", "round_robin", "priority_order"] },
"pool_providers": {
"type": "array",
"items": { "type": "string" }
},
"stats_sample_size": {
"type": "integer",
"description": "Number of recent routed llm_call spans used to derive runtime health. The server samples max(limit, 120) spans even when recent_requests remains capped by limit."
},
"provider_counts": {
"type": "array",
"items": {
"type": "object",
"required": ["provider_name", "request_count", "direct_selection_count", "failover_serve_count", "success_count", "failure_count", "consecutive_failures", "success_rate", "health_score", "health_state"],
"properties": {
"provider_name": { "type": "string", "description": "Provider alias in the configured pool." },
"request_count": { "type": "integer", "description": "Backward-compatible count of requests where this alias was directly selected by the router." },
"direct_selection_count": { "type": "integer", "description": "Number of recent requests where the router selected this alias first." },
"failover_serve_count": { "type": "integer", "description": "Number of recent requests this alias served after another alias was selected first." },
"success_count": { "type": "integer", "description": "Trace-backed successes attributed to the alias that actually served the request." },
"failure_count": { "type": "integer", "description": "Trace-backed failures attributed to earlier attempted aliases on successful failover, or to every attempted alias on terminal error." },
"consecutive_failures": { "type": "integer", "description": "Newest-first failure streak for this alias." },
"success_rate": { "type": "integer", "description": "Success rate percent derived from recent runtime outcomes." },
"health_score": { "type": "integer", "description": "Heuristic runtime health score from 0 to 100." },
"health_state": { "type": "string", "description": "Health band derived from recent runtime outcomes: idle when there are no outcomes, critical at 3+ consecutive failures or score < 40, degraded below 80, otherwise healthy.", "enum": ["idle", "healthy", "degraded", "critical"] },
"last_selected_at": { "type": "string", "format": "date-time", "nullable": true },
"last_failover_at": { "type": "string", "format": "date-time", "nullable": true },
"last_used_at": { "type": "string", "format": "date-time", "nullable": true },
"last_success_at": { "type": "string", "format": "date-time", "nullable": true },
"last_failure_at": { "type": "string", "format": "date-time", "nullable": true }
}
}
},
"recent_requests": {
"type": "array",
"items": {
"type": "object",
"required": ["span_id", "trace_id", "started_at", "status", "duration_ms", "provider_name", "model", "attempt_count", "used_failover"],
"properties": {
"span_id": { "type": "string", "format": "uuid" },
"trace_id": { "type": "string", "format": "uuid" },
"started_at": { "type": "string", "format": "date-time" },
"status": { "type": "string" },
"duration_ms": { "type": "integer" },
"provider_name": { "type": "string", "description": "Alias that actually served the request. Empty when a terminal failure completed without any alias serving successfully." },
"selected_provider": { "type": "string", "description": "Alias chosen first by the router before any failover." },
"model": { "type": "string" },
"attempt_count": { "type": "integer" },
"used_failover": { "type": "boolean" },
"failover_providers": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
}
}
}
}
},
"401": { "$ref": "#/components/responses/Unauthorized" },
"404": { "$ref": "#/components/responses/NotFound" }
}
}
},
"/v1/agents/{id}/wake": {
"post": {
"tags": ["Agents"],
@@ -640,7 +837,22 @@
"description": { "type": "string" },
"provider": { "type": "string", "description": "LLM provider name" },
"model": { "type": "string", "description": "Model ID" },
"system_prompt": { "type": "string" }
"system_prompt": { "type": "string" },
"other_config": {
"type": "object",
"description": "Optional per-agent JSON config.",
"properties": {
"chatgpt_oauth_routing": {
"type": "object",
"description": "Optional agent-side routing override for ChatGPT OAuth providers. The main provider field remains the preferred/default account, while provider settings may supply inherited defaults.",
"properties": {
"override_mode": { "type": "string", "enum": ["inherit", "custom"] },
"strategy": { "type": "string", "enum": ["manual", "primary_first", "round_robin", "priority_order"] },
"extra_provider_names": { "type": "array", "items": { "type": "string" } }
}
}
}
}
}
},
"ProviderInput": {
@@ -649,7 +861,7 @@
"properties": {
"name": { "type": "string", "description": "Unique provider slug" },
"display_name": { "type": "string" },
"provider_type": { "type": "string", "enum": ["anthropic_native", "openai_compat", "gemini_native", "groq", "deepseek", "mistral", "xai", "ollama"] },
"provider_type": { "type": "string", "enum": ["anthropic_native", "openai_compat", "gemini_native", "openrouter", "groq", "deepseek", "mistral", "xai", "minimax_native", "cohere", "perplexity", "dashscope", "bailian", "chatgpt_oauth", "claude_cli", "suno", "yescale", "zai", "zai_coding", "ollama", "ollama_cloud", "acp"] },
"api_base": { "type": "string" },
"api_key": { "type": "string" },
"enabled": { "type": "boolean", "default": true }
@@ -674,6 +886,10 @@
"NotFound": {
"description": "Resource not found",
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
},
"Conflict": {
"description": "Resource state conflict",
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
}
}
}