Menu
popagent
publicLatest change 7cecf6a89b6f39dae8c5678f5b369014199aeb3c - Add self-hosted Mastra observability 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 explicit light and dark modes; 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`, and `AgentSettingsPage.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, `/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 mask `recall-secret` results as `[redacted]` at render time. - 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, 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 shows repository cards with chat counts, active task counts, and recent activity. A workspace 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** toggles global persisted browser-tool enablement. Headless operation and read-only tool exclusions are fixed server safety boundaries. - **Memory** exposes persisted compaction controls: automatic compaction, observation/reflection thresholds, recent raw-history retention, and idle buffering. - **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, Memory, and Runtime controls render only after their API values load; the client has no substitute settings defaults. - **Observability** 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. - **Tasks** selects a repository workspace before creating background prompts or recurring schedules. The schedule 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. Schedule rows show a human-readable recurrence and local next-run timestamp. The section also shows running specialist/supervisor iteration progress, restart-recovery counts, final output/errors, and queued, cancelling, or cancelled state for active work. ## 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. - Visual changes require exercising the affected desktop and mobile paths in a real browser.