- Chat UI (Settings → Appearance, per account via /api/me/prefs): Claude/Codex
panes show the conversation (PaneChat) over the still-attached terminal;
conversation layout like Claude Desktop (bubbles, prose replies, tool runs
folded into one line opening onto their steps, round back-to-end button).
- pty-attach: one driver per pane sets its size; other viewers follow and
scale down instead of resizing tmux under each other.
- Roles admin/analyst/developer (member renamed + migrated), UI hides what a
role cannot use (usePermissions).
- Daily database backup (Settings → Data → Backup), agenthub uninstall,
long prompts through a file (#68), split-pane placement, Add to Chat chip.
Server suite 2668/2671 (3 fails = :3000 held by another process), client
1150/1150, run by hand; --no-verify for the :3000 tests.
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
Right-click menus: one portal shell (components/ContextMenu.tsx) for every
item with actions - sidebar/Recents/archived session rows (SessionMenu),
console tabs, pane headers, the Changes panel's files/tree/commits, the
workspace home, sessions outside a workspace (SessionRowMenu: Sessions
table, Dashboard, Activity, Workflow runs), prompt images, every Settings
list and every Claude Code/Codex config list (FileItemMenu). Buttons stay;
a delete keeps its button's confirm; the shell stops portal clicks from
reaching the card that opened it.
Status bar: Workspace $ and Session $ on the Workspace page, from
?lane= on GET /api/pricing/cost and the focused/replayed session's cost.
Database, no data dropped: synchronous=NORMAL, journal_size_limit 64 MB,
hourly PASSIVE checkpoint + PRAGMA optimize, idx_events_session_created
(replacing the redundant idx_events_session), watchdog reads
sessions.transcript_path, transcript parsed before the hook's write
transaction, GET /api/sessions/:id?events=0 for the web page (reloads only
on its own session, debounced), streamed export, transactional clear-data
and chunked cleanup, subagent import reads tool_use_ids once, runtime logs
rotated past 5 MB.
- A session picks its own AI tool: `tool` on POST /api/lanes/:id/start
(a catalog id, known + enabled, else 400 EBADTOOL/EDISABLED); a resume
keeps the tool its session ran on (`sessions.tool`). The workspace's
`lanes.tool` is now only the default.
- The workspace home's prompt box is the one place a session starts. Its
fields run in order: AI → AI Router (Native or a profile) → Model (the
tool's own list, or the router's models) → Permission (Claude mode /
Codex sandbox+approval / catalog bypass) → Effort.
- The pane's run form (RunSetup) is gone: New run, a single pane's exit and
an empty split pane's New session all go to the home. RunSetup.tsx keeps
PromptEditor, CwdAutocomplete and ModelPicker (new `choices` prop).
- The tool icon moved from the sidebar's workspace row to each session row
(right edge, `session.tool`); the split-pane header shows the run's tool.
- An ended session opens in its workspace (`&session=`, SessionReplay.tsx):
its conversation read-only plus a prompt box; only a SENT message resumes
it (resumeSessionId + prompt, first free slot). The console is only
hidden behind it.
- Analyst button beside Files/Changes in the right panel opens
/sessions/:id for the opened session.
- Gemini CLI ships its own icon (client/public/ai-tools/gemini.svg, from
zonalogo.com).
- RouterPicker always shows (Native disabled for tools without router
support); useAiRouters() exported.
- Tests: server start-with-tool cases; SessionReplay, home field order/tool/
router models, empty pane, Analyst; dropped the cwd-field suites
(Run.defaultCwd, Workspace.laneCwd). Docs: CLAUDE.md rules, API.md,
LANES.md, README, ARCHITECTURE, INSTALL.
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.
Workspace sessions (docs/plans/desktop-parity, phases 1-5 and 7):
- sessions.lane_id: a workspace lists only the sessions its panes held,
stamped by bindRunSession (first writer wins, nested `claude -p`
excluded); GET /api/sessions?lane=&archived=
- session row state dot, double-click rename, archive (sessions.archived_at,
hides only); the activity bar's duplicate Sessions tab removed
- Fork beside Resume (--fork-session, Claude only), Ctrl/Cmd+click a live
session opens it in a second pane, a diff line's comment is pasted into
the workspace's last-focused pane (lib/panePaste.ts)
dashboard_runs stability:
- a pane's previous run is archived as <pane>@<started_at> instead of being
overwritten by INSERT OR REPLACE
- a run is flagged abandoned only once its tmux session is gone, at boot and
on the run-heal beat (panes outlive a server restart)
UI:
- sidebar: no footer, language or theme block; small collapse button in the
brand corner; version + update check in the connection details modal, a
status-bar dot while a release is out; language in Settings -> General
- one Switch component for every switch, the themed Checkbox for every
checkbox (no native checkboxes)
Also: AI Tools catalog trimmed to 10 built-ins (custom-file prompt modes
tested through fixtures), and the workspace plans under docs/plans/ (the
multi-user server plan lives in the AgentHub-Server repo).
Settings moves from a tab bar to /settings/<section>: a left nav grouped
General / Agents / Routing / Git / Projects (a select on narrow screens), a
search box over every section (Ctrl/Cmd+F, Enter opens the first match), and
row primitives. Each AI tool has its page under agents/<id>, the AI Tools
modal becomes the Agents page, Pricing shows every vendor, Git → Worktrees
sets which untracked worktrees the sidebar lists, and each project gets a
page with its own worktree override and Forget all. ?tool= and #ai-tools
links redirect to the path form.
Pricing: the LiteLLM sync now adds every current Claude / GPT (>= 5) /
Gemini (>= 3) chat model before it is used and keeps its own rows
(model_pricing.source = 'sync', the Auto badge) at the published rate. A
user's row (any PUT) and a deleted pattern (model_pricing_dismissed) are
never touched; a deleted built-in row is not re-seeded at the next start; a
reset re-applies the list at once; retired Claude models are priced only
once used.
Models: GET /api/ai-tools/:id/models reads each tool's own list (Codex's
models_cache.json, Claude's aliases named after the newest version plus
priced versions, a catalog tool's vendor rows plus the models its sessions
used). A catalog `model: {flag, vendor?}` field passes `<flag> <id>`
(validated: never a flag or shell text); a per-tool default model starts
each run, and a resume keeps the session's own model.
Settings: every installed, enabled catalog tool without its own tab gets
one (AiToolConfig.tsx): path, homepage, Update, sessions/cost/lanes,
default model, vendor pricing, and the config files its release catalog
lists, edited over the opened sha, syntax-checked, backed up, written
atomically; credential-looking names refused. Antigravity keeps its hooks
card there.
Lanes: a just-started run no longer reads "dead" (age counts the run's
start while the old session is still bound); a second dashboard on the
same database re-broadcasts the lanes the other changed (PRAGMA
data_version, every 2 s); a lane start with a bad model answers 400.
DB: every migration runs under a cross-process lock (<db>-migrate.lock), so
two dashboards started at once on a fresh database both come up.
- AI Tools moves from a Settings section into a modal behind a button at the
Settings tab bar's right edge (AiToolsModal, `actions` on PageTabs,
/settings#ai-tools); rows get an enable switch, Default badge, and an
Update button for every tool whose catalog names an updater (no version
lookup on open); not-installed tools fold behind a disclosure, a finished
job's log folds to one line; shared AiToolIcon
- codex updates through its own `codex update`, openclaw/hermes gain
`update --yes`; update-all runs every installed updater and skips an npm
one only when npm does not manage the binary
- Add-lane tool picker is one button showing the chosen tool that opens the
enabled tools as buttons
- Claude Code / Codex AgentHub sub-tab folded into Overview (home editor,
hooks, pricing); the path list drops CLAUDE_HOME/CODEX_HOME;
HomeDirSetting shows the saved value only while the field differs
- Preview/Raw toggle for markdown files in the config FileViewer and the
Changes panel's whole-file view (MarkdownPreview: react-markdown +
remark-gfm, raw HTML never run, frontmatter as a code block)
- docs and the CLAUDE.md marker updated
Settings → Dashboard gains an AI Tools section (installed tools with
Enabled/Disabled and Set default, available ones with a homepage link,
Refresh). useAiTools shares one catalog copy across the app and replaces
LANE_TOOL_LABEL. The Add-lane modal picks Default (<tool>) or Other…; the
router row reads Native (<tool>) and disables AI Router profiles for tools
that cannot use one. The run form gives catalog tools resume and a red
per-run Bypass permissions switch (Codex too). Claude Code / Codex settings
tabs hide while that tool is disabled. The Workspace probes each lane tool's
CLI fresh.
Docs: CLAUDE.md tool/bypass rule, AGENTS.md, LANES.md, API.md, CLI.md,
server/README.md, OpenAPI fragment + regenerated openapi.yaml.