From 9ae4ee7cb206b3e35f44785a0304ad26c9cf38c9 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Tue, 18 Aug 2026 14:57:34 +0000 Subject: [PATCH 1/5] feat: track Junie as an AI Gateway client Junie CLI sends a 'Junie:' user agent, which AI Gateway did not recognize, so every Junie session was recorded as 'Unknown'. Detect it in GuessClient, add the client icon, and document the Junie client setup. --- aibridge/client.go | 3 + aibridge/client_test.go | 10 ++ docs/ai-coder/ai-gateway/clients/index.md | 1 + docs/ai-coder/ai-gateway/clients/junie.md | 127 ++++++++++++++++++ docs/ai-coder/ai-gateway/monitoring.md | 3 + docs/manifest.json | 6 + .../AIBridgePage/icons/AIBridgeClientIcon.tsx | 7 + site/src/theme/icons.json | 1 + site/static/icon/junie.svg | 17 +++ 9 files changed, 175 insertions(+) create mode 100644 docs/ai-coder/ai-gateway/clients/junie.md create mode 100644 site/static/icon/junie.svg diff --git a/aibridge/client.go b/aibridge/client.go index f5ff608a324d9..08516e28d1395 100644 --- a/aibridge/client.go +++ b/aibridge/client.go @@ -22,6 +22,7 @@ const ( ClientRoo Client = "Roo Code" ClientCursor Client = "Cursor" ClientOpenCode Client = "OpenCode" + ClientJunie Client = "Junie" ClientUnknown Client = "Unknown" ) @@ -58,6 +59,8 @@ func GuessClient(r *http.Request) Client { return ClientCursor case strings.HasPrefix(userAgent, "opencode/"): return ClientOpenCode + case strings.HasPrefix(userAgent, "junie:") || strings.HasPrefix(userAgent, "junie/"): + return ClientJunie } return ClientUnknown } diff --git a/aibridge/client_test.go b/aibridge/client_test.go index 253a374a69982..d9062c47f173e 100644 --- a/aibridge/client_test.go +++ b/aibridge/client_test.go @@ -104,6 +104,16 @@ func TestGuessClient(t *testing.T) { userAgent: "opencode/1.16.0 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14", wantClient: aibridge.ClientOpenCode, }, + { + name: "junie_cli", + userAgent: "Junie:SNAPSHOT", + wantClient: aibridge.ClientJunie, + }, + { + name: "junie_cli_versioned", + userAgent: "Junie/2651.6", + wantClient: aibridge.ClientJunie, + }, { name: "unknown_client", userAgent: "ccclaude-cli/calude-with-wrong-prefix", diff --git a/docs/ai-coder/ai-gateway/clients/index.md b/docs/ai-coder/ai-gateway/clients/index.md index f0d78aae31e56..cc372c1df6f72 100644 --- a/docs/ai-coder/ai-gateway/clients/index.md +++ b/docs/ai-coder/ai-gateway/clients/index.md @@ -50,6 +50,7 @@ The table below shows tested AI clients and their compatibility with AI Gateway. | [Kilo Code](./kilo-code.md) | ✅ | ✅ | ❌ | | | [VS Code](./vscode.md) | ✅ | ✅ | ❌ | VS Code 1.122+ via Custom Endpoint provider. GitHub sign-in not required. Inline suggestions still require GitHub Copilot. | | [JetBrains IDEs](./jetbrains.md) | ✅ | ❌ | ❌ | Works in Chat mode via [third-party model configuration](https://www.jetbrains.com/help/ai-assistant/use-custom-models.html#provide-your-own-api-key). | +| [Junie](./junie.md) | ✅ | ✅ | ✅ | Junie CLI via custom model profiles. Use the `OpenAIResponses` API type. | | [Zed](./zed.md) | ✅ | ✅ | ❌ | | | [GitHub Copilot](./copilot.md) | ⚙️ | - | - | Requires [AI Gateway Proxy](../ai-gateway-proxy/index.md). Uses per-user GitHub tokens. | | Devin Desktop (formerly Windsurf) | ❌ | ❌ | ❌ | No option to override base URL. | diff --git a/docs/ai-coder/ai-gateway/clients/junie.md b/docs/ai-coder/ai-gateway/clients/junie.md new file mode 100644 index 0000000000000..f71ea048c3100 --- /dev/null +++ b/docs/ai-coder/ai-gateway/clients/junie.md @@ -0,0 +1,127 @@ +# Junie + +> [!NOTE] +> AI Gateway is part of [AI Governance](../../ai-governance.md), which is +> included with a Premium license. + +[Junie CLI](https://junie.jetbrains.com/docs/junie-cli.html) supports AI Gateway +through [custom model profiles](https://www.jetbrains.com/help/junie/custom-llm-models.html). +Both the OpenAI and Anthropic endpoints are supported. + +For Junie inside JetBrains IDEs, refer to [JetBrains IDEs](./jetbrains.md). + +## Prerequisites + +* **Junie CLI**: Installed with `curl -fsSL https://junie.jetbrains.com/install.sh | bash`. +* **Authentication**: Your **[Coder API token](../../../admin/users/sessions-tokens.md#generate-a-long-lived-api-token-on-behalf-of-yourself)**. + +## Centralized API Key + +Junie discovers custom model profiles from JSON files in two locations: + +| Scope | Location | +|---------|--------------------------| +| User | `~/.junie/models/*.json` | +| Project | `.junie/models/*.json` | + +The file name without the `.json` extension is the profile ID. + +Create `~/.junie/models/ai-gateway.json` for the OpenAI endpoint: + +```json +{ + "id": "gpt-5.5", + "baseUrl": "https://coder.example.com/api/v2/ai-gateway/openai/v1/responses", + "apiType": "OpenAIResponses", + "apiKey": "${CODER_API_TOKEN}", + "fasterModel": { "id": "gpt-5.4-mini" } +} +``` + +> [!IMPORTANT] +> Use the `OpenAIResponses` API type. Junie sends a `stop` sequence on the Chat +> Completions API, which OpenAI reasoning models reject. + +Or for the Anthropic endpoint: + +```json +{ + "id": "claude-sonnet-5", + "baseUrl": "https://coder.example.com/api/v2/ai-gateway/anthropic/v1/messages", + "apiType": "Anthropic", + "apiKey": "${CODER_API_TOKEN}", + "fasterModel": { "id": "claude-haiku-4-5-20251001" } +} +``` + +*Replace `coder.example.com` with your Coder deployment URL.* + +> [!NOTE] +> `baseUrl` is the complete endpoint URL. Junie does not append a path to it, so +> include `/v1/responses` for `OpenAIResponses` and `/v1/messages` for +> `Anthropic`. + +Junie resolves `${VAR}` references in `apiKey` and `extraHeaders` from the +environment, so the token never has to be written to disk: + +```sh +export CODER_API_TOKEN=$(coder login token) +``` + +Run Junie with the profile: + +```sh +junie --model custom:ai-gateway +``` + +Custom profiles also appear in the model selection list of the interactive TUI +and in the `/model` slash command. + +## BYOK (Personal API Key) + +Junie supports custom headers, so BYOK works by sending your personal provider +key as the API key and the Coder API token as the governance token: + +```json +{ + "id": "gpt-5.5", + "baseUrl": "https://coder.example.com/api/v2/ai-gateway/openai/v1/responses", + "apiType": "OpenAIResponses", + "apiKey": "${OPENAI_API_KEY}", + "extraHeaders": { "X-Coder-AI-Governance-Token": "${CODER_API_TOKEN}" } +} +``` + +Set both environment variables: + +```sh +# Your personal provider API key, forwarded to the upstream provider. +export OPENAI_API_KEY="" + +# Your Coder API token, used for authentication with AI Gateway. +export CODER_API_TOKEN="" +``` + +## Pre-configuring in Templates + +Commit the profile to your repository at `.junie/models/ai-gateway.json` and +inject the token from the template, so users get a working configuration without +manual setup: + +```tf +data "coder_workspace_owner" "me" {} + +data "coder_workspace" "me" {} + +resource "coder_env" "coder_api_token" { + agent_id = coder_agent.main.id + name = "CODER_API_TOKEN" + value = data.coder_workspace_owner.me.session_token +} +``` + +Reference the deployment URL in the profile with +`${data.coder_workspace.me.access_url}/api/v2/ai-gateway/openai/v1/responses` +if you generate the file from the template instead of committing it. + +**References:** [Junie custom LLMs](https://www.jetbrains.com/help/junie/custom-llm-models.html) diff --git a/docs/ai-coder/ai-gateway/monitoring.md b/docs/ai-coder/ai-gateway/monitoring.md index 8cb9670899852..12b5c01701260 100644 --- a/docs/ai-coder/ai-gateway/monitoring.md +++ b/docs/ai-coder/ai-gateway/monitoring.md @@ -211,10 +211,13 @@ Available query filters: - `GitHub Copilot (VS Code)` - `GitHub Copilot (CLI)` - `Kilo Code` + - `Roo Code` - `Coder Agents` - `Mux` - `Cursor` - `OpenCode` + - `Charm Crush` + - `Junie` - `Unknown`
diff --git a/docs/manifest.json b/docs/manifest.json index c4aa0ee5549cb..41f8669b0c992 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1267,6 +1267,12 @@ "path": "./ai-coder/ai-gateway/clients/jetbrains.md", "state": ["premium"] }, + { + "title": "Junie", + "description": "Configure Junie CLI to route requests through AI Gateway with a custom model profile.", + "path": "./ai-coder/ai-gateway/clients/junie.md", + "state": ["premium"] + }, { "title": "Zed", "description": "Configure the Zed editor's language models to route requests through AI Gateway.", diff --git a/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx b/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx index 9bfd2914830ec..575e586b81207 100644 --- a/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx +++ b/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx @@ -84,6 +84,13 @@ export const AIBridgeClientIcon = ({ className={cn(iconClassName, className)} /> ); + case "Junie": + return ( + + ); case "GitHub Copilot (VS Code)": case "GitHub Copilot (CLI)": return ( diff --git a/site/src/theme/icons.json b/site/src/theme/icons.json index dcf8a0194284c..402ffcb6e957d 100644 --- a/site/src/theme/icons.json +++ b/site/src/theme/icons.json @@ -79,6 +79,7 @@ "jetbrains-toolbox.svg", "jetbrains.svg", "jfrog.svg", + "junie.svg", "jupyter.svg", "k8s.png", "k8s.svg", diff --git a/site/static/icon/junie.svg b/site/static/icon/junie.svg new file mode 100644 index 0000000000000..4f93979c56870 --- /dev/null +++ b/site/static/icon/junie.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + From 78898a8f126875cd2f112f5cca2df6eccff8e268 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Tue, 18 Aug 2026 15:05:02 +0000 Subject: [PATCH 2/5] docs: state the tested Junie API types --- docs/ai-coder/ai-gateway/clients/index.md | 2 +- docs/ai-coder/ai-gateway/clients/junie.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ai-coder/ai-gateway/clients/index.md b/docs/ai-coder/ai-gateway/clients/index.md index cc372c1df6f72..a4bddc4cda526 100644 --- a/docs/ai-coder/ai-gateway/clients/index.md +++ b/docs/ai-coder/ai-gateway/clients/index.md @@ -50,7 +50,7 @@ The table below shows tested AI clients and their compatibility with AI Gateway. | [Kilo Code](./kilo-code.md) | ✅ | ✅ | ❌ | | | [VS Code](./vscode.md) | ✅ | ✅ | ❌ | VS Code 1.122+ via Custom Endpoint provider. GitHub sign-in not required. Inline suggestions still require GitHub Copilot. | | [JetBrains IDEs](./jetbrains.md) | ✅ | ❌ | ❌ | Works in Chat mode via [third-party model configuration](https://www.jetbrains.com/help/ai-assistant/use-custom-models.html#provide-your-own-api-key). | -| [Junie](./junie.md) | ✅ | ✅ | ✅ | Junie CLI via custom model profiles. Use the `OpenAIResponses` API type. | +| [Junie](./junie.md) | ✅ | ✅ | ✅ | Junie CLI via custom model profiles. | | [Zed](./zed.md) | ✅ | ✅ | ❌ | | | [GitHub Copilot](./copilot.md) | ⚙️ | - | - | Requires [AI Gateway Proxy](../ai-gateway-proxy/index.md). Uses per-user GitHub tokens. | | Devin Desktop (formerly Windsurf) | ❌ | ❌ | ❌ | No option to override base URL. | diff --git a/docs/ai-coder/ai-gateway/clients/junie.md b/docs/ai-coder/ai-gateway/clients/junie.md index f71ea048c3100..6190dfe30789f 100644 --- a/docs/ai-coder/ai-gateway/clients/junie.md +++ b/docs/ai-coder/ai-gateway/clients/junie.md @@ -6,7 +6,11 @@ [Junie CLI](https://junie.jetbrains.com/docs/junie-cli.html) supports AI Gateway through [custom model profiles](https://www.jetbrains.com/help/junie/custom-llm-models.html). -Both the OpenAI and Anthropic endpoints are supported. + +| Provider | API type | Endpoint | +|-----------|-------------------|--------------------------| +| OpenAI | `OpenAIResponses` | `/openai/v1/responses` | +| Anthropic | `Anthropic` | `/anthropic/v1/messages` | For Junie inside JetBrains IDEs, refer to [JetBrains IDEs](./jetbrains.md). @@ -38,10 +42,6 @@ Create `~/.junie/models/ai-gateway.json` for the OpenAI endpoint: } ``` -> [!IMPORTANT] -> Use the `OpenAIResponses` API type. Junie sends a `stop` sequence on the Chat -> Completions API, which OpenAI reasoning models reject. - Or for the Anthropic endpoint: ```json From b5e7aa95f60d634d364aa7888ead847ddad10690 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Tue, 18 Aug 2026 15:08:14 +0000 Subject: [PATCH 3/5] chore(site/static/icon): scale junie icon to 256x256 --- site/static/icon/junie.svg | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/site/static/icon/junie.svg b/site/static/icon/junie.svg index 4f93979c56870..537d1cd663aad 100644 --- a/site/static/icon/junie.svg +++ b/site/static/icon/junie.svg @@ -1,17 +1,5 @@ - - - - - - - - - - - - - - - - + + + + From 73dc98a530bfb4ffc70b22fd11514f141fa46776 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Tue, 18 Aug 2026 16:06:40 +0000 Subject: [PATCH 4/5] fix(aibridge): match only the observed Junie user agent prefix Only 'junie:' has been observed from the Junie CLI. Drop the 'junie/' prefix, which was an unverified guess. --- aibridge/client.go | 2 +- aibridge/client_test.go | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/aibridge/client.go b/aibridge/client.go index 08516e28d1395..836c09c4b0bfd 100644 --- a/aibridge/client.go +++ b/aibridge/client.go @@ -59,7 +59,7 @@ func GuessClient(r *http.Request) Client { return ClientCursor case strings.HasPrefix(userAgent, "opencode/"): return ClientOpenCode - case strings.HasPrefix(userAgent, "junie:") || strings.HasPrefix(userAgent, "junie/"): + case strings.HasPrefix(userAgent, "junie:"): return ClientJunie } return ClientUnknown diff --git a/aibridge/client_test.go b/aibridge/client_test.go index d9062c47f173e..cde72029a16c4 100644 --- a/aibridge/client_test.go +++ b/aibridge/client_test.go @@ -109,11 +109,6 @@ func TestGuessClient(t *testing.T) { userAgent: "Junie:SNAPSHOT", wantClient: aibridge.ClientJunie, }, - { - name: "junie_cli_versioned", - userAgent: "Junie/2651.6", - wantClient: aibridge.ClientJunie, - }, { name: "unknown_client", userAgent: "ccclaude-cli/calude-with-wrong-prefix", From c1d7eafb015a6acf85161e60052119d23f1f71e2 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 19 Aug 2026 09:29:18 +0000 Subject: [PATCH 5/5] docs(docs/ai-coder/ai-gateway/clients): use junie.jetbrains.com reference links --- docs/ai-coder/ai-gateway/clients/junie.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ai-coder/ai-gateway/clients/junie.md b/docs/ai-coder/ai-gateway/clients/junie.md index 6190dfe30789f..9d9fd5d752141 100644 --- a/docs/ai-coder/ai-gateway/clients/junie.md +++ b/docs/ai-coder/ai-gateway/clients/junie.md @@ -5,7 +5,7 @@ > included with a Premium license. [Junie CLI](https://junie.jetbrains.com/docs/junie-cli.html) supports AI Gateway -through [custom model profiles](https://www.jetbrains.com/help/junie/custom-llm-models.html). +through [custom model profiles](https://junie.jetbrains.com/docs/custom-llm-models.html#using-custom-models). | Provider | API type | Endpoint | |-----------|-------------------|--------------------------| @@ -124,4 +124,4 @@ Reference the deployment URL in the profile with `${data.coder_workspace.me.access_url}/api/v2/ai-gateway/openai/v1/responses` if you generate the file from the template instead of committing it. -**References:** [Junie custom LLMs](https://www.jetbrains.com/help/junie/custom-llm-models.html) +**References:** [Junie custom LLMs](https://junie.jetbrains.com/docs/custom-llm-models.html#using-custom-models)