Layout: agents now arrange in a horizontal grid (3-4 per row based on
viewport width) instead of stacking vertically. Position calculation
uses layoutPositions() which guarantees agents stay within the canvas.
Performance: render loop throttled to 30fps instead of uncapped 60fps.
Removed Math.random() from render path — building window lights now use
a deterministic hash that changes slowly. Canvas dimensions only resize
when clientWidth/clientHeight actually change.
Crash fix: clearRect at frame start prevents accumulated draw state.
setTransform instead of scale prevents compounding transforms. Animation
cleanup properly cancels requestAnimationFrame on unmount.
Characters now have round heads, headphones, eyes, shirts, pants, and
shoes — proper pixel-art people like claude-office. Desks are detailed
wooden tables with monitors showing task text, keyboards, and coffee
cups. The wall has a city skyline window, analog clock, whiteboard with
TODO items, and an employee-of-the-month painting. Active agents bob
and show speech bubbles with their current task. Floor has tile grid.
The virtual office is now a dedicated page (nav: Office) with an HTML5
Canvas rendering animated pixel-art characters. Each agent is drawn as
a pixel person with deterministic skin/shirt colors, walking legs, and
a bobbing head when active. Idle agents wander randomly near their desk.
Active agents show a green dot, thinking agents show a question mark.
Departments are labeled zones with desks and monitors. Removed the old
sidebar component entirely.
The channel timeline now renders each thread as a single compact row
with sender avatar, message preview (truncated to one line), reply
count with participant avatars, and last reply time. Clicking a row
opens the thread in the right panel where the full conversation lives.
This matches Slack's thread-list pattern instead of dumping every
message body into the channel view.
Replaced generic bot icons with CSS pixel-art people — colored head,
shirt body, and legs, each agent with a stable color derived from their
name. Layout is now compact flex-wrap (42px per agent) instead of one
agent per row. Sidebar narrowed to 172px. Tooltip anchored to right
edge to prevent clipping. Department floors use the app's panel-2
background for a clean integrated look.
Tools are now cached per-org with a 5-minute TTL to avoid hitting the
Composio API on every agent run. The ComposioPanel loads the currently
saved API key status and pre-checks enabled toolkits from the snapshot,
so users see their existing configuration when opening Settings > Tools.
Inspired by claude-office's multi-floor building metaphor but rendered
as a polished UI instead of pixel art. Departments appear as numbered
floors in a building frame. Each workstation has a monitor with animated
screen glow for active agents, a stand, and a status dot. The building
header shows the active/total agent ratio at a glance.
Each agent sits at a workstation with a monitor, status-colored glow for
active agents, and a rich tooltip showing name, role, and current task.
Departments render as labeled rooms with subtle borders. The monitor
lights up when the agent has a running task run, giving a real sense of
who is working and who is idle.
Settings > Tools tab lets the user enter their Composio API key and
select which toolkits (GitHub, Slack, Gmail, etc.) to enable for agents.
The configuration is saved per-org and loaded at agent run time.
The catch block in wakeAgentMentions now records wake_state on each
mention (woken/failed) instead of silently discarding the error. Failed
mentions render with a strikethrough and muted color so the user sees
which agents could not be reached, with a tooltip on hover. Migration
044 adds the wake_state column to chat_mentions.
A collapsible right sidebar renders agent presence as a visual office
floor plan grouped by department. Each agent shows at their desk with a
status ring (active/thinking/busy/idle) and a tooltip revealing their
current task on hover. Toggle via the Office button in the left nav.
MentionTextarea inserted raw agent names (e.g. @Sarah Chen) but the
backend regex captured only the first word (@Sarah), causing mention
resolution to fail silently. Moved canonicalMentionHandle to shared/
so the renderer can produce space-free handles that the backend resolves
correctly.
The policy fell back to default-src for scripts and allowed
'unsafe-inline', so any future injection sink would run with the
renderer's full contextBridge authority. The page has no inline scripts
(one external module), so split the directives: script-src 'self' with
no inline, style-src keeps 'unsafe-inline' for React inline styles, and
add object-src/base-uri/frame-src 'none'. localhost stays only on
script/connect for the dev server. Verified the production build still
loads and renders end to end.
Bodies were printed verbatim, so an agent's **bold**, `code`, or
[link](https://...) showed as literal punctuation — the opposite of
people talking. Render the inline markdown agents and humans actually
use in chat into React nodes, composed around the existing @mention
highlighting.
Links are restricted to http(s) and nothing is ever set as raw HTML
(no dangerouslySetInnerHTML), so a message body can never inject markup
or a javascript: URL. A plain message still comes back as one string.
The thread panel rendered an "Audit" feed — "Chat message sent", "Task
run completed", "Approval requested" rows with monospace timestamps —
a server log sitting inside a conversation. Attachments showed an eight-
character SHA-256 fragment next to the file type. Neither belongs in a
surface a non-technical operator reads as people talking.
Remove the audit replay from the chat thread (and its now-dead
replayEventsForThread/auditPayload plumbing) and drop the checksum from
the attachment card. The audit trail itself is unchanged — it is still
recorded and still shown on the task activity tab. The e2e thread check
now asserts the reply box, not the log.
Sweep the remaining inline type across every page onto the system scale:
oversized metric numbers (22/24px) down to the 18px title step, body text
13px → 14px, captions/meta 11px and 12.5px → 12px, and small control icons
13/11px → 16/14px. No structural or layout changes — the foundation
already gridded spacing, radius, and color; this finishes the type so the
whole app reads from one ramp. Full interactive e2e and the overflow
guards stay green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add CascadeMenu/CascadeRow: a menu whose rows reveal a side submenu on
hover with an open delay, a forgiving close delay, and a cursor→submenu
safe triangle — while the cursor sweeps toward an open submenu, hovers
over sibling rows are ignored, so a natural diagonal move never snaps the
target shut. The triangle geometry (pointInTriangle) is unit-tested.
Apply it to the org switcher: each organization reveals its actions
(switch / delete) on hover instead of a hidden hover-only "x", while the
destructive delete keeps its deliberate, non-hover-dismissible confirm.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The first-run flow still carried its own sizes: a 34px hero, 13/12.5px
intros and helper text, 13px control icons. Restrain the hero to the 24px
display step and move intros/errors to 14px, helper text to 13px, and the
provider/workspace icons to the 16–18px standard. Structure was already
clean (two-pane, single cards) — this is purely scale alignment.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "Spaces" section badged each department with its agent count, so an
org with one CEO and zero files showed "Executive 1". Clicking it filtered
to that department's files and revealed nothing — the tree advertised a
folder that opened empty. collectDepartments now counts files per
department and omits departments with none, so every badge equals what the
section contains. Unit-tested for file-based counting and empty omission.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The run rows used a .nav-row class that was never defined, so the button
had no width or flex constraint: long run titles pushed the row past the
340px pane and forced a horizontal scrollbar with the title clipped.
Define .nav-row as a constrained selectable row (width:100%, min-width:0)
so the inner title ellipsizes in place. Align the pane and detail
typography to the 14/12 scale while here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The home page was 13 cards deep with empty roll-ups (Today, In progress,
Team duplicated other pages) and a stark full-black goal card that fought
the rest of the surface. Now:
- Three intent cards lead — Quarter goal (light, integrated), Monthly
budget, and Waiting-on-you with a real "all caught up" empty state.
- A four-up metric strip (workload, run activity, success rate, token
usage) replaces the redundant budget chart. Each tile lifts on hover
and its header shows a live readout of the hovered datum.
- Activity (agent runs, recent messages) closes the page.
No data wiring or approval logic changed; HomePage drops 500 → 344 lines.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The flat 13-item list read as clutter and reused icons (Team/Meetings,
Work/Sprints, Inbox/Approvals all shared a glyph). Now:
- Primary group leads with the core loop: Home, Work, Team, Messages,
Runs, Files, Approvals.
- A divider demotes the occasional surfaces: Inbox, Documents,
Knowledge, Meetings, Sprints, Social — still one click away, nothing
orphaned (search can't route to several of them).
- Distinct calendar/target/inbox/folder icons end the collisions.
- Every nav glyph is the 18px icon bound in a 24px box; the collapsed
rail centers icons instead of hiding them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Establish one token system across the shell and primitives:
- Typography: vendor Geist (variable woff2, self-hosted, CSP-clean);
body 14/16px, title 18px, captions 12px; drop oversized headings.
- Color: primary #181818, secondary #6A6A6A over a pure-neutral ramp.
- Spacing: strict 4px grid via --sp-* tokens.
- Radius: 8 → 40 scale (--r-sm..--r-2xl, --r-full).
- Icons: 18px glyph, 1.5 stroke, 24px bound by default.
Sweeps tokens through styles.css, components.css, and overlays.css so
buttons, inputs, cards, pills, menus, the sidebar, topbar, and page
titles all read from the same scale. No page logic touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every interactive control now acknowledges input: buttons and icon
buttons press down (scale on :active) and ease their hover states, nav
items, menu rows, segmented controls, cards, table rows, and inputs
transition over shared motion tokens (120/200/280ms, ease-out curves).
Dialogs and popovers enter with a short rise + fade over a fading
backdrop; switching routes and onboarding steps plays a quiet view
entrance keyed on the route/step. State pills cross-fade their tones
as runs progress. All motion collapses under prefers-reduced-motion.
PRODUCT.md captures the register and design principles guiding this.
Adversarial review of the kickoff feature surfaced two gaps. First, the
kickoff task was inserted with raw SQL, so a workspace-bound kickoff
carried requires_filesystem without the execution approval every other
task receives - manual runs failed and review-mode orgs had no approval
trail. Kickoffs (in-app and onboarding) now go through createTask and
its policy gates. Second, a goal created before any agent existed
stayed inert forever; creating an agent now operationalizes every
active goal that has no task yet. Goal owners must be active agents:
an archived owner falls back to the top-level agent and the owner
dropdown only offers active agents.
Remove dead imports and unused values across pages; replace the
MentionTextarea index-reset effect with an event-time reset; compute
budget chart segment offsets in a pure helper instead of mutating a
render-scope variable; drop the manual memoization the compiler could
not preserve in BillingPanel.
Settings shrinks from 14 tabs to the five that serve the product:
Workspace, Billing, Providers, Skills, Migration. The deleted panels
(Members/IAM, Models, Policies, Operations, SDLC, Compliance, Profile,
Notifications, Security) were read-only governance tables and raw
audit-log dumps with no old-app counterpart. The Automation page goes
too: its rules stored empty conditions and no engine ever executed
them. Sidebar reordered to match the old app hierarchy (Home, Inbox,
Work, Approvals first). Backend services and schema are untouched.
Inbox page has three tabs: Inbox (filterable by approvals, failures,
blocked, messages — shows chronological items with agent attribution),
Standup (per-agent daily summary with tasks/runs counts), and
Performance (company metrics with success rate, throughput, and
per-agent breakdown table).
HiringPipeline component shows agents across stages: Pending Approval,
Approved, Active, and Rejected. Each stage column shows agent cards
with role, department, and approval timing. Stage assignment is derived
from the agent's lifecycle and associated hire approval state.
AgentWorkloadChart (bar), BudgetUtilizationChart (stacked bar),
RunActivityChart (7-day bar), SuccessRateChart (ring gauge), and
CostTrendChart (7-day line). Pure SVG, no external charting library.
Charts render from snapshot data in the DashboardCharts module.
Two-pane RunsPage: left pane shows searchable, filterable run queue
(by status: queued/running/completed/failed). Right pane shows full
run detail: agent, provider, trigger, timing, duration, execution
context, error messages for failures, and provider request response
text as a scrollable log viewer.
Migration 041 adds parent_task_id to tasks for subtask hierarchy.
TaskDetailDialog now has three tabs: Overview (description, assignee,
project, goal, workspace, runs with error messages), Subtasks (child
tasks with status), and Activity (audit events and approval history).
Inline edit overlay covers title, description, assignee, risk, priority,
and parent task. Task delete is available from the dialog footer.
Three new pages in the sidebar navigation: Documents (CRUD with type
filtering, inline editing, status lifecycle), Knowledge Base (card grid
with category/importance filters, create/delete), and Automation (rule
table with trigger/action/priority/enable toggle, create/edit/delete).
Each page connects to the corresponding backend service from the
previous commit.
UpdateAgentInput now includes department and budgetCents. The edit dialog
on the Team page shows department and monthly budget alongside the
existing role, title, seniority, provider, and model fields.
The workspace settings panel now includes an Add button that opens a
dialog with name, local path (with directory picker), and optional
project binding. Supports creating secondary workspaces beyond the
initial bootstrap workspace.
Full-stack: IPC channels, command types, OrganizationService methods,
IPC handlers, preload bridge, desktop-api types, and UI dialogs on the
Work page. Users can now create goals and projects directly instead of
relying on the onboarding bootstrap.
The homepage now shows two activity cards: recent agent runs (with
state, trigger, and timing) and recent chat messages (with sender,
channel, and message preview). Gives the operator real-time visibility
into what agents are doing.
Shared MentionTextarea component provides @mention autocomplete in both
the channel composer and the thread reply composer. Type @ to see agents
and employees, arrow-navigate, Enter to select.
Type @ in the message composer to see a dropdown of agents and employees.
Arrow keys navigate, Enter/Tab selects, Escape dismisses. The selected
handle is inserted at the cursor position.