AkurAI Build
Menu

popagent

public

Latest change 859715a358c1f2870d67ca4387dbb7e587d2d39a - fix: remove duplicate company roster 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, `/channels/:channelId` is a native
  shared channel, and `/settings/:section` is a settings section. Bun serves the
  application shell for browser routes while unmatched `/api/*` requests remain
  JSON 404 responses.
- Agent-communication channel cards use the shared wire DTO and render sender
  and recipient roles, assigned Company people and avatars, and reply relations.
  Task runs render as one structured objective/status/model/progress component;
  low-level `task:*` activity rows remain durable evidence but are omitted from
  the channel transcript instead of producing repetitive tool-status messages.
- `/changelog` bundles the tracked `CHANGELOG.md` into a responsive repository-style release timeline, so the release feed cannot drift from the deployed source or depend on a runtime API. Bun's production Markdown loader supplies rendered HTML while tests may supply raw Markdown; the parser preserves identical release history from both forms. The sidebar places Changelog directly above Settings.
- `/settings/company` is the single execution-team surface: one identity-first
  card per staff agent, followed by that agent's editable capabilities,
  instructions, and skills. It does not repeat the same people in a second
  department directory. Legacy `/settings/agents` URLs redirect here.
- `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 with the package semantic version directly below the Popagent
name, 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`; duplicate gateway route IDs render once. Long-term-memory
control and tool-display settings share the header. 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, human-readable latest agent
activity without internal tool identifiers, bounded step progress, attempts,
retries, recovery, errors, source lineage (including distinct user, self-update,
and build-maintenance labels), 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,
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 plus governed Build Maintainer, Release
  Manager, and Community Steward roles. Each execution profile is assigned to
  one Company staff identity and shows that person's avatar, name, job title,
  department, internal email, model, and delegation state before exposing the
  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 source
  provenance. Build role tool memberships remain server-enforced by role
  allowlists, so general agents cannot gain protected Build promotion tools.
  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.
- **Company** renders the organizational roster as department squads with
  generated Midget avatars, job titles, and internal mail links. Organizational
  members remain distinct from the seven governed LLM execution agents.
- **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** selects the persisted execution source: a configured 9Router model,
  Titan's local Ornith 9B route, or 9Router's OpenRouter-backed free coding
  router. It also edits supervisor/specialist execution limits, delegation bounds
  and feedback, and background-task concurrency/timing. Changes apply to new
  turns and tasks; active tasks retain the route and 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, local Titan model execution, and optional server-owned publication/deployment. Self-update cadence belongs to its default row in Schedules. `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. The Autonomy surface keeps these settings functional and adds a bounded workflow history with a semantic ordered rail for Prepare, Research, Decide, Implement, Inspect, Verify, Review, and Commit. It polls `GET /api/tasks?workflow=true` once per interval; the root task poller remains suspended while Settings owns this refresh.
- 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 new-schedule editor offers every day, weekdays, or every week; weekly
  schedules add a day selector, and every option uses a native time input.
  Existing rows expose their persisted cron timing for exact edits, including
  intervals not represented by those presets. Schedules use the server
  computer's configured timezone. Self-update and Build-maintenance schedules
  are default system rows: operators may edit timing and pause/resume them, but
  their identity, prompt, model, attempt limit, source, and deletion remain
  protected. 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).