feat: workspaces own their sessions; desktop parity; sidebar and settings cleanup

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).
This commit is contained in:
2026-10-02 12:05:00 +07:00
parent 5b269698f6
commit a6bd33bcff
121 changed files with 2699 additions and 1552 deletions
+128
View File
@@ -0,0 +1,128 @@
# Workspace = Claude-Desktop-style project (v2 of the workspace redesign)
_Replaces the Orca-shaped project → workspace → worktree tree from phase 1.
Three phases, each one commit on `main`, in order._
## What the user asked for
1. Merge the **Workflows** page into the Dashboard → **Analytics** tab.
2. The sidebar lists **workspaces**, each expanding to **its sessions only** —
like Claude Desktop's project list (name ›, `+` on the row, sessions under it).
3. A workspace opens on a **project home** like Claude Desktop's: title,
prompt box, Recents; on the right, **Instructions**, **Memory**,
**Context** (files/documents) and **Folder** (folders) — no Scheduled.
4. Clicking a session **opens its tmux pane** and never resumes it (a resume
re-reads the whole transcript and burns tokens).
5. The Orca parts are regrouped by workspace and kept where they still make sense.
## Phase A — Workflows inside Analytics
- `Dashboard.tsx` Analytics → the existing `workflow` sub-tab renders the whole
`Workflows` page body (pipelines, skill usage, Workflow-tool runs, DAG, error
map, complexity, compaction, drill-in) under its current small cards.
- `/workflows` → `<Navigate to="/?tab=analytics&sub=workflow">`; the sidebar
loses its **Workflows** item; `DocumentTitle`, i18n `nav.workflows` removed.
- No server change.
## Phase B — Sidebar: workspaces → sessions
```
WORKSPACES + 🔍 ⚙
agenthub › +
SG_MiniApp ⌄ +
● Tách thông tin official account (● = a live tmux pane)
○ Kiến trúc tenant đơn mở rộng
scratch › +
```
- A flat list in the stored drag order (`agenthub.workspace.laneOrder`, kept);
the repo grouping, the "Folders" pseudo-project, untracked worktrees and
Adopt leave the sidebar.
- A row's chevron expands its sessions (`GET /api/sessions?cwd=&tool=`, the
newest 20, the session's name or first prompt). A dot shows a live pane.
- Click the **name** → the workspace home (Phase C). Click `+` → the home with
the prompt focused. Click a **session**:
- its pane is alive (a `dashboard_runs` row with that `session_id`, the
tmux session `agenthub-lane-<id>[-slot]` still there) → the session view
attached to that pane — no new process, no resume;
- no live pane → the read-only transcript (`/sessions/:id`, Conversation tab),
with **Resume** as a separate, explicit button that warns about context cost.
- Kept: rename (double-click), drag / Alt+↑↓ reorder, right-click menu
(Start/Stop, Rename, router, readme, Copy path, Reset, Remove).
- Header: `+` new workspace (the Add modal), search (filters workspaces and
loaded session titles), a tool filter.
## Phase C — Workspace home + sources
Route: `/workspace?lane=<id>` = home; `/workspace?lane=<id>&run=<runId>` = the
session view (today's terminal, split panes and the Files/Changes panel,
unchanged — the one-render-site console rule still holds).
```
Workspaces / SG_MiniApp 📌 ⋯
┌────────────────────────────────────────────┐ ┌ Instructions ✎ ┐
│ How can I help you today? │ │ text… │
│ + [Claude ▾] [model ▾] [effort ▾] [Run]│ ├ Memory View ────┤
└────────────────────────────────────────────┘ │ CLAUDE.md, auto │
Recents ├ Context + ─┤
Tách thông tin official account 2h ago ● │ spec.pdf notes… │
Kiến trúc tenant đơn mở rộng Sep 29 ├ Folder + ─┤
│ 📁 SG_MiniApp │
│ 📁 sg-shared │
└──────────────────┘
```
- **Prompt box** = today's run form (tool's model picker, permission, effort,
bypass), compact. Run → a new pane, then the session view.
- **Recents** = the same session list as the sidebar, with time ago, cost and
the live dot.
- **Instructions** — one text per workspace, `lanes.instructions`. Reaches the
agent at spawn: Claude `--append-system-prompt <text>`; Codex
`-c developer_instructions=<text>` if 0.155 accepts it (verified while
building — otherwise Claude-only, shown as "not supported by <tool>").
Never written into the user's repo.
- **Memory** — read-only: the folder's `CLAUDE.md` / `AGENTS.md` and Claude's
auto-memory dir for that cwd (`<CLAUDE_HOME>/projects/<slug>/memory/`).
**View memory** opens them in the existing markdown viewer.
- **Context** — files the user uploads (pdf, md, txt, images…, ≤ 10 MB each),
stored by AgentHub under `<data dir>/workspace-context/<laneId>/` with a
server-chosen name. The directory goes to the agent as `--add-dir`, and the
appended instructions list the files, so the agent knows to read them.
- **Folder** — the workspace's own cwd (fixed) plus extra folders, each
`--add-dir` for Claude and Codex (both verified in `--help`); other tools show
them as unsupported. **+** opens a folder picker that also suggests the repo's
untracked worktrees (what is left of Adopt). A folder must exist, be a
directory and not be or contain the OS home (`validateHomeDir`).
- **⋯ menu** = the row menu plus what Settings → Projects held (facts,
worktree count, Forget).
### Schema (migration-safe)
- `lanes.instructions TEXT` (ADD COLUMN, null).
- `CREATE TABLE IF NOT EXISTS lane_sources (id INTEGER PRIMARY KEY, lane_id
INTEGER NOT NULL REFERENCES lanes(id) ON DELETE CASCADE, kind TEXT NOT NULL
CHECK (kind IN ('folder','file')), path TEXT NOT NULL, name TEXT NOT NULL,
size INTEGER, added_at TEXT NOT NULL)`.
- Removing a workspace deletes its rows and AgentHub's own context copies —
never a user folder.
### API
- `GET/PUT /api/lanes/:id/instructions`
- `GET /api/lanes/:id/sources`, `POST …/sources/folder {path}`,
`POST …/sources/file` (raw body, name header, size cap),
`DELETE …/sources/:sourceId` — all `sameOriginGuard`.
- `GET /api/lanes/:id/memory` → the memory files above, read-only.
- Spawn (`spawnRun`) reads instructions + sources from the lane row, never
from the request — the same rule as `tool`.
### Settings
- **Projects** group leaves Settings (now the workspace's ⋯ menu).
- **Worktrees** stays, renamed "Worktree suggestions": whether Add folder
suggests untracked worktrees and from which sources.
## Verification
Each phase: tests first for new logic, `npm run test:server`,
`npm run test:client`, headers, build, screenshots; docs + CLAUDE.md marker.