diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2dd3b81f..b970869d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,35 @@
+# v0.5.45 (2026-07-30)
+
+## Features
+- **Providers**: add Poolside (OpenAI-compatible)
+- **Providers**: add api-airforce, baidu, bazaarlink, bluesminds, kilo-gateway, llm7, morph, sambanova, tencent
+- **OAuth**: zed / trae / windsurf providers + harden callback proxies
+- **CLI tools**: set Claude Code max context tokens
+- **Qoder**: PAT auth + refresh model list
+- **Gemini**: Gemini 3.6 Flash tier routing + Gemini 3.5 Flash Lite
+- **Claude**: bump default Opus to `claude-opus-5`
+- **Kiro**: add Claude Opus 5 models
+- **Usage**: Kimi and DeepSeek usage handlers
+- **Usage**: SuperGrok weekly pool via gRPC-web
+
+## Fixes
+- **Refresh**: rotate `refresh_token` between retry attempts
+- **Kiro**: canonicalize tool history and route API keys correctly
+- **Kiro**: normalize dashboard thinking intensity models
+- **Cursor**: stop leaking agent tool errors as text
+- **Gemini**: fill empty tool schemas after `$ref` strip
+- **Antigravity**: strip `stream_options` from non-stream requests
+- **Jina-reader**: recover after transient errors, use JSON POST API
+- **Usage**: record exact embedding tokens
+- **Tunnel**: preserve successor cloudflared PID
+- **Console-log**: initialize capture at server boot + prevent SSE proxy buffering
+- **Dashboard**: count dual-auth, free-tier OAuth and API-key connections correctly
+- **Dashboard**: flex quota rows, thin global scrollbars, no hidden-row overflow
+
+## Docs
+- **i18n**: expand pt-BR translation to 986 terms
+- README: Indonesian translation
+
# v0.5.40 (2026-07-20)
## Features
diff --git a/cli/package.json b/cli/package.json
index cb24c4f0..efb786e4 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "9router",
- "version": "0.5.40",
+ "version": "0.5.45",
"description": "9Router CLI - Start and manage 9Router server",
"bin": {
"9router": "./cli.js"
diff --git a/open-sse/executors/devin-cli.js b/open-sse/executors/devin-cli.js
index dd452f9b..7cb35ff1 100644
--- a/open-sse/executors/devin-cli.js
+++ b/open-sse/executors/devin-cli.js
@@ -32,26 +32,34 @@ function resolveDevinBin() {
const envBin = process.env.CLI_DEVIN_BIN?.trim();
if (envBin) return envBin;
- // 2. Common name (PATH lookup handled by spawn shell option)
const isWin = process.platform === "win32";
-
- // 3. Windows installer default: %LOCALAPPDATA%\devin\cli\bin\devin.exe
- if (isWin) {
- const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
- const winPath = path.join(localAppData, "devin", "cli", "bin", "devin.exe");
- if (fs.existsSync(winPath)) return winPath;
- }
-
- // 4. Linux/macOS installer paths
const home = os.homedir();
- for (const candidate of [
- path.join(home, ".local", "share", "devin", "bin", "devin"),
- path.join(home, ".devin", "bin", "devin"),
- ]) {
+
+ // 2. Known installer / package-manager locations. spawn uses shell:false on
+ // macOS/Linux, so process.env.PATH alone may miss ~/.local/bin, Homebrew,
+ // Scoop, etc. when the server runs detached (tray/daemon/launchd) without
+ // a login shell — probe these explicitly before falling back to PATH.
+ const candidates = isWin
+ ? [
+ // Official installer: %LOCALAPPDATA%\devin\cli\bin\devin.exe
+ path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "devin", "cli", "bin", "devin.exe"),
+ path.join(home, ".local", "bin", "devin.exe"),
+ path.join(home, "scoop", "shims", "devin.exe"),
+ path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "Programs", "devin", "devin.exe"),
+ ]
+ : [
+ path.join(home, ".local", "share", "devin", "bin", "devin"),
+ path.join(home, ".devin", "bin", "devin"),
+ path.join(home, ".local", "bin", "devin"), // pipx / user install
+ "/opt/homebrew/bin/devin", // Homebrew (Apple Silicon)
+ "/usr/local/bin/devin", // Homebrew (Intel) / manual
+ "/usr/bin/devin",
+ ];
+ for (const candidate of candidates) {
if (fs.existsSync(candidate)) return candidate;
}
- // Fallback — rely on PATH
+ // 3. Fallback — rely on process.env.PATH
return isWin ? "devin.exe" : "devin";
}
diff --git a/open-sse/providers/capabilities.js b/open-sse/providers/capabilities.js
index d999278d..526ceb9b 100644
--- a/open-sse/providers/capabilities.js
+++ b/open-sse/providers/capabilities.js
@@ -174,11 +174,10 @@ export const PROVIDER_CAPABILITIES = {
"deepseek-v4-flash": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 1000000, maxOutput: 50000 },
"deepseek-v3-2-volc": { reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 96000, maxOutput: 32000 },
},
- // Poolside Laguna — OpenAI-compatible, all reasoning-capable (262K context, 32K max output).
+ // Poolside Laguna — OpenAI-compatible, all reasoning-capable (32K max output).
"poolside": {
- "laguna-s-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 262000, maxOutput: 32000 },
- "laguna-xs-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 262000, maxOutput: 32000 },
- "laguna-m.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 262000, maxOutput: 32000 },
+ "laguna-s-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 1000000, maxOutput: 32000 },
+ "laguna-xs-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 32000 },
},
};
@@ -302,6 +301,13 @@ export const PATTERN_CAPABILITIES = [
{ pattern: "*pplx*", caps: { search: true, contextWindow: 128000 } },
{ pattern: "*perplexity*", caps: { search: true, contextWindow: 128000 } },
+ // ── Poolside Laguna (resellers: openrouter/nvidia/kilocode/vercel/...) ──
+ // Free tiers cap S 2.1 well below the paid 1M window → match the free suffix
+ // (":free" or "-free", depending on reseller) before the plain id.
+ { pattern: "*laguna-s-2.1*free*", caps: { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 32000 } },
+ { pattern: "*laguna-s-2.1*", caps: { reasoning: true, thinkingFormat: "openai", contextWindow: 1000000, maxOutput: 32000 } },
+ { pattern: "*laguna*", caps: { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 32000 } },
+
// ── Others ───────────────────────────────────────────────────────
{ pattern: "*hunyuan*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } },
{ pattern: "hy3*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } },
diff --git a/open-sse/providers/registry/devin-cli.js b/open-sse/providers/registry/devin-cli.js
index 3ff062fe..09391649 100644
--- a/open-sse/providers/registry/devin-cli.js
+++ b/open-sse/providers/registry/devin-cli.js
@@ -3,6 +3,7 @@ export default {
alias: "dv",
aliases: ["devin"],
uiAlias: "dv",
+ hidden: true,
display: {
name: "Devin CLI",
icon: "smart_toy",
@@ -11,7 +12,7 @@ export default {
website: "https://devin.ai",
notice: {
signupUrl: "https://cli.devin.ai",
- text: "Install the Devin CLI and run `devin auth login` first. Spawns the `devin` binary via ACP/stdio — no API key field. Uses the default agent with built-in fs/shell tools (DEVIN_PERMISSION_MODE=bypass). Local use only. Set CLI_DEVIN_AGENT_TYPE=summarizer for a tool-less mode.",
+ text: "Install: `curl -fsSL https://cli.devin.ai/install.sh | bash` (macOS: `brew install --cask devin-cli`, Windows PowerShell: `irm https://static.devin.ai/cli/setup.ps1 | iex`). Then run `devin auth login`. No API key needed.",
},
},
category: "free",
diff --git a/open-sse/providers/registry/index.js b/open-sse/providers/registry/index.js
index 8d29e2cb..7bfdd0d7 100644
--- a/open-sse/providers/registry/index.js
+++ b/open-sse/providers/registry/index.js
@@ -113,7 +113,7 @@ import p110 from "./llm7.js";
import p111 from "./sambanova.js";
import p112 from "./tencent.js";
import p113 from "./morph.js";
-import p114 from "./devin-cli.js";
+// import p114 from "./devin-cli.js";
// import p104 from "./windsurf.js";
import p115 from "./poolside.js";
@@ -230,7 +230,7 @@ export default [
p111,
p112,
p113,
- p114,
+ // p114, // devin-cli — hidden, spawns local agent with shell/fs access
// p104, // windsurf — hidden, no tool calling
p115,
];
diff --git a/open-sse/providers/registry/poolside.js b/open-sse/providers/registry/poolside.js
index 462bd3f4..02882ad9 100644
--- a/open-sse/providers/registry/poolside.js
+++ b/open-sse/providers/registry/poolside.js
@@ -26,6 +26,5 @@ export default {
models: [
{ id: "poolside/laguna-s-2.1", name: "Laguna S 2.1" },
{ id: "poolside/laguna-xs-2.1", name: "Laguna XS 2.1" },
- { id: "poolside/laguna-m.1", name: "Laguna M.1" },
],
};
diff --git a/package.json b/package.json
index dc89873d..fbedad57 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "9router-app",
- "version": "0.5.40",
+ "version": "0.5.45",
"description": "9Router web dashboard",
"private": true,
"scripts": {
diff --git a/src/app/api/cli-tools/devin-settings/route.js b/src/app/api/cli-tools/devin-settings/route.js
index 1679ef92..240df0a8 100644
--- a/src/app/api/cli-tools/devin-settings/route.js
+++ b/src/app/api/cli-tools/devin-settings/route.js
@@ -13,15 +13,25 @@ const execAsync = promisify(exec);
// matches what the runtime actually spawns.
const candidateDevinPaths = () => {
const home = os.homedir();
- const paths = [
- path.join(home, ".local", "share", "devin", "bin", "devin"),
- path.join(home, ".devin", "bin", "devin"),
- ];
- if (process.platform === "win32") {
- const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
- paths.push(path.join(localAppData, "devin", "cli", "bin", "devin.exe"));
- }
- return paths;
+ const isWin = os.platform() === "win32";
+ const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
+ // Mirror resolveDevinBin in the executor — cover installer + common
+ // package-manager locations so detection matches runtime resolution.
+ return isWin
+ ? [
+ path.join(localAppData, "devin", "cli", "bin", "devin.exe"),
+ path.join(home, ".local", "bin", "devin.exe"),
+ path.join(home, "scoop", "shims", "devin.exe"),
+ path.join(localAppData, "Programs", "devin", "devin.exe"),
+ ]
+ : [
+ path.join(home, ".local", "share", "devin", "bin", "devin"),
+ path.join(home, ".devin", "bin", "devin"),
+ path.join(home, ".local", "bin", "devin"),
+ "/opt/homebrew/bin/devin",
+ "/usr/local/bin/devin",
+ "/usr/bin/devin",
+ ];
};
const checkDevinInstalled = async () => {
diff --git a/src/shared/components/Sidebar.js b/src/shared/components/Sidebar.js
index ef4fcba8..cded1824 100644
--- a/src/shared/components/Sidebar.js
+++ b/src/shared/components/Sidebar.js
@@ -302,9 +302,26 @@ export default function Sidebar({ onClose }) {
computer
- Remote
+ 9Remote
+ {/* 9English */}
+
+
+ translate
+
+ 9English
+
+
{/* Settings */}