This merge adds two substantial features: a rooms and chat sidebar system (FN-3806) with a new `CreateRoomModal` and enriched `ChatView`, wired through plugin routes and a new `listTasksModifiedSince` core API; and a durable review addressing workflow (FN-3282) that persists snapshots, renders addre Fusion-Task-Id: FN-3282
Dashboard API route registrars
packages/dashboard/src/routes.ts remains the single public entrypoint (createApiRoutes(store, options)), but route definitions are registered by domain modules in this directory.
Shared context contract
All registrars receive ApiRoutesContext from ./types.ts, built by createApiRoutesContext() in ./context.ts.
Registrars should be typed as ApiRouteRegistrar so modules share one explicit registration contract.
The context centralizes cross-cutting dependencies so registrars preserve behavior without re-implementing plumbing.
Some registrars (for example register-task-workflow-routes.ts) also take a narrow dependency-injection object for non-context helpers that must stay source-of-truth in routes.ts (git helpers, background refresh helpers, multer upload middleware). This avoids helper duplication while preserving runtime parity.
The context provides core cross-cutting plumbing:
- Request/project scoping:
getProjectIdFromRequest,getScopedStore,getProjectContext- These are also exported from
context.tsas canonical helpers for future extraction tasks.
- These are also exported from
- Engine-aware fallback behavior for project-bound and root-store APIs
- Runtime loggers and diagnostics emitters (
runtimeLogger,planningLogger,chatLogger) - Proxy/auth/audit helpers (
emitRemoteRouteDiagnostic,emitAuthSyncAuditLog) - Automation/routine resolvers and scope parsing helpers
- Shared error normalization (
rethrowAsApiError)
Registrar module map
register-settings-memory-routes.ts— settings APIs and memory backend/file/insight routes (excluding node-to-node sync endpoints)register-project-routes.ts—/projectsCRUD +/projects/across-nodes,/projects/detect, health/config/pause/resume routesregister-node-routes.ts—/nodesCRUD + operational endpoints (/health-check,/metrics,/version,/sync-plugins,/compatibility)register-settings-sync-routes.ts— node settings/auth sync routes (/nodes/:id/settings*,/nodes/:id/auth/sync)register-mesh-routes.ts— mesh topology routes (/mesh/state,/mesh/sync)register-discovery-routes.ts— discovery routes (/discovery/status|start|stop|nodes|connect) withoptions?.centralCorereuseregister-settings-sync-inbound-routes.ts— inbound sync/auth endpoints (/settings/sync-receive,/settings/auth-receive,/settings/auth-export)register-settings-sync-helpers.ts— shared sync-domain helpers (fetchFromRemoteNode,readStoredAuthProvidersFromDisk)register-task-workflow-routes.ts— task/workflow domain (/tasks*,/documents, task comments/docs/checkout/spec/attachments, PR+issue status, task lifecycle/workflow endpoints)register-planning-subtask-routes.ts— planning sessions and subtask breakdown routesregister-chat-routes.ts— chat session/list/mutation/stream routesregister-messaging-scripts.ts— scripts API and mailbox/message routesregister-git-github.ts— git/GitHub workflows and related helpers- Git plumbing routes:
/git/remotes*,/git/status,/git/commits*,/git/branches*,/git/worktrees,/git/fetch|pull|push,/git/stashes*,/git/diff*,/git/changes,/git/stage|unstage|commit|discard - GitHub import/integration routes:
/github/issues/*,/github/pulls/*,/github/webhooks,/github/batch/status(includes shared batch-import rate limiter state + reset export) - Task-scoped GitHub routes:
/tasks/:id/pr/*and/tasks/:id/issue/*status/refresh/create flows
- Git plumbing routes:
register-model-routes.ts—/modelsendpoint, favorites projection, anduseClaudeClifiltering forpi-claude-clientriesregister-auth-routes.ts— auth/provider domain (/auth/status,/auth/login,/auth/logout,/auth/api-key,/auth/claude-cli,/providers/claude-cli/status)register-usage-routes.ts—/usageendpoint withfetchAllProviderUsage(options?.authStorage)integrationregister-files-terminal-workspaces.ts— infrastructure aggregator for file/workspace + session-diff + terminal routes- Calls
register-session-diff-routes.tsfirst (session changed files + task diff endpoints) - Calls
register-file-workspace-routes.tssecond (task/workspace file browsing and file operations) - Calls
register-terminal-routes.tslast (terminal command/session + PTY lifecycle endpoints)
- Calls
register-file-workspace-routes.ts— task/workspace file domain:- Task files:
/tasks/:id/files,/tasks/:id/files/{*filepath}(read/write) - Workspace discovery/files:
/workspaces,/files,/files/markdown-list,/files/search,/files/{*filepath} - File operations:
/files/{*filepath}/copy|move|delete|rename,/files/{*filepath}/download,/files/{*filepath}/download-zip - Generic wildcard write:
/files/{*filepath}(must remain after operation routes) - Project markdown search:
/project-files/md
- Task files:
register-session-diff-routes.ts— task session/diff domain:- Session changed-file list:
/tasks/:id/session-files - Aggregate task diff:
/tasks/:id/diff - Per-file diffs:
/tasks/:id/file-diffs - Caches: module-level
sessionFilesCacheandfileDiffsCache(10-second TTL)
- Session changed-file list:
resolve-diff-base.ts— shared git diff-base utilities:runGitCommand(args, cwd, timeoutMs)resolveDiffBase(task, cwd)+ResolveDiffBaseTaskInputtype
register-terminal-routes.ts— terminal execution and PTY endpoints:- Command execution + streaming:
/terminal/exec,/terminal/sessions/:id,/terminal/sessions/:id/stream,/terminal/sessions/:id/kill - PTY lifecycle:
/terminal/sessions(create/list) and/terminal/sessions/:id(delete)
- Command execution + streaming:
register-agent-core-routes.ts— core agent CRUD, lookups, stats/org-tree, hierarchy aliases (/agents/:id/children|employees)register-agent-runtime-routes.ts— agent runtime/control-plane, heartbeats/runs, access/permissions, soul/memory, revisions/budget/keys, task/inbox surfacesregister-agent-reflection-rating-routes.ts— reflection/performance/context endpoints and ratings APIsregister-agent-import-export-generation-routes.ts— agent import/export, companies catalog, and/agents/generate/*session/spec lifecycleregister-agent-skills-routes.ts— skills discovery/content/execution/catalog endpoints coupled to agent capability flowregister-plugins-automation.ts— plugin CRUD, automation, routines/webhooksregister-proxy-routes.ts— remote-node proxy forwarding and SSE proxy routes- Injected dependencies:
{ store, runtimeLogger } - Endpoint inventory (must remain in this order):
GET /proxy/:nodeId/healthGET /proxy/:nodeId/projectsGET /proxy/:nodeId/tasksGET /proxy/:nodeId/project-healthGET /proxy/:nodeId/events(SSE pass-through, 30s timeout, client-disconnect cleanup)ALL /proxy/:nodeId/{*splat}(generic wildcard forwarder)
- Shared diagnostics: imports
emitRemoteRouteDiagnosticandclassifyRemoteRouteErrorfromroutes/context.tsso proxy and non-proxy registrars (for example mesh/sync routes) keep one diagnostic classification contract.
- Injected dependencies:
createApiRoutes mount sequence (current)
createApiRoutes() mounts registrars in this precedence-sensitive order:
registerSettingsMemoryRoutes(...)registerTaskWorkflowRoutes(...)registerPlanningSubtaskRoutes(...)registerChatRoutes(...)registerMessagingScriptRoutes(...)registerGitGitHubRoutes(...)registerFilesTerminalWorkspaceRoutes(...)registerAgentsProjectsNodesRoutes(...)registerPluginsAutomationRoutes(...)- (later)
registerAgentSkillsRoutes(...) - (last)
registerProxyRoutes(...)
Compatibility re-exports that must remain on routes.ts for tests and existing importers:
resolveDiffBase+ResolveDiffBaseTaskInput(fromresolve-diff-base.ts)__resetBatchImportRateLimiter(fromregister-git-github.ts)__setCreateFnAgentForRefine(defined inroutes.ts)
Ordering rules (critical)
Express matches in registration order. Keep registrar and in-registrar route ordering stable:
- Specific operation routes before generic parameterized routes (
/runs,/runs/:id,/copy,/deletebefore/:idstyle handlers) - Specific operation routes before wildcard paths (
/files/{*filepath}/copy|move|delete|rename|download|download-zipbeforePOST /files/{*filepath})- Why: Express route matching is first-win. If the wildcard write route is registered first, paths like
/files/somefolder/deletewill be treated as file writes instead of delete operations.
- Why: Express route matching is first-win. If the wildcard write route is registered first, paths like
- Do not move proxy/script/message/file wildcards ahead of specific routes
- For proxy routes specifically, keep all explicit
GET /proxy/:nodeId/*handlers ahead ofALL /proxy/:nodeId/{*splat}and keep proxy registration last increateApiRoutes().
- For proxy routes specifically, keep all explicit
- Project/node/sync/discovery ordering constraints must stay intact:
/projects/across-nodesand/projects/detectmust be registered before/projects/:id/nodes/:id/settingsmust be registered before/nodes/:id/settings/push|pull|sync-statusand before/nodes/:id/auth/sync/mesh/statemust be registered before/mesh/sync- Discovery routes stay grouped after mesh routes
- Inbound
/settings/sync-receive|auth-receive|auth-exportroutes mount after discovery routes
- Auth/model/usage ordering constraints must stay intact:
- Keep
/modelsregistration before auth-dependent picker/settings flows that rely on consistent model filtering - Keep auth registrar routes grouped as currently mounted (status/diagnostic + mutation endpoints) so no wildcard handler can shadow
/providers/claude-cli/status - Keep
/usagemounted as a standalone registrar route (not under auth paths) with unchanged error mapping semantics
- Keep
- Agent ordering constraints must stay intact:
/agents/stats,/agents/org-tree,/agents/resolve/:shortnamebefore/agents/:id/agents/:id/runs/stopbefore/agents/:id/runs/:runId/agents/:id/reflections/latestbefore/agents/:id/reflections
If adding a new endpoint, place it in the domain registrar and verify it does not shadow existing handlers.
Integrated routers
Integrated routers are mounted through register-integrated-routers.ts and intentionally called from routes.ts at precedence-sensitive points:
registerIntegratedRouters(...)mounts:createMissionRouter→/api/missionscreateInsightsRouter→/api/insightscreateTodoRouter→/api/todos
- Roadmap endpoints are plugin-owned and exposed under
/api/plugins/roadmap-planner/.... registerIntegratedDevServerRouter(...)mounts:createDevServerRouter→/api/dev-server
Keep these calls in their current positions inside createApiRoutes() unless an explicit route-ordering migration is planned and regression-tested.