AkurAI Build
Menu

popagent

public

Latest change baffb3c2ac834de138dc9ac86330273851896d24 - Move task board to root overview by AkurAI Build

# Chat UI

Library: **assistant-ui** (`@assistant-ui/react` primitives). Docs: https://www.assistant-ui.com/

## Architecture

- `useChatRuntime` + `AssistantChatTransport` (`@assistant-ui/react-ai-sdk`)
  targets `/api/chat`; the selected model, persistent session ID, and repository
  workspace ID ride in the request. Backend history reconstruction and
  newest-message policy are defined in [AGENTS_AGENT.md](AGENTS_AGENT.md).
- Composed from primitives (`ThreadPrimitive`, `MessagePrimitive`,
  `ComposerPrimitive`) with our own markup — not the styled `Thread` bundle.
- Markdown: `MarkdownTextPrimitive` (`@assistant-ui/react-markdown`) +
  `remark-gfm`, styled under `.md-content` in `globals.css`.
- Tool calls use `MessagePrimitive.Parts` `tools.Fallback`. The global display
  preference selects compact chips, expanded argument/result details, or
  timeline-accented cards; each card exposes its current execution status.
- Styling: **Tailwind v4** at serve time (`bun-plugin-tailwind`, `bunfig.toml`
  `[serve.static]`). Semantic theme tokens in `globals.css` support Light, Dark,
  Dracula, Nord, Tokyo Night, Catppuccin, Gruvbox, and Solarized Light palettes;
  the Appearance setting persists the selection in browser local storage and
  initially follows the operating system. Icons come from `lucide-react`.
- Shared server/UI wire shapes come from `src/api-types.ts`. Large independent
  surfaces are split into `ModelPicker.tsx`, `MemoryDrawer.tsx`,
  `WorkspaceOverview.tsx`, `DocumentationPage.tsx`, `AgentSettingsPage.tsx`, and
  `AutonomySettingsPanel.tsx`; `Chat.tsx` owns repository-workspace and session
  orchestration.
- Native browser-history routing keeps major locations reloadable and shareable:
  `/` is the overview, `/workspaces/:workspaceId` is a repository workspace,
  `/workspaces/:workspaceId/docs/*` is its Markdown documentation workspace,
  `/chats/:sessionId` is a conversation, and `/settings/:section` is a settings
  section. Bun serves the application shell for browser routes while unmatched
  `/api/*` requests remain JSON 404 responses.
- `src/ui/api.ts` provides `apiFetch`, attaching the optional localStorage API key and dispatching `popagent:unauthorized` on 401; all UI API calls use it.
- Session saves include the current `revision`; a 409 conflict is retried once with the server session revision while retaining local messages.
- Tool cards centrally mask arguments and results for retired plaintext secret tools as `[redacted]`.
- Failure surfacing: transient errors show in the module-level toast host
  (`src/ui/toast.ts` + `<Toasts/>` in the Chat root); a failed initial session
  load renders a retry card instead of a blank screen; a 401 renders an access
  key gate that stores the key in `localStorage["popagent:api-key"]`.
- Message actions use `ActionBarPrimitive`: copy on all messages, regenerate on
  assistant messages, and trace-linked thumbs up/down plus trace-ID copy when a
  traced assistant response is available. Actions appear on hover/focus and hide
  while running. Feedback is recorded once per rendered response through the
  protected observability API.
- Accessibility: an `aria-live=polite` region announces run start/completion;
  `useFocusTrap` (`src/ui/use-focus-trap.ts`) traps Tab and restores prior
  focus in MemoryDrawer, AgentSettingsPage, WorkspaceDialog, DeleteDialog,
  credential edit/delete dialogs, and the ModelPicker popover.
- The context meter renders nothing when the catalog has no context window for
  the selected model, rather than showing a zero denominator.

## Workspace layout

Sidebar: branding, a master overview link, repository workspace switcher, one
primary `Create new chat` control, title search, active/archived views, and the
selected workspace's sessions ordered by activity. Each session can be renamed,
archived/restored, or permanently deleted. Permanent deletion confirms that
transcript and Mastra thread data are removed while independent long-term
memories remain.
Main header: a searchable, provider-grouped model picker fed by
`GET /api/models`, long-term-memory control, and tool-display settings. Tool
display mode (`compact`, `expanded`, or `timeline`) is global and persisted in
browser local storage.
Main: the default master overview is the operational root. It creates background
tasks for a selected repository and renders the live agile task board before
repository cards with chat counts, active task counts, and recent activity. The
board groups queued, in-progress/cancelling, attention-required (failed,
cancelled, or dead-letter), and done work without inventing review or
artifact-readiness state. Cards show workspace, latest agent activity, bounded
step progress, attempts, retries, recovery, errors, source lineage, timestamps,
and collapsible request/output evidence. Root task state refreshes every two
seconds; mobile uses horizontal snap lanes instead of compressing four columns.
Each repository workspace landing page can permanently delete all of its active
and archived chats through a focus-trapped confirmation modal; transcripts and
Mastra threads are removed while independent long-term memories remain. The
documentation workspace provides a Markdown page tree, editor, GFM preview,
semantic search, and index status; its durable contracts live in
[AGENTS_DOCUMENTATION.md](AGENTS_DOCUMENTATION.md). A chat view contains its thread viewport,
empty-state suggestions, composer with send/stop toggle, thinking indicator,
collapsible hook activity, and context-window meter. Specialist
`data-agentActivity` parts render inline as live iteration cards beside the
supervisor's final response. The meter uses provider `inputTokens` from
`message.metadata.custom.usage`; its denominator comes from live 9Router
context-window metadata.
Desktop keeps session navigation visible. Mobile uses an isolated modal session
drawer, a single-row workspace header, 44px touch targets, safe-area-aware
composer spacing, and a horizontal settings-section bar above full-width
settings content.

Settings is a routed full-screen modal workspace reached from session
navigation or a direct `/settings/:section` URL. It uses dialog semantics,
focuses its close control on open, closes on Escape to the originating route,
and restores focus to the opener:

- **Agents** presents a compact selectable handoff sequence—Orchestrator →
  Researcher → Implementer → Reviewer—then edits each agent's PostgreSQL-backed
  display identity, delegation description, complete role instructions,
  workspace/browser access, delegation eligibility, and tool membership. Instructions are capped at 32,000
  characters and link to their agency-agents source provenance. It also manages
  database-backed Agent Skills including instructions, references, source
  provenance, enablement, user invocation, creation, and deletion. Empty
  identity or instruction fields are rejected.
- **Browser** provides a structured AgentBrowser overview plus persisted runtime;
  security and API boundaries are canonical in [AGENTS_BROWSER.md](AGENTS_BROWSER.md). It includes
  viewport, isolation, session/idle limits, screencast, recording, capability,
  host-policy, active-session, and encrypted Playwright storage-profile controls.
  It lists and closes live sessions and downloads/deletes retained recordings.
  Headless operation and read-only tool exclusions remain fixed server safety
  boundaries.
- **Secrets** manages write-only credentials through the authenticated
  metadata-only API. Operators can create, rotate, bind, and delete a credential;
  values are never rendered or returned. Each usable credential requires one
  exact HTTPS origin. Create/edit and destructive confirmation use focus-trapped,
  theme-matched dialogs with explicit loading, empty, error, and busy states.
- **Memory** explains Mastra's recent-message → observation → reflection model
  with an accessible, settings-aware connection graph and exposes persisted
  operating presets, token thresholds, retention,
  asynchronous buffering and safety controls, idle/provider-change activation,
  shared budgets, exact-history recall scope, temporal markers, attachment
  policy, Observer context optimization, and custom Observer/Reflector guidance.
  It links to the current Mastra Observational Memory documentation and previews
  the effective token intervals calculated from the persisted values.
- **Runtime** edits the database-backed default model, supervisor/specialist
  execution limits, delegation bounds and feedback, and background-task
  concurrency/timing. Changes apply to new turns and tasks; active tasks retain
  the timeout captured when they started.
- Browser, Secrets, Memory, Runtime, and Autonomy controls render only after
  their API values load; the client has no substitute settings defaults.
- **Autonomy** configures persisted self-improvement and contained source
  self-update enablement, reflection cadence, batch and retry bounds, automatic
  strategy/skill application, and the internal self-update cron through
  `GET/PATCH /api/settings/autonomy`. Protected
  `GET /api/autonomy/signals` and `GET /api/autonomy/revisions` accept only a
  strict `limit` from 1–200; `POST /api/autonomy/revisions/:id/revert` rejects
  unknown or non-revertible revisions. While mounted, the section refreshes
  signal and revision history every two seconds and aborts its in-flight read on
  unmount; background failures retain the last rendered history without
  replacing the initial loading, empty, or error states. It shows
  pending/processing/applied/retried/dead-letter health, signal attempts and
  redacted errors, and applied or reverted learned overlay/skill contents.
  Compensating revisions display their explicit `revertsRevisionId` lineage.
  Reverting an applied revision requires a focus-trapped theme-matched
  confirmation; it never changes user-managed base instructions, capability
  membership, workspace/browser access, secrets, hooks, authentication, or
  execution bounds.
- **Observability** follows [AGENTS_OBSERVABILITY.md](AGENTS_OBSERVABILITY.md) and shows last-24-hour run/error/token/latency KPIs, recent
  traces with expandable span timelines, and correlated warnings/errors. Reads
  use the server's bounded observability APIs and expose explicit loading, empty,
  and failure states.
- **Schedules** selects a repository workspace before creating recurring work.
  The editor offers every day, weekdays, or every week; weekly schedules add a
  day selector, and every option uses a native time input. It does not expose a
  timezone because schedules use the server computer's configured timezone.
  Self-update schedules are managed in Autonomy and cannot be paused or deleted
  from Schedules; user-created schedules retain those controls. Schedule rows
  show explicit source lineage, a human-readable recurrence, and the local
  next-run timestamp. Live one-off tasks are intentionally absent from Settings
  and belong to the master overview.
- When a Mastra browser becomes active for the current chat, an authenticated
  SSE screencast workspace slides in from the right. Desktop keeps transcript
  and browser side by side; mobile uses a focus-trapped full-screen dialog. The
  panel shows URL, tabs, access and stream state, remains view-only by default,
  and requires explicit interactive-session takeover before bounded pointer or
  keyboard events are accepted. Hiding the panel disables takeover.

## Decisions

- No shadcn/ui — Tailwind + primitives cover current needs.
- No AI Elements — overlaps with assistant-ui; one chat layer, not two.
- Gotcha: `useThread` is not exported by this `@assistant-ui/react` version —
  use `ThreadPrimitive.If running` for run state and `useAuiState` for message
  metadata/state selectors.
- Visual direction: Mastra Docs density and hierarchy interpreted as a
  macOS-forward liquid-glass workspace. Use near-black/soft-white neutral
  fields, restrained green status/accent color, translucent navigation and
  controls with real backdrop context, fine inner highlights, and compact
  workhorse typography. Glass is reserved for navigation, controls, overlays,
  the composer, and user messages; reading content remains visually quiet.
- Session creation is single-flight (`src/ui/single-flight.ts`): rapid clicks
  share one pending `POST /api/sessions`, then creation becomes available again.
  Keep the primary creation and session-selection accessible names distinct;
  duplicate `New chat` names make automation and assistive navigation ambiguous.
- Initial loading honors the current browser route; `/` opens the master
  workspace overview and never creates a chat implicitly. A chat is created only
  after the user selects a repository workspace and activates `New chat`. With
  no active model selection, creation omits the model so the server uses the
  current persisted default.
- Active and archived lists are disjoint API views. Every session response
  includes `archivedAt` (`null` while active); verify both views after lifecycle
  or cleanup changes rather than inferring state from the visible list alone.
- Initial session and workspace loading is independent of the live model catalog.
  If 9Router catalog discovery fails, existing sessions and the master overview
  still open; the model picker remains empty until a reload.

## Contract checks

- UI helper contracts live beside their helpers.
- UI verification follows the TDD, browser-lifecycle, console, network, and
  reporting gates in [AGENTS_RULES.md](AGENTS_RULES.md).