mirror of
https://github.com/coder/coder.git
synced 2026-10-30 18:12:29 +08:00
fix: detect Xum client in aibridge after Mux rename (#28448)
This commit is contained in:
+4
-3
@@ -18,7 +18,7 @@ const (
|
||||
ClientKilo Client = "Kilo Code"
|
||||
ClientCoderAgents Client = "Coder Agents"
|
||||
ClientCrush Client = "Charm Crush"
|
||||
ClientMux Client = "Mux"
|
||||
ClientXum Client = "Xum"
|
||||
ClientRoo Client = "Roo Code"
|
||||
ClientCursor Client = "Cursor"
|
||||
ClientOpenCode Client = "OpenCode"
|
||||
@@ -35,8 +35,9 @@ func GuessClient(r *http.Request) Client {
|
||||
|
||||
// Must be kept in sync with documentation: https://github.com/coder/coder/blob/3cf867f84aa32d2febf7a26dc7e52be6beb8a2ac/docs/ai-coder/ai-gateway/monitoring.md?plain=1#L47-L57
|
||||
switch {
|
||||
case strings.HasPrefix(userAgent, "mux/"):
|
||||
return ClientMux
|
||||
case strings.HasPrefix(userAgent, "xum/") || strings.HasPrefix(userAgent, "mux/"):
|
||||
// Mux was renamed to Xum; "mux/" is kept for older, unupgraded clients.
|
||||
return ClientXum
|
||||
case strings.HasPrefix(userAgent, "claude"):
|
||||
return ClientClaudeCode
|
||||
case strings.HasPrefix(userAgent, "codex"):
|
||||
|
||||
@@ -19,9 +19,14 @@ func TestGuessClient(t *testing.T) {
|
||||
wantClient aibridge.Client
|
||||
}{
|
||||
{
|
||||
name: "mux",
|
||||
name: "xum",
|
||||
userAgent: "xum/0.20.0 ai-sdk/openai/3.0.36 ai-sdk/provider-utils/4.0.15 runtime/node.js/22",
|
||||
wantClient: aibridge.ClientXum,
|
||||
},
|
||||
{
|
||||
name: "xum_legacy_mux_user_agent",
|
||||
userAgent: "mux/0.19.0-next.2.gcceff159 ai-sdk/openai/3.0.36 ai-sdk/provider-utils/4.0.15 runtime/node.js/22",
|
||||
wantClient: aibridge.ClientMux,
|
||||
wantClient: aibridge.ClientXum,
|
||||
},
|
||||
{
|
||||
name: "claude_code",
|
||||
|
||||
@@ -1172,13 +1172,13 @@ func TestSessionIDTracking(t *testing.T) {
|
||||
}{
|
||||
// Session in header.
|
||||
{
|
||||
name: "mux",
|
||||
name: "xum",
|
||||
fixture: fixtures.AntSimple,
|
||||
expectedClient: aibridge.ClientMux,
|
||||
expectSessionID: "mux-workspace-321",
|
||||
expectedClient: aibridge.ClientXum,
|
||||
expectSessionID: "xum-workspace-321",
|
||||
header: http.Header{
|
||||
"User-Agent": []string{"mux/1.0.0"},
|
||||
"X-Mux-Workspace-Id": []string{"mux-workspace-321"},
|
||||
"User-Agent": []string{"xum/1.0.0"},
|
||||
"X-Mux-Workspace-Id": []string{"xum-workspace-321"},
|
||||
},
|
||||
},
|
||||
// Session in body.
|
||||
|
||||
+3
-1
@@ -62,7 +62,9 @@ func GuessSessionID(client Client, r *http.Request) *string {
|
||||
return sid
|
||||
}
|
||||
return cleanRef(r.Header.Get("session_id"))
|
||||
case ClientMux:
|
||||
case ClientXum:
|
||||
// Header name is intentionally still "X-Mux-Workspace-Id"; Xum keeps the
|
||||
// wire value from before the Mux rename.
|
||||
return cleanRef(r.Header.Get("X-Mux-Workspace-Id"))
|
||||
case ClientZed:
|
||||
return nil // Zed does not send a session ID from Zed Agent or Text Thread.
|
||||
|
||||
@@ -139,16 +139,16 @@ func TestGuessSessionID(t *testing.T) {
|
||||
headers: map[string]string{"session_id": "zed-session"},
|
||||
body: `{"metadata":{"user_id":"user_abc_account_456_session_some-id"}}`,
|
||||
},
|
||||
// Mux.
|
||||
// Xum.
|
||||
{
|
||||
name: "mux_with_workspace_header",
|
||||
client: aibridge.ClientMux,
|
||||
name: "xum_with_workspace_header",
|
||||
client: aibridge.ClientXum,
|
||||
headers: map[string]string{"X-Mux-Workspace-Id": "ws-abc-123"},
|
||||
sessionID: utils.PtrTo("ws-abc-123"),
|
||||
},
|
||||
{
|
||||
name: "mux_without_workspace_header",
|
||||
client: aibridge.ClientMux,
|
||||
name: "xum_without_workspace_header",
|
||||
client: aibridge.ClientXum,
|
||||
},
|
||||
// Copilot VS Code.
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@ The table below shows tested AI clients and their compatibility with AI Gateway.
|
||||
|
||||
| Client | OpenAI | Anthropic | BYOK | Notes |
|
||||
|-----------------------------------|--------|-----------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Mux](./mux.md) | ✅ | ✅ | - | |
|
||||
| [Xum](./xum.md) | ✅ | ✅ | - | |
|
||||
| [Claude Code](./claude-code.md) | - | ✅ | ✅ | |
|
||||
| [Codex CLI](./codex.md) | ✅ | - | ✅ | |
|
||||
| [OpenCode](./opencode.md) | ✅ | ✅ | ✅ | |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Mux
|
||||
# Xum
|
||||
|
||||
> [!NOTE]
|
||||
> AI Gateway is part of [AI Governance](../../ai-governance.md), which is
|
||||
> included with a Premium license.
|
||||
|
||||
Mux makes it easy to run parallel coding agents, each with its own isolated workspace, from your browser or desktop; it is open source and provider-agnostic.
|
||||
Xum (formerly Mux) makes it easy to run parallel coding agents, each with its own isolated workspace, from your browser or desktop; it is open source and provider-agnostic.
|
||||
|
||||
Mux can be configured to route OpenAI- and Anthropic-compatible traffic through AI Gateway by setting a custom provider base URL and using a Coder-issued token for authentication.
|
||||
Xum can be configured to route OpenAI- and Anthropic-compatible traffic through AI Gateway by setting a custom provider base URL and using a Coder-issued token for authentication.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -19,14 +19,14 @@ Mux can be configured to route OpenAI- and Anthropic-compatible traffic through
|
||||
|
||||
### OpenAI
|
||||
|
||||
1. Open Mux settings (`Cmd+,` / `Ctrl+,`).
|
||||
1. Open Xum settings (`Cmd+,` / `Ctrl+,`).
|
||||
2. Go to **Providers** → **OpenAI**.
|
||||
3. Set **API Key** to your Coder API token.
|
||||
4. Set **Base URL** to `https://coder.example.com/api/v2/ai-gateway/openai/v1`.
|
||||
|
||||
### Anthropic
|
||||
|
||||
1. Open Mux settings (`Cmd+,` / `Ctrl+,`).
|
||||
1. Open Xum settings (`Cmd+,` / `Ctrl+,`).
|
||||
2. Go to **Providers** → **Anthropic**.
|
||||
3. Set **API Key** to your Coder API token.
|
||||
4. Set **Base URL** to `https://coder.example.com/api/v2/ai-gateway/anthropic`.
|
||||
@@ -37,11 +37,11 @@ _Replace `coder.example.com` with your Coder deployment URL._
|
||||
|
||||
## Environment variables
|
||||
|
||||
Mux reads provider configuration from its settings UI and also from environment variables.
|
||||
Environment variables are useful in CI or when running Mux inside a Coder workspace.
|
||||
Xum reads provider configuration from its settings UI and also from environment variables.
|
||||
Environment variables are useful in CI or when running Xum inside a Coder workspace.
|
||||
|
||||
> [!NOTE]
|
||||
> Mux treats environment variables as a fallback when a provider is not configured in settings.
|
||||
> Xum treats environment variables as a fallback when a provider is not configured in settings.
|
||||
> If you have already configured a provider in the UI, clear it (or update it) for env vars to take effect.
|
||||
|
||||
```sh
|
||||
@@ -54,9 +54,9 @@ export ANTHROPIC_API_KEY="<your-coder-api-token>"
|
||||
export ANTHROPIC_BASE_URL="https://coder.example.com/api/v2/ai-gateway/anthropic"
|
||||
```
|
||||
|
||||
## Run Mux in a Coder workspace
|
||||
## Run Xum in a Coder workspace
|
||||
|
||||
To run Mux inside a Coder workspace (for example, as a Coder app), you can install it with the [Mux module](https://registry.coder.com/modules/coder/mux) and pre-configure AI Gateway via environment variables on the agent:
|
||||
To run Xum inside a Coder workspace (for example, as a Coder app), you can install it with the [`mux` module](https://registry.coder.com/modules/coder/mux) (still published under its pre-rename name) and pre-configure AI Gateway via environment variables on the agent:
|
||||
|
||||
```tf
|
||||
data "coder_workspace" "me" {}
|
||||
@@ -82,7 +82,7 @@ module "mux" {
|
||||
|
||||
## Advanced: providers.jsonc
|
||||
|
||||
If you prefer a file-based config, edit `~/.mux/providers.jsonc`:
|
||||
If you prefer a file-based config, edit `~/.xum/providers.jsonc`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -97,4 +97,4 @@ If you prefer a file-based config, edit `~/.mux/providers.jsonc`:
|
||||
}
|
||||
```
|
||||
|
||||
**References:** [Mux provider environment variables](https://mux.coder.com/config/providers#environment-variables)
|
||||
**References:** [Xum provider environment variables](https://xum.coder.com/config/providers#environment-variables)
|
||||
@@ -220,7 +220,7 @@ Available query filters:
|
||||
- `Kilo Code`
|
||||
- `Roo Code`
|
||||
- `Coder Agents`
|
||||
- `Mux`
|
||||
- `Xum`
|
||||
- `Cursor`
|
||||
- `OpenCode`
|
||||
- `Charm Crush`
|
||||
|
||||
+3
-3
@@ -1226,9 +1226,9 @@
|
||||
"state": ["premium"]
|
||||
},
|
||||
{
|
||||
"title": "Mux",
|
||||
"description": "Configure Mux, the parallel coding agent tool, to route requests through AI Gateway.",
|
||||
"path": "./ai-coder/ai-gateway/clients/mux.md",
|
||||
"title": "Xum",
|
||||
"description": "Configure Xum, the parallel coding agent tool, to route requests through AI Gateway.",
|
||||
"path": "./ai-coder/ai-gateway/clients/xum.md",
|
||||
"state": ["premium"]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -49,6 +49,14 @@ export const AIBridgeClientIcon = ({
|
||||
className={cn(iconClassName, className)}
|
||||
/>
|
||||
);
|
||||
case "Xum":
|
||||
return (
|
||||
<ExternalImage
|
||||
src="/icon/xum.svg"
|
||||
className={cn(iconClassName, className)}
|
||||
/>
|
||||
);
|
||||
// Legacy label for interceptions recorded before the Mux -> Xum rename.
|
||||
case "Mux":
|
||||
return (
|
||||
<ExternalImage
|
||||
|
||||
@@ -5611,10 +5611,10 @@ export const MockSession: TypesGen.AIBridgeSession = {
|
||||
},
|
||||
providers: ["anthropic", "openai"],
|
||||
models: ["claude-opus-4-6", "gpt-5.4"],
|
||||
client: "Mux",
|
||||
client: "Xum",
|
||||
metadata: {
|
||||
request_user_agent:
|
||||
"mux/0.20.1-next.8.g0f494106 ai-sdk/anthropic/3.0.58 ai-sdk/provider-utils/4.0.19 runtime/node.js/22",
|
||||
"xum/0.20.1-next.8.g0f494106 ai-sdk/anthropic/3.0.58 ai-sdk/provider-utils/4.0.19 runtime/node.js/22",
|
||||
},
|
||||
started_at: "2026-03-09T09:28:15.03152Z",
|
||||
ended_at: "2026-03-09T10:28:17.294897Z",
|
||||
|
||||
@@ -187,6 +187,7 @@ export const defaultParametersForBuiltinIcons = new Map<string, string>([
|
||||
["/icon/terminal.svg", "monochrome"],
|
||||
["/icon/vercel.svg", "whiteWithColor"],
|
||||
["/icon/widgets.svg", "monochrome"],
|
||||
["/icon/xum.svg", "monochrome"],
|
||||
["/icon/windsurf.svg", "monochrome"],
|
||||
["/icon/zed.svg", "monochrome"],
|
||||
]);
|
||||
|
||||
@@ -151,5 +151,6 @@
|
||||
"widgets.svg",
|
||||
"windows.svg",
|
||||
"windsurf.svg",
|
||||
"xum.svg",
|
||||
"zed.svg"
|
||||
]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="matrix(5.595628 0 0 5.595628 -73.442623 -73.442623)">
|
||||
<path d="M13.125 47.5 20.9101 36.4133 13.3288 25.6526H19.8096L24.497 32.6634L29.0214 25.6526H35.6652L28.1246 36.454L35.869 47.5H29.3882L24.5785 40.0817L19.7281 47.5H13.125Z" fill="black"/>
|
||||
<rect x="38.875" y="24.5" width="20" height="23" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 433 B |
Reference in New Issue
Block a user