feat: v2.5.5 — AI Router renamed AI Proxy, reload onto new builds, update no longer spins
AI Proxy (was AI Router): - server/lib/ai-proxies.js, proxy-apply.js, routes/ai-proxies.js mounted at /api/ai-proxies and the old /api/ai-routers; `router` still accepted on the start routes; profiles move to <CLAUDE_HOME>/agenthub-proxies/ - dashboard_runs.router renamed in place to proxy: an older build on the same DB fails on that column, so dev and prod sharing one DB update together - a proxy's baseUrl stored as its root (a typed /v1 made /v1/v1/messages); Test probes Claude /v1/messages and Codex /v1/responses - Settings → AI Proxy (/settings/ai-router redirects), ProxyPicker Updates: - every tab reloads onto the new UI build when its WebSocket reconnects to a server of another version (lib/newBuildReload.ts, once per version) - Update now stops waiting on a plugin chain that failed or pulled nothing (the old server never goes down), and reloads as soon as a new version answers Also: - a pane whose command exits at once says so (EPANEEXITED) instead of "Command failed: tmux set-option … mouse on" - the pane drag preview draws where the pane actually lands
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
* `ConsoleTabs.tsx`). Inside
|
||||
* the box, like Claude Desktop's: AI (the session's own tool, the workspace's
|
||||
* by default) and one Model chip (`ModelMenu`: the tool's own list or the
|
||||
* router's models, plus Effort); under it the quieter choices — AI Router
|
||||
* proxy's models, plus Effort); under it the quieter choices — AI Proxy
|
||||
* (Native or a profile), Permission (Claude's mode, Codex's sandbox/approval,
|
||||
* a catalog tool's bypass) and Local / New worktree — the workspace's Recents — the sessions it
|
||||
* owns and has not archived (`?lane=&archived=0`, with each one's state dot,
|
||||
@@ -58,7 +58,7 @@ import { useAiTools } from "../../hooks/useAiTools";
|
||||
import { useProjectTreePrefs } from "../../lib/projectTreePrefs";
|
||||
import { ModelMenu, PromptEditor, promptWithImages, type PromptImage } from "../run/RunSetup";
|
||||
import { AiToolIcon } from "../AiToolIcon";
|
||||
import { RouterPicker, routerArg, useAiRouters, useSessionRouter } from "../run/RouterPicker";
|
||||
import { ProxyPicker, proxyArg, useAiProxies, useSessionProxy } from "../run/ProxyPicker";
|
||||
import { Select } from "../Select";
|
||||
import { MarkdownPreview } from "../MarkdownPreview";
|
||||
import { WorkspaceRowMenu, type WorkspaceMenuAction } from "./WorkspaceRowMenu";
|
||||
@@ -143,25 +143,25 @@ function HomeBody({
|
||||
}
|
||||
}, [draftKey, prompt]);
|
||||
const [images, setImages] = useState<PromptImage[]>([]);
|
||||
const [router, setRouter] = useSessionRouter();
|
||||
const routers = useAiRouters();
|
||||
// Claude and Codex, also before the catalog has loaded (as RouterPicker).
|
||||
const routerCapable = entry?.routerCapable ?? (claude || codex);
|
||||
const [proxy, setProxy] = useSessionProxy();
|
||||
const proxies = useAiProxies();
|
||||
// Claude and Codex, also before the catalog has loaded (as ProxyPicker).
|
||||
const proxyCapable = entry?.proxyCapable ?? (claude || codex);
|
||||
// A profile picked for a tool that can use one; its models replace the tool's own.
|
||||
const profile = routerCapable
|
||||
? (routers?.find((r) => r.name === router && !r.builtin) ?? null)
|
||||
const profile = proxyCapable
|
||||
? (proxies?.find((r) => r.name === proxy && !r.builtin) ?? null)
|
||||
: null;
|
||||
const routerModels: ModelChoice[] | undefined = profile
|
||||
const proxyModels: ModelChoice[] | undefined = profile
|
||||
? [
|
||||
...new Set(
|
||||
[profile.model, profile.models.opus, profile.models.sonnet, profile.models.haiku].filter(
|
||||
Boolean
|
||||
(m): m is string => !!m
|
||||
)
|
||||
),
|
||||
].map((id) => ({ id, label: id }))
|
||||
: undefined;
|
||||
// The tool's default model (also when the catalog arrives after mount); on
|
||||
// a router, the router's own default. A new tool or router drops the pick.
|
||||
// a proxy, the proxy's own default. A new tool or proxy drops the pick.
|
||||
const defaultModel = profile ? "" : (entry?.defaultModel ?? "");
|
||||
const [model, setModel] = useState(defaultModel);
|
||||
useEffect(() => setModel(defaultModel), [defaultModel, tool, profile?.name]);
|
||||
@@ -319,7 +319,7 @@ function HomeBody({
|
||||
// Only a different tool is named: the workspace's own keeps starting
|
||||
// even after it was disabled in Settings.
|
||||
...(tool !== lane.tool ? { tool } : {}),
|
||||
...routerArg(routerCapable, router),
|
||||
...proxyArg(proxyCapable, proxy),
|
||||
...(hasModel && model ? { model } : {}),
|
||||
...(claude ? { permissionMode: mode } : {}),
|
||||
...(codex ? { sandbox, approval } : {}),
|
||||
@@ -533,7 +533,7 @@ function HomeBody({
|
||||
value={model}
|
||||
onChange={setModel}
|
||||
tool={tool}
|
||||
choices={routerModels}
|
||||
choices={proxyModels}
|
||||
{...(hasEffort ? { effort, onEffortChange: setEffort } : {})}
|
||||
/>
|
||||
)}
|
||||
@@ -556,12 +556,12 @@ function HomeBody({
|
||||
data-testid="workspace-home-options"
|
||||
className="mt-1.5 flex flex-wrap items-center gap-0.5 px-1"
|
||||
>
|
||||
<RouterPicker
|
||||
<ProxyPicker
|
||||
variant="ghost"
|
||||
tool={tool}
|
||||
routers={routers}
|
||||
value={router}
|
||||
onChange={setRouter}
|
||||
proxies={proxies}
|
||||
value={proxy}
|
||||
onChange={setProxy}
|
||||
/>
|
||||
{claude && (
|
||||
<Select<PermissionMode>
|
||||
|
||||
Reference in New Issue
Block a user