AkurAI Build
Menu

popagent

public

Latest change c1d09a9885ada54a5c4b8a3daa4a09a53c789fef - Render task runs as full chat transcripts 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 command center is the operational root. Its hierarchy is:
explicit queued/running/attention/completed totals, live work, then repository
workspaces. `Start task` opens a focus-trapped modal for repository and outcome
selection instead of leaving a large composer in the dashboard. The live agile
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 request/output evidence. `Open task run` opens a focus-trapped chat-style
modal that updates with task polling. It renders the original user prompt,
chronological specialist and supervisor responses, thinking state, structured
tool cards with arguments/results/errors/status, step bounds, model, run state,
and final output using the same theme language as ordinary chat. Active
transcripts follow new activity unless the operator is reviewing terminal work.
Terminal cards expose removal through a focus-trapped destructive confirmation
that explains task history/evidence is deleted without undoing repository changes.
Active tasks remain cancellable but cannot be removed. Each lane bounds its card stack and
scrolls independently so completed history cannot push repository navigation
out of reach. Root task state refreshes every two seconds; mobile uses
horizontal snap lanes instead of compressing four columns. Repository cards
follow the board and show chat counts, active work, and recent activity.
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 PostgreSQL-backed project-folder tree, Markdown editor, GFM preview,
semantic search with local-model reranking, 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.
- Terminal task review can mark failed, cancelled, or dead-lettered work completed with inline evidence through the one-shot CLI/API; active and already-completed tasks cannot be relabeled.
- Browser, Secrets, Memory, Runtime, and Autonomy controls render only after
  their API values load; the client has no substitute settings defaults.
- **Autonomy** configures persisted learning and continuous repository improvement: instruction overlays, agent-created skills, bounded durable-fact retention, selected workspaces, cycle cadence, local Titan model execution, and optional server-owned publication/deployment. `GET /api/autonomy/status` exposes paused, active, and improving state; `POST /api/autonomy/run` requests one immediate enabled-workspace cycle and rejects concurrent work. The overview displays current autonomous state, and schedule rows expose latest task status and heartbeat.
- Protected signal and revision APIs retain bounded history, retries, and focus-trapped reversion for overlays, skills, and evolution-managed facts. Reversion 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, current Popagent process RSS and heap usage, 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).