feat: AI Router per session; Settings at the sidebar's foot; shipped tool icons

AI Router is a session's choice, not a workspace's:
- an AI Router select beside the model picker in the workspace home and a
  pane's run form (RouterPicker.tsx; hidden off Claude/Codex or with only
  Native; last pick remembered in agenthub.session.router)
- `router` on POST /api/lanes/:id/start and POST /api/run; Claude gets
  --settings=<data dir>/router-settings/<name>.json (0600, outranks project
  and user settings, key never in argv), Codex -p <name> + key via tmux -e
- dashboard_runs.router; a resume naming none goes back to it
- profile PATCH rewrites the session files, DELETE removes them
- gone: POST /api/lanes/:id/router/apply, LaneRouterRow, the Add-workspace
  router radio, the claude-/codex-home `routers` answers; clearLaneRouters
  takes the old keys out of each workspace's settings.local.json at start

Sidebar: Settings pinned to the foot, the collapse toggle beside it (stacked
on the rail) instead of in the brand's corner.

Icons: shipped SVG marks for claude, codex, openclaw and hermes (a
black/white pair swapped by theme) in client/public/ai-tools/, used by
AiToolIcon before the server's favicon.
This commit is contained in:
2026-10-02 14:19:02 +07:00
parent 5084334d81
commit c507d37baf
55 changed files with 851 additions and 1992 deletions
+1 -14
View File
@@ -40,23 +40,15 @@
"addLaneRepoHintAdopt": "An existing directory. The dashboard tracks it as-is — no worktree, no new branch.",
"addLaneRepoLabel": "Source repository",
"addLaneRepoLabelAdopt": "Directory",
"addLaneRouterCustom": "AI Router",
"addLaneRouterEmptyHint": "No router profile saved yet — add one under Settings › AI Router.",
"addLaneRouterHint": "Native talks to the vendor's own API. An AI Router profile is written into this workspace's own config when it is created.",
"addLaneRouterLabel": "Model endpoint",
"addLaneRouterLoading": "Loading router profiles…",
"addLaneRouterProfileLabel": "Router profile",
"addLaneSetupClose": "Close",
"addLaneSetupMcp": "MCP servers",
"addLaneSetupProfile": "Profile",
"addLaneSetupRouter": "Model endpoint",
"addLaneSetupTitle": "Setup",
"addLaneTitleLabel": "Title",
"addLaneTitlePlaceholder": "Optional",
"addLaneToolDefaultBadge": "Default",
"addLaneNoTools": "No AI tool is installed and enabled.",
"addLaneNoToolsLink": "Open Settings → AI Tools",
"addLaneRouterNativeTool": "Native ({{tool}})",
"addLaneToolHint": "Which CLI drives this workspace. Cannot be changed later.",
"addLaneWorktreeDetached": "(detached)",
"addLaneWorktreesMore": "+{{count}} more",
@@ -222,13 +214,8 @@
"proof.ticketReport": "Task report",
"renameLabel": "Rename this workspace",
"reorderHint": "Drag to reorder · Alt+←/→",
"router.applied": "Applied.",
"router.native": "Native ({{tool}})",
"router.nativeOnly": "{{tool}} runs on its own settings — AI Router profiles work with Claude Code and Codex only.",
"router.appliedRestart": "Applied, but the session running in this workspace still uses the old models — restart it to take effect.",
"router.label": "Model",
"router.restart": "Restart",
"router.restarted": "Session restarted with the new router.",
"router.label": "AI Router",
"runtime.boot": "▶ up",
"runtime.busy.down": "stopping…",
"runtime.busy.up": "booting…",
+2 -5
View File
@@ -67,8 +67,7 @@
"claudeHome": {
"title": "CLAUDE_HOME",
"description": "Root directory for Claude Code session data. Changing this updates the scan path for transcripts and imports immediately.",
"placeholder": "/path/to/claude-home",
"routersMissing": "Saved. These workspaces name an AI Router profile the new home does not have, and start on Native until it is added there or they are re-pointed: {{lanes}}"
"placeholder": "/path/to/claude-home"
},
"hooks": {
"title": "Hook Configuration",
@@ -510,7 +509,6 @@
"cancel": "Cancel",
"removeConfirm": "Remove the profile \"{{label}}\"? Every workspace using it is reset to the vendor's own API right away — on disk and in the dashboard.",
"loading": "Loading profiles…",
"lanesFailed": "Saved, but {{count}} workspace could not be updated and still carries the old endpoint: {{lanes}}",
"test": "Test",
"testing": "Testing…",
"testOk": "The endpoint answered — {{model}} is reachable with this key.",
@@ -589,8 +587,7 @@
"title": "CODEX_HOME",
"description": "Root directory for Codex config and session journals. The dashboard reads it, writes AI Router layers into it, and starts every codex workspace with it.",
"restartNote": "A codex workspace already running keeps the home it started with, and shows as idle, until it is restarted.",
"placeholder": "/path/to/codex-home",
"routersFailed": "Saved, but the AI Router layer could not be re-written for: {{lanes}}"
"placeholder": "/path/to/codex-home"
},
"codexHooks": {
"title": "Codex hooks",
+1 -14
View File
@@ -40,23 +40,15 @@
"addLaneRepoHintAdopt": "Một thư mục có sẵn. Dashboard theo dõi nguyên trạng — không tạo worktree, không tạo nhánh mới.",
"addLaneRepoLabel": "Repo nguồn",
"addLaneRepoLabelAdopt": "Thư mục",
"addLaneRouterCustom": "AI Router",
"addLaneRouterEmptyHint": "Chưa lưu profile router nào — thêm ở Settings › AI Router.",
"addLaneRouterHint": "Native là gọi thẳng API của hãng. Chọn profile AI Router thì cấu hình được ghi vào config riêng của workspace này lúc tạo.",
"addLaneRouterLabel": "Endpoint model",
"addLaneRouterLoading": "Đang tải danh sách router…",
"addLaneRouterProfileLabel": "Profile router",
"addLaneSetupClose": "Đóng",
"addLaneSetupMcp": "MCP server",
"addLaneSetupProfile": "Profile",
"addLaneSetupRouter": "Endpoint model",
"addLaneSetupTitle": "Thiết lập",
"addLaneTitleLabel": "Tiêu đề",
"addLaneTitlePlaceholder": "Không bắt buộc",
"addLaneToolDefaultBadge": "Mặc định",
"addLaneNoTools": "Chưa có AI tool nào được cài và bật.",
"addLaneNoToolsLink": "Mở Cài đặt → AI Tools",
"addLaneRouterNativeTool": "Native ({{tool}})",
"addLaneToolHint": "CLI nào điều khiển workspace này. Không thể thay đổi sau này.",
"addLaneWorktreeDetached": "(detached)",
"addLaneWorktreesMore": "+{{count}} nữa",
@@ -222,13 +214,8 @@
"proof.ticketReport": "Báo cáo nhiệm vụ",
"renameLabel": "Đổi tên workspace này",
"reorderHint": "Kéo để sắp xếp · Alt+←/→",
"router.applied": "Đã áp dụng.",
"router.native": "Native ({{tool}})",
"router.nativeOnly": "{{tool}} chạy bằng cấu hình riêng — hồ sơ AI Router chỉ dùng được với Claude Code và Codex.",
"router.appliedRestart": "Đã áp dụng, nhưng phiên đang chạy trong workspace này vẫn dùng model cũ — khởi động lại phiên để có hiệu lực.",
"router.label": "Model",
"router.restart": "Khởi động lại",
"router.restarted": "Đã khởi động lại phiên với router mới.",
"router.label": "AI Router",
"runtime.boot": "▶ chạy",
"runtime.busy.down": "đang dừng…",
"runtime.busy.up": "đang khởi động…",
+2 -5
View File
@@ -67,8 +67,7 @@
"claudeHome": {
"title": "CLAUDE_HOME",
"description": "Thư mục gốc chứa dữ liệu phiên Claude Code. Thay đổi sẽ cập nhật ngay đường dẫn quét cho transcript và import.",
"placeholder": "/đường-dẫn/tới/claude-home",
"routersMissing": "Đã lưu. Các workspace này dùng một hồ sơ AI Router mà thư mục mới không có, nên sẽ chạy Native cho đến khi bạn thêm hồ sơ đó ở đây hoặc chọn lại router cho workspace: {{lanes}}"
"placeholder": "/đường-dẫn/tới/claude-home"
},
"hooks": {
"title": "Cấu hình hook",
@@ -510,7 +509,6 @@
"cancel": "Huỷ",
"removeConfirm": "Xoá profile \"{{label}}\"? Mọi workspace đang dùng nó sẽ được đưa về API gốc của hãng ngay lập tức — cả trên đĩa lẫn trong dashboard.",
"loading": "Đang tải danh sách profile…",
"lanesFailed": "Đã lưu, nhưng {{count}} workspace không cập nhật được và vẫn đang giữ endpoint cũ: {{lanes}}",
"test": "Kiểm tra",
"testing": "Đang kiểm tra…",
"testOk": "Endpoint đã trả lời — {{model}} dùng được với key này.",
@@ -589,8 +587,7 @@
"title": "CODEX_HOME",
"description": "Thư mục gốc chứa config và nhật ký phiên của Codex. Dashboard đọc từ đây, ghi layer AI Router vào đây, và mở mọi workspace codex với thư mục này.",
"restartNote": "Workspace codex đang chạy vẫn dùng thư mục cũ, và hiện là idle, cho tới khi được khởi động lại.",
"placeholder": "/đường-dẫn/tới/codex-home",
"routersFailed": "Đã lưu, nhưng không ghi lại được layer AI Router cho: {{lanes}}"
"placeholder": "/đường-dẫn/tới/codex-home"
},
"codexHooks": {
"title": "Hook của Codex",