Commit graph

946 commits

Author SHA1 Message Date
Nexus Dev
bd4e7c5c5d feat(nexus): add ModeBreadcrumb for layout overhaul (phase 8)
Uppercase slash-separated breadcrumb that derives from the current
pathname per docs/specs/2026-04-11-nexus-layout-overhaul.md §4.2.
Leaf segment in text-primary (volt), non-leaf segments in
text-muted-foreground (silver). Pure function deriveBreadcrumbSegments
is exported for unit testing and covers 16 route patterns plus the
catch-all HOME fallback.

The derivation intentionally collapses Phase 11's soon-to-be-demoted
routes (issues/agents/routines/goals/approvals/costs/activity/inbox/
execution-workspaces) under the PROJECTS umbrella segment. When
Phase 11 lands and those routes become /projects/:slug/<tab>, the
derivation will naturally produce PROJECTS / PROJECT-SLUG without
code changes.

Part of Phase 8 of the Nexus layout overhaul (task 2 of 7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:04:31 +00:00
Nexus Dev
9c1525a0f9 refactor(nexus): apply Task 1 code-quality fixes to IconRail (phase 8)
Addresses two Important findings from the Task 1 code-quality review:

1. Swap literal #faff69 hex for the text-primary / bg-primary token
   (4 sites). The --primary CSS variable is already wired to volt via
   MIGRATION-PLAN.md phases 1-3, and 50+ existing files use the token.
   The original Task 1 commit landed on literal hex as a self-
   documenting placeholder; moving to the token aligns with the rest
   of the codebase and makes theme swaps a single-file change.

2. Add focus-visible styles to the Nexus mark link and each
   DestinationLink. Previously the component relied on browser-default
   outline, which on pure-black canvas + volt icons is both off-palette
   and visually weak. Now uses a volt ring with background offset for
   clear keyboard focus indication.

No behavior change. All 7 IconRail tests still pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:01:17 +00:00
Nexus Dev
efbd1e451e refactor(nexus): remove redundant MemoryRouter re-export in router wrapper
Spec compliance review for Task 1 flagged that the explicit
`export { MemoryRouter } from "react-router-dom"` added in 332ed47b
is redundant — the line immediately above already does `export *`,
which includes MemoryRouter. The explicit line was cleanup debt from
defensive "just in case" reasoning, not an actual fix.

No behavioral change. IconRail.test.tsx still resolves MemoryRouter
via the export * and all 7 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:56:22 +00:00
Nexus Dev
332ed47bc0 feat(nexus): add icon rail component for layout overhaul phase 8
Introduces the 56px left icon rail specified in
docs/specs/2026-04-11-nexus-layout-overhaul.md §4.1. Four primary
destinations (Assistant, Studio, Projects, Settings) rendered as Lucide
icons with silver default + volt active state and a 2px volt bar on the
right edge of the active item. Destinations are company-prefixed except
Settings, which points at the global /instance/settings/general route.

The Studio icon also highlights on /convert because Phase 10 folds
ConvertPage into Studio as a workshop. The Projects icon is the umbrella
for all Phase 11 per-project-tab routes (issues, agents, routines,
goals, approvals, costs, activity, inbox, execution-workspaces).

The rail is not yet mounted in Layout.tsx — that happens in task 6.

Part of the Nexus v1.7 structural overhaul (Phase 8 of MIGRATION-PLAN.md
§8b). Companion tests cover all 4 destinations, active-state derivation,
and aria-current semantics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:52:18 +00:00
Nexus Dev
137bd3d0f6 fix(nexus): unblank assistant page on piper-tts import error
The usePiperTts hook imported a non-existent 'tts' namespace from
@mintplex-labs/piper-tts-web@1.0.4. The package exports named
functions (stored, download, predict, etc.) at the top level, not
under a tts namespace. The failing named-import threw at module-link
time, which crashed the lazy chunk for PersonalAssistant.tsx and
left /NEX/assistant blank with only a React error boundary fallback.

Two fixes in one file:

1. Import as namespace:
     import * as tts from "@mintplex-labs/piper-tts-web"
   ESM namespace imports synthesize a 'tts' object whose members are
   the package's named exports, so the existing tts.stored() /
   tts.download() / tts.predict() call sites bind to real functions
   without touching the hook body.

2. Wrap predict() Blob result in URL.createObjectURL() before passing
   to new Audio(). predict() returns Promise<Blob>, not a URL string,
   and Audio() cannot accept a Blob directly. Added a shared cleanup
   callback that revokes the object URL on onended/onerror and in the
   catch path so we don't leak blob URLs on every speak invocation.

Bug 1 was the page-blanking crash at module load. Bug 2 was a latent
runtime crash behind the speak button click handler, surfaced while
the file was already being edited.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:35:01 +00:00
Nexus Dev
d478cc3daf feat(nexus): nexus-first navigation and first-run onboarding trigger
Stop showing Paperclip's board UI by default. First-time users now
land on Personal Assistant (v1.5), see a Nexus-first sidebar, and the
NexusOnboardingWizard (built in v1.5) actually fires on first run
instead of sitting behind a dead "Start Onboarding" button click.

App.tsx
  - CompanyRootRedirect now reads useNexusMode() and lands the user
    at /${prefix}/assistant by default. Only project_builder mode
    lands at /${prefix}/dashboard. "personal_ai" and "both" (the
    default) both go to the Assistant.
  - NoCompaniesStartPage gutted: the old "Create your first company"
    button is gone. Single-workspace mode doesn't ask users to name
    workspaces; the onboarding wizard handles it. Replaced with a
    minimal "Setting up your workspace..." loading shim.
  - OnboardingRoutePage now auto-opens the wizard on mount when no
    companies exist. Closes the dead-button gap: previously the user
    had to click "Start Onboarding" to actually get the wizard; now
    the wizard opens itself as soon as they land.

Sidebar.tsx
  - Restructured around two mode-gated sections:
    * Always visible (Nexus essentials): Assistant, Content Studio,
      Convert, Inbox, Skills, Settings. Plus the New Issue button and
      plugin sidebar items.
    * project_builder-only: Work (Issues, Routines, Goals), Projects,
      Agents, and the remaining Workspace items (Org, Costs, Activity).
  - Top bar no longer renders a company switcher dropdown — single-
    workspace mode shows the workspace name as a static label with
    the search button beside it.
  - Dashboard link removed from the always-visible section. The
    default landing is /assistant; users who explicitly want the
    Paperclip dashboard can type the URL or switch to project_builder
    mode.

Layout.tsx
  - Removed both <CompanyRail /> renderings (mobile and desktop
    branches). Single-workspace mode doesn't need a multi-company
    icon rail. Import preserved with a [nexus] comment for upstream
    rebase compat.
  - Onboarding useEffect's authenticated-mode gate removed (root
    cause of the v1.5 wizard-not-firing bug on fresh DB). This
    effect is now a belt-and-suspenders fallback; the real auto-
    trigger lives in OnboardingRoutePage because Layout isn't
    actually mounted during the zero-company first-run state
    (CompanyRootRedirect navigates to /onboarding before Layout
    ever renders).

NexusOnboardingWizard.tsx
  - handleSubmit and handleStartChat both used to hardcode the post-
    creation navigation to /${prefix}/dashboard. Now mode-aware:
    project_builder lands at /dashboard, everything else lands at
    /assistant. Matches the Sidebar and CompanyRootRedirect logic —
    a fresh user never touches the Paperclip dashboard unless they
    explicitly chose project_builder during the wizard.

Not changed:
  - The Paperclip pages themselves (Dashboard, Issues, Projects,
    Agents, Org, etc.) — still present, still accessible by URL,
    still upstream-mergeable. Just hidden from the default nav.
  - CompanyRail.tsx, CompanySwitcher.tsx, NewCompanyDialog — files
    preserved for upstream rebase diff minimization. No call sites
    remain.
  - /NEX/companies route still registered in boardRoutes(), just
    unlinked from the default UI.

TypeScript: zero new errors (pre-existing errors in AgentConfigForm,
command.tsx, useKeyboardShortcuts, usePiperTts, useVadRecorder,
OnboardingSummaryStep.test, PersonalAssistant unchanged).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:19:14 +00:00
Nexus Dev
50e91d8732 fix(nexus): register missing board route roots for prefix classifier
The company-aware router helper (applyCompanyPrefix / Link wrapper in
ui/src/lib/router.tsx) uses BOARD_ROUTE_ROOTS to tell the difference
between "this path is under /:companyPrefix/..." and "this is a raw
company prefix like /NEX." When a path segment isn't in BOARD_ROUTE_
ROOTS or GLOBAL_ROUTE_ROOTS, extractCompanyPrefixFromPath assumes
it's a company prefix and applyCompanyPrefix returns the path
unchanged instead of prepending the current company.

Every board route added in v1.5 (Personal Assistant) and v1.7
(Content Generation, Convert) was wired in App.tsx but never added
here. Sidebar's <SidebarNavItem to="/assistant"> therefore rendered
as /assistant (raw), the router treated "assistant" as a company
prefix, no company matched, and the user landed on the "Company not
found" 404 — this is how the /ASSISTANT/company/settings confusion
from the previous debug session was born: after the Layout
auto-recovery fix the broken link now redirects to /NEX/dashboard
but silently eats the Assistant navigation intent.

Added:
  - assistant     (v1.5 Personal Assistant)
  - content-studio (v1.7 Content Generation)
  - convert        (v1.7 Format Conversion)
  - plugins        (board-scoped PluginPage at /:prefix/plugins/:id)
  - tests          (dev-only /tests/ux/runs RunTranscriptUxLab)
  - settings       (LegacySettingsRedirect at /:prefix/settings, which
                    then redirects to /instance/settings/general)

Also added a block comment above the set explaining the invariant
so future additions to App.tsx's boardRoutes() don't drift out of
sync again. Existing company-routes.test.ts passes unchanged
(2/2 green).

Nothing in the sidebar / nav wiring changed — the Nexus v1.5 and
v1.7 pages were always correctly routed in App.tsx. They were just
unreachable via Link clicks because the prefix classifier lied about
what counted as a board route.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:40:53 +00:00
Nexus Dev
3a41ec7b9c feat(nexus): design system phase 3 raw utility sweep
Third phase of the DESIGN.md migration. Removes every raw Tailwind
color palette utility (bg-red-*, text-amber-*, border-blue-*, etc.)
from component source and replaces them with the semantic tokens
introduced in phases 1 and 2.

Scope:
  - 84 files touched under ui/src/
  - ~420 raw palette utility instances replaced
  - 23 hardcoded hex fallbacks replaced with var(--token) refs
  - Zero raw palette utilities remain in component source
    (verified with rg '(bg|text|border|ring)-(red|blue|green|amber|
    yellow|cyan|violet|purple|pink|slate|zinc|neutral|sky|teal|
    emerald|indigo|rose|orange|fuchsia)-[0-9]+' ui/src)

Mapping rules applied:
  - red-* -> destructive
  - amber-/yellow-/orange-* -> warning
  - green-/emerald-* -> success
  - blue-/cyan-/sky-* -> primary (info/in-progress) or muted-foreground
  - slate-/gray-/zinc-/neutral-* -> muted / muted-foreground / border
  - violet-/purple-/pink-/indigo-/rose-/teal-* -> collapsed to
    primary or muted (most were one-off decorative choices, not
    role-bearing). Role-bearing uses go through lib/agent-role-colors
    which was rewritten in phase 2.
  - Opacity modifiers preserved (/10, /15, /20, etc.)
  - dark: variant duplicates removed (theme tokens auto-switch)

Hardcoded hex fallbacks fixed:
  - #6366f1 (indigo) -> var(--primary) / var(--volt)
  - #64748b (slate) -> var(--muted-foreground) / var(--silver)
  - #4f46e5 (indigo) -> var(--primary)
  - #89b4fa (old Catppuccin blue) -> var(--primary) / #faff69
  - OrgChart status dots (#22d3ee/#4ade80/#facc15/#f87171/#a3a3a3)
    -> var(--primary) / var(--success) / var(--warning) /
    var(--destructive) / var(--muted-foreground) per status
  - VoiceWaveform fallback #89b4fa -> #faff69 (volt)

Legitimate hex values left untouched (12 total):
  - lib/color-contrast.ts WCAG reference constants
  - lib/worktree-branding.ts contrast fallback references
  - lib/mention-chips.ts runtime-generated SVG fills
  - context/ThemeContext.tsx theme metadata brand hexes
  - components/ThemeSeedInput.tsx user-facing hex picker

Ambiguous decisions (flagged for visual QA):
  - AgentDetail.tsx invocation-source badges (timer/assignment/
    on_demand) collapsed to primary/muted — visual distinction
    is reduced, labels still differ. Consider chart-role slots
    if differentiation matters.
  - AgentDetail.tsx mixed-opacity amber banners: bg-warning/60
    against new warning base reads heavier than original amber-50
    base.
  - Live-state dots in KanbanBoard/AgentDetail: bg-blue-* ->
    bg-primary — will glow volt in dark mode, probably desirable.

Verification:
  - npx tsc --noEmit in ui/ — zero errors introduced. Pre-existing
    errors in AgentConfigForm, command.tsx, useKeyboardShortcuts,
    usePiperTts, useVadRecorder, PersonalAssistant remain, all
    unrelated to color work.
  - Dev server on :6100 returns 200.

Not changed in this commit:
  - ui/src/lib/company-routes.ts — separate routing fix for broken
    Assistant/ContentStudio/Convert links, committed next.
  - Test files — a few will need assertion updates but are out of
    phase 3 scope.

Phase 4 follow-ups (rounded-xl/2xl collapse, soft shadow removal,
gradient removal) noted in .planning/AUDIT-RADIUS-SHADOWS.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:40:32 +00:00
Nexus Dev
4b8f8178ee feat(nexus): design system phase 2 status and role color dictionaries
Second phase of the DESIGN.md migration. Rewrites the two color
dictionaries that back most status/priority/role indicators across the
app, and adds the controlled "chart palette" exception for agent role
colors per user decision in MIGRATION-PLAN section 9.

status-colors.ts (full rewrite, 109 -> 122 lines)
  - All 5 dictionaries (statusBadge, statusDot, priorityColor,
    issueStatusIcon, agentStatusDot) + 5 defaults rewritten to use
    semantic token classes:
    * done/completed/approved -> bg-success/15 text-success border-success/30
    * error/failed/terminated/rejected -> bg-destructive/15 text-destructive border-destructive/30
    * pending/paused/in_review -> bg-warning/15 text-warning border-warning/30
    * running/in_progress -> bg-primary/15 text-primary border-primary/30
    * idle/planned/backlog/todo -> bg-muted text-muted-foreground border-border
    * blocked -> bg-destructive/10 text-destructive border-destructive/25 (softer)
    * cancelled/archived -> bg-muted/40 text-muted-foreground/70 border-border
    * priority urgent/high/medium/low -> destructive/warning/primary/muted
  - Zero raw palette utilities remain (rg verified).
  - All export identifiers and signatures preserved; 11 caller files
    across ui/src compile unchanged.
  - Note: timed_out was not explicitly mapped in the spec but exists
    in the source; agent chose warning (semantically closer than error).

agent-role-colors.ts (full rewrite, 17 -> 68 lines)
  - Controlled "chart palette" exception: 5 muted desaturated hues,
    passed WCAG AA for all 10 combinations (dark + light), 7/10
    also pass AAA.
  - 11 AgentRole entries cycle through 5 slots via mod-5:
    * slot 1 (volt #faff69 dark / olive #4f5100 light): general, pm
    * slot 2 (teal #6ee7b7 / #0f766e): devops, cto
    * slot 3 (lavender #c4b5fd / violet #6d28d9): designer, cmo
    * slot 4 (amber #fcd34d / #b45309): ceo, cfo, researcher
    * slot 5 (silver #a0a0a0 / gray #6b6b6b): engineer, qa
  - Hue collisions past slot 5 are intentional and documented inline;
    secondary differentiation relies on icons/labels.

index.css
  - Added 5 --chart-role-* vars to :root and .dark (light + dark modes).
  - Mirrored as --color-chart-role-N in @theme inline so
    text-chart-role-1..5 become valid Tailwind utilities.
  - Minimal surgical additions — nothing else touched.

Verification
  - npx tsc --noEmit in ui/: zero errors in modified files. Pre-existing
    errors in unrelated files (AgentConfigForm, command.tsx, etc.)
    remain unchanged.
  - rg '(bg|text|border|ring)-(red|blue|green|amber|yellow|cyan|violet|pink|slate|zinc|neutral|sky|teal|emerald|indigo|rose|orange)-\d'
    on modified files: zero matches.

Test follow-up (out of scope, flagged for next PR)
  - ui/src/lib/agent-role-colors.test.ts asserts each role has a
    "dark:" prefix (no longer true — CSS vars handle dark variants)
    and that all roles have unique colors (no longer true — 11 roles,
    5 slots). Both assertions need rewriting.

Phase 3 follow-ups
  - Sweep agent should verify no component layers raw palette
    utilities on top of dictionary output.
  - Consumers that previously wrapped statusBadge output in their own
    border-* class may now double-border — worth a visual audit.
  - agentStatusDot's animate-pulse modifier is gone except on
    "running" — if any caller expected animation on "active",
    inline handling needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:25:21 +00:00
Nexus Dev
e49144a4e8 feat(nexus): design system phase 1 tokens and inter font
First phase of the DESIGN.md (ClickHouse-inspired) migration. Rewrites
the foundation CSS variables and theme machinery; downstream phases
(status/role dictionaries, raw utility sweep) still pending.

index.css
  - Full rewrite of @theme inline block. Dark (.dark) and light (:root)
    token sets per MIGRATION-PLAN sections 3 and 5:
    * Dark: pure black canvas (#000000), Neon Volt primary (#faff69),
      Forest Green secondary (#166534), charcoal border (rgba(65,65,65,
      0.8)), near-black cards (#141414), silver muted (#a0a0a0).
    * Light: near-white canvas (#fafafa), Forest primary, Volt
      downgraded to dark olive (#4f5100) for border/active use only,
      silver inverted to #6b6b6b. Accessibility fallback, not brand.
  - Added --warning (#f59e0b / #b45309), --success, and direct brand
    token refs (--volt, --volt-pale, --volt-border, --forest, --near-
    black, --hover-gray, --silver, --charcoal-border) exposed as
    Tailwind utilities via --color-* mirrors.
  - Added --destructive: #ef4444 (#dc2626 in light).
  - Radius scale collapsed to 4px sharp / 8px comfortable / 9999px pill.
  - Deleted .theme-tokyo-night.dark block entirely (was dead code —
    ThemeContext never applied the class).
  - Rewrote hljs syntax highlighting: one dark block under .dark .hljs
    using volt for keywords, pale volt for strings, silver for
    comments; one light block under .hljs using forest/dark-olive/
    silver. Replaced all three Catppuccin + Tokyo Night hljs rule sets.
  - Rewrote scrollbar rules to use var(--muted) / var(--charcoal-
    border) / var(--hover-gray) instead of hardcoded oklch values.
  - Added @font-face declarations for Inter (normal + italic) from the
    self-hosted woff2 files at /fonts/InterVariable*.woff2. font-weight
    100-900 range unlocks weight 900 for DESIGN.md hero moments from
    a single variable font.
  - Set --font-sans to Inter-first stack; body rule pulls the token.

ThemeContext.tsx
  - Simplified to binary Theme = "light" | "dark". Dropped "custom"
    theme type, PaletteRole interface, ROLE_TO_TOKEN map, and the
    /api/nexus/settings custom-theme hydration effect.
  - applyTheme() now just toggles .dark on <html> and sets
    colorScheme. applyCustomTheme() left as a deprecated no-op (no
    external callers but keeping the export avoids churn).
  - Legacy localStorage values (catppuccin-mocha, tokyo-night, custom,
    catppuccin-latte) coerced to "dark" on read so existing users
    don't see a crash after the migration.
  - Default theme: "dark".

Layout.tsx
  - Dropped THEME_META import and the THEME_CYCLE map. Theme toggle
    is now a binary sun/moon flip via toggleTheme().

index.html
  - Added <link rel="preload" href="/fonts/InterVariable.woff2"
    as="font" type="font/woff2" crossorigin>.
  - Set inline style="background:#000000; color-scheme:dark;" on
    <html> so the pre-React paint is already dark — no white flash.
  - Boot script coerces legacy localStorage theme values and persists
    "light" or "dark" only.

ui/public/fonts/
  - Added InterVariable.woff2 (344 KB) and InterVariable-Italic.woff2
    (379 KB), both Inter v4.x from rsms.me/inter (the canonical
    upstream). Self-hosted for LAN/offline reliability.

Not changed:
  - lib/status-colors.ts, lib/agent-role-colors.ts — next phase
  - Any component files — phase 3
  - MIGRATION-PLAN.md — will be updated with resolved decisions later

Expected visual state: pages using theme tokens (bg-background,
text-muted-foreground, border-border, ~1,250 instances) immediately
render with the new palette. Pages using raw Tailwind utilities
(bg-red-500, text-amber-600, ~274 instances) still show old colors
until phase 3 sweep.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:19:56 +00:00
Nexus Dev
ab45bc063d fix(nexus): auto-recover from unknown company prefix
When the URL contains a :companyPrefix segment that doesn't match any
fetched company, Layout previously rendered a dead-end NotFoundPage
with no recovery link. The sidebar's company switcher depends on
selectedCompany being non-null, so users landing on a bogus prefix
had no way back without hand-typing a new URL.

Replace the NotFoundPage branch with a redirect to the same path under
the first available company (selectedCompany ?? companies[0]). If the
rest of the path is empty, fall back to /dashboard so the target is
guaranteed to exist. The hasUnknownCompanyPrefix condition is already
gated on companies.length > 0, so the fallback is reachable; the old
NotFoundPage remains as a theoretical safety net if somehow both
selectedCompany and companies[0] are null.

Triggered by a user session after the embedded-postgres wipe: the
browser had a stale localStorage selectedCompanyId and the user
hand-typed URLs with guessed prefixes like /ASSISTANT/company/settings.
Hitting any invalid prefix stranded them on the 404 with no UI to
pick a different company.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:11:17 +00:00
Nexus Dev
3d2117ee9f fix(nexus): auto-bootstrap invite and vite onnxruntime middleware
Zero-terminal first boot. Previously the bootstrap_ceo invite had to be
created via a CLI command (paperclipai auth bootstrap-ceo) and the UI
showed a code block instructing the user to run it. Nexus is meant to
be zero-terminal, so the server now auto-creates the invite on startup
when no instance admin exists and exposes its relative path through
/api/health. BootstrapPendingPage redirects straight to /invite/{token}.
The CLI command is left intact for headless/SSH-only setups.

Invite flow fixes that surfaced during testing:

  - InviteLanding's invite query had default React Query refetch
    behavior. After a successful bootstrap accept, the invite is marked
    accepted server-side, so the refetch returned "not available" and
    shadowed the success screen, making it look like the bootstrap had
    failed when it actually succeeded. Set staleTime: Infinity +
    refetchOnWindowFocus/Mount/Reconnect: false so the first fetch is a
    one-shot snapshot.

  - Reordered the render checks so result?.kind === "bootstrap" / "join"
    are evaluated before the invite-availability error check — defensive
    against any stray refetch that still leaks through.

  - On bootstrap success, window.location.replace("/") lands the new
    admin directly on the board; the "Bootstrap complete" confirmation
    screen is now an unreachable safety net.

Vite onnxruntime middleware replaces the earlier public/ dump. The
previous commit put ort-wasm-simd-threaded.{mjs,wasm} in ui/public/ so
VAD's onnxWASMBasePath: "/" would find them. That works at runtime but
trips vite's dep optimizer: it scans onnxruntime-web, resolves the
dynamic import string to the public asset, and errors with "files in
/public should not be imported from source code." Remove the files and
add a vite plugin (configureServer middleware) that serves the two URLs
straight from node_modules/.pnpm/onnxruntime-web@*/. Runtime keeps
working and the files never enter vite's module graph.

Production build caveat: the middleware only runs in dev. When building
a static dist for production, the wasm files will need a different
mechanism (e.g. generateBundle hook). Not addressed here.

Also bundled (load-bearing for LAN browser testing):

  - ui/src/lib/queryKeys.ts: add missing 'nexus' group. useNexusMode
    referenced queryKeys.nexus.settings since commit 7bb72a5a (Phase
    33-02) but the key was never added. Caused a blank screen crash on
    any page that mounts Sidebar.

  - ctl.sh: read PORT from .env instead of hardcoding 3100, and read it
    once at the top so every subcommand honors it. Fixes the Version /
    Mode showing '?' in status output after the port move to 6100.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:50:28 +00:00
Nexus Dev
1c8a26dbb4 feat: Phase 45 — Content as Skills (9 SKILL.md files, Creative group, gap fixes) 2026-04-05 09:57:20 +00:00
Nexus Dev
d25d88d053 feat: Phase 44 — Video & Presentations (Remotion MP4, SSE progress) 2026-04-05 09:57:11 +00:00
Nexus Dev
e4165adefb feat: Phase 43 — Documents & Branding (PDF reports, brand kit ZIP) 2026-04-05 09:57:02 +00:00
Nexus Dev
0956c31384 feat: Phase 42 — Wallpapers, Social, Format Conversion & Voice (12 platforms, convert pipeline, offline badge) 2026-04-05 09:56:53 +00:00
Nexus Dev
fc55990fde feat: Phase 41 — Diagrams, Icons & Theme Engine (Mermaid, SVG icons, OKLCH palettes) 2026-04-05 09:56:37 +00:00
Nexus Dev
8f97e69184 feat(39-02): VoiceStep hardware-aware UI with conditional enable/skip
- Add VoiceCapability interface to ui/src/api/hardware.ts
- Export VoiceCapability type from useHardwareInfo.ts
- VoiceStep accepts voiceCapability prop, renders conditionally
- Insufficient hardware: shows capability note with skip-only button
- Binaries present: shows green checkmarks next to STT/TTS labels
- Missing binaries on sufficient hardware: shows install note, dimmed Enable
- NexusOnboardingWizard passes voiceCapability from hardware probe to VoiceStep
2026-04-04 03:55:50 +00:00
Nexus Dev
9dc00f3a88 feat(39-01): ChatVoicePlayer sentence-buffered streaming playback
- Add streaming prop (default true) to ChatVoicePlayerProps
- Connect to POST /api/synthesize/stream via fetch + ReadableStream
- Parse SSE lines manually from response body stream
- First sentence audio begins playing as soon as first chunk arrives
- Subsequent sentences auto-play in sequence from audioQueue
- Show 'Sentence N of M' progress indicator during streaming playback
- Dot progress bar shows completed vs pending sentences
- Falls back to full-fetch mode on stream error or streaming=false
- Clean up all object URLs on unmount or new text
2026-04-04 03:55:50 +00:00
Nexus Dev
ed21eb339c feat(38-03): insert TelegramStep as step 5 in NexusOnboardingWizard
- Import TelegramStep component
- Insert Telegram step at position 5 (between Voice and Root Directory)
- Shift Root Directory from step 5 → step 6
- Shift Summary from step 6 → step 7
- Update step indicator from 'of 5' to 'of 6'
- Update summary indicator from step===6 to step===7
- Update all setStep() navigation callbacks accordingly
- Update error message referencing step 6 for root directory
2026-04-04 03:55:50 +00:00
Nexus Dev
713e92be0f feat(38-03): create TelegramStep onboarding component
- BotFather numbered instructions (4-step setup guide)
- Token input with live validation via POST /api/telegram/token
- Success state showing connected bot username
- Error state with descriptive message
- Skip/Back/Next navigation; Next enabled only after validation
2026-04-04 03:55:50 +00:00
Nexus Dev
35be67d019 fix(37): pass voiceMode in ChatPanel handleEdit path + add verification 2026-04-04 03:55:50 +00:00
Nexus Dev
7d3820a84f feat(37-04): wire VoiceMicButton, VoiceModeToggle, ChatVoiceBadge, voiceMode into chat UI
- ChatInput: replace VoiceRecordButton with VoiceMicButton (VAD-powered)
- ChatInput: add VoiceModeToggle above input when enableVoiceInput=true
- ChatMessage: add ChatVoiceBadge render for voice_input and voice_full messageTypes
- ChatMessage: auto-play reads from localStorage nexus:voice:autoplay key
- ChatPanel: import and call useVoiceMode, extract mode as voiceMode
- ChatPanel: pass voiceMode as third arg to all startStream calls (5 call sites)
2026-04-04 03:55:50 +00:00
Nexus Dev
9435458033 feat(37-04): add voiceMode to chatApi.postMessageAndStream + useStreamingChat.startStream
- postMessageAndStream data type extended with optional voiceMode field
- startStream signature updated: (userMessage, agentId?, voiceMode?)
- voiceMode forwarded into fetch body via postMessageAndStream call
2026-04-04 03:55:50 +00:00
Nexus Dev
8bf2a65a0b feat(37-03): VoiceModeToggle three-pill component + useVoiceMode hook
- VoiceModeToggle: Text / Voice In / Full Voice pills with active/inactive styling
- Auto-play checkbox in full_voice mode, persists to nexus:voice:autoplay in localStorage
- useVoiceMode: reads/writes voiceMode via PATCH /api/nexus/settings with loading state
  (deviation Rule 3: created missing blocking dependency for VoiceModeToggle)
2026-04-04 03:55:50 +00:00
Nexus Dev
6b60f42a25 feat(37-03): ChatVoicePlayer + ChatVoiceBadge components
- ChatVoicePlayer: POST /api/synthesize, play/pause controls, autoPlay support, blob URL cleanup
- ChatVoiceBadge: Voice badge, SPOKEN/DETAILED parsing, collapsible full markdown for voice_full
2026-04-04 03:55:50 +00:00
Nexus Dev
21ecf23d9a feat(37-02): VoiceWaveform canvas component and VoiceMicButton
- VoiceWaveform: 80x32 canvas with Web Audio AnalyserNode (fftSize=64), 20 animated bars drawn from frequency data using --primary color
- VoiceMicButton: three visual states — idle (Mic icon), recording (VoiceWaveform + ring-2 ring-primary), processing (Loader2 animate-spin)
- All three states have correct aria-labels per UI spec copywriting contract
2026-04-04 03:55:50 +00:00
Nexus Dev
0d0b17c8a0 feat(37-02): encodeWav utility, useVadRecorder + useVoiceMode hooks
- encodeWav: 44-byte WAV header encoder (RIFF/WAVE/fmt/data), PCM mono 16-bit
- useVadRecorder: wraps useMicVAD with startOnLoad:false, auto-stop on speech end, POSTs to /api/transcribe
- useVoiceMode: reads/writes voiceMode from GET/PATCH /api/nexus/settings with optimistic update
2026-04-04 03:55:50 +00:00
Nexus Dev
c7974fa67c feat(34-02): voice onboarding step + PersonalAssistant voice wiring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:49 +00:00
Nexus Dev
847f316319 feat(34-01): create usePiperTts hook and TtsButton component with piper-tts-web
- Install @mintplex-labs/piper-tts-web as UI dependency
- Create usePiperTts hook with prewarm/speak/stop/status/progress (VOICE-01, VOICE-02)
- tts.stored() checks IndexedDB cache to skip re-download
- tts.download() with progress callback for visible download progress
- tts.predict() returns WAV blob URL for CPU-safe WASM synthesis
- Create TtsButton component showing download progress during prewarm
- TtsButton shows Volume2/VolumeX icons for idle/speaking states
2026-04-04 03:55:49 +00:00
Nexus Dev
36746ed17b feat(34-01): register chatFileRoutes + nexusSettingsRoutes in app.ts, add voiceEnabled to nexus-settings
- Add chatFileRoutes(db, storageService) after assistantHandoffRoutes (inside boardMutationGuard)
- Add nexusSettingsRoutes() after chatFileRoutes
- Extend nexusSettingsSchema with voiceEnabled: z.boolean().default(false)
- Update default return values in nexusSettingsService.get() to include voiceEnabled: false
- Add voiceEnabled?: boolean to NexusSettings client interface in hardware.ts
2026-04-04 03:55:49 +00:00
Nexus Dev
222d00c57f feat(33-03): real AI streaming with memory injection + assistant handoff
Replace streamEcho with Puter proxy AI call, inject memory facts as
system message, append memory after each turn. Assistant-to-PM handoff
creates new conversation with context summary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:49 +00:00
Nexus Dev
7bb72a5a2f feat(33-01,33-02): memory service + sanitizer, personal assistant page
33-01: memory-sanitizer, assistant-memory service, REST routes, 17 tests
33-02: useNexusMode hook, PersonalAssistantPage, sidebar nav, route wiring

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:49 +00:00
Nexus Dev
4032d4d88b feat(32-01): wire summary step, skip buttons, chat handoff in wizard
- Add OnboardingSummaryStep as step 5 of the wizard
- Add Skip buttons on step 1 (hardware) and step 2 (mode)
- Replace step 4 form submit with Review & finish -> step 5 flow
- Add Skip to summary on step 4
- Step indicator shows 'Summary' on step 5 instead of 'Step 5 of 4'
- Add deriveProviderLabel helper for provider display text
- Add handleStartChat that creates workspace then calls setChatOpen(true)
- Refactor shared workspace creation into createWorkspace() helper
2026-04-04 03:55:49 +00:00
Nexus Dev
1683e88b9f feat(32-01): create OnboardingSummaryStep component and tests
- Read-only summary card with hardware, mode, provider, root dir rows
- SummaryRow helper component with optional mono styling
- Start chatting CTA with spinner and disabled state
- 6 unit tests covering rendering, empty root dir, error, click, loading
2026-04-04 03:55:49 +00:00
Nexus Dev
d9c6d121f3 feat(31-03): add ProviderSelectionStep and wire 4-step onboarding wizard
- ProviderSelectionStep: three provider cards (Puter/Google/API key) with adapter badges
- Cards use border-primary bg-primary/5 when selected (matches ModeSelector pattern)
- PuterAuthButton/GoogleOAuthButton/ApiKeyEntryForm wired via callbacks
- NexusOnboardingWizard: step count 3→4, provider selection at step 3
- Parallel probe for hermes_local/claude_local/openclaw_gateway on wizard open
- Credentials stored after company creation (puterToken, googleOAuthStateId, apiKeyData)
- Skip always advances to step 4; Back from step 4 goes to step 3
2026-04-04 03:55:49 +00:00
Nexus Dev
f6db1f7882 feat(31-03): add puter-proxy API client and auth/key entry components
- puterProxyApi: storeToken, getAuthUrl, claimGoogleTokens, storeApiKey
- PuterAuthButton: loads Puter CDN script, triggers signIn popup, captures token
- GoogleOAuthButton: 3-second risk warning gate, opens OAuth popup, captures stateId
- ApiKeyEntryForm: provider dropdown (OpenAI/Anthropic/Groq) + password input
2026-04-04 03:55:49 +00:00
Nexus Dev
28a1d1aa5e feat(30-02): wire multi-step wizard in NexusOnboardingWizard
- Refactor to 3-step flow: hardware detection, mode selection, root directory
- Add step indicator 'Step N of 3'
- Add HardwareSummaryStep on step 1 with dynamic heading
- Add ModeSelector on step 2 with 'both' pre-selected
- Add Back buttons on steps 2 and 3
- Persist selected mode via updateNexusSettings on wizard completion
- Reset step and mode on wizard close
2026-04-04 03:55:49 +00:00
Nexus Dev
2a47c60057 feat(30-02): API client, hook, ModeSelector, and HardwareSummaryStep
- Add ui/src/api/hardware.ts with fetchHardwareInfo, fetchNexusSettings, updateNexusSettings
- Add ui/src/hooks/useHardwareInfo.ts with useQuery wrapper
- Add queryKeys.hardware.info to ui/src/lib/queryKeys.ts
- Add ModeSelector with three-card layout and selected state styling
- Add HardwareSummaryStep with skeleton loading, tier-appropriate labels, privacy frame
2026-04-04 03:55:49 +00:00
Nexus Dev
0fc748d2d4 feat(29-02): Hermes skill injection + default provider integration tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:49 +00:00
Nexus Dev
1ff3953c97 feat(29-01): adapter probe route, Hermes onboarding fallback, neutral templates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:49 +00:00
Nexus Dev
1cf1c1f038 feat(28-02,28-03): Ollama UI surface + Hermes runtime dashboard
28-02: ollamaApi client, model dropdown in config, skill badge
28-03: stateJson merge after heartbeat, HermesRuntimeCard in AgentOverview

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:49 +00:00
Nexus Dev
39327b7660 feat(28-03): merge Hermes runtime data into stateJson and add HermesRuntimeCard
- Add OllamaPsResponse interface and getOllamaMemoryUsage() to ollama.ts
- Import getOllamaMemoryUsage in heartbeat.ts
- Add hermes_local block in updateRuntimeState: COALESCE jsonb merge of hermesModel + hermesMemoryBytes
- Add HermesRuntimeCard component in AgentDetail.tsx
- Render HermesRuntimeCard in AgentOverview gated by adapterType === hermes_local
- Native skill count derived from agentsApi.skills entries with originLabel === Hermes skill
2026-04-04 03:55:49 +00:00
Nexus Dev
fbea338c23 feat(28-02): add Hermes skill badge and native skills section header in AgentSkillsTab
- Render purple "Hermes skill" badge for skills with originLabel === "Hermes skill"
- Section header shows "Hermes native skills & user-installed skills" for hermes_local agents
- Non-Hermes originLabel values continue to render as plain muted text
2026-04-04 03:55:49 +00:00
Nexus Dev
926b3a8763 feat(28-02): create ollamaApi client and Hermes Ollama model dropdown
- Add ui/src/api/ollama.ts with ollamaApi.status() and ollamaApi.models()
- Replace free-text Model input with hybrid dropdown/fallback in HermesLocalConfigFields
- Dropdown shows pulled Ollama models with * prefix for recommended entries
- Install callout shown when Ollama is absent (with link to installUrl)
- Edit mode: selecting an Ollama model atomically sets model + provider:custom + base_url
- Manual entry fallback via "Other (manual entry)..." option or when Ollama absent
- Uses useCompany() hook for companyId (consistent with AgentConfigForm pattern)
2026-04-04 03:55:48 +00:00
Nexus Dev
9ef04fd1de feat(27-01): close Hermes adapter integration gaps
- Add hermes_local to SESSIONED_LOCAL_ADAPTERS (HERM-03)
- Fix create-mode toolsets field guard (HERM-02)
- Add hermes session codec round-trip tests (HERM-04)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:48 +00:00
Nexus Dev
4b9d267f46 fix(v1.3): close 3 integration gaps from milestone audit
1. Push notifications: call sendPushToAll after streaming completes
2. Mobile offline: add useOfflineQueue + banners to MobileChatView
3. New conversation streaming: call startStream in Path 1 handleSend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:48 +00:00
Nexus Dev
862cf7fef3 feat(26-04): create push API client, usePushNotifications hook, and NotificationPermissionPrompt
- Add ui/src/api/push.ts with getVapidPublicKey, subscribe, unsubscribe methods
- Add ui/src/hooks/usePushNotifications.ts with SW pushManager subscription flow
- urlBase64ToUint8Array utility converts VAPID key for applicationServerKey
- NotificationPermissionPrompt shows after 3rd agent response (engagement gate)
- Checks nexus.notifPromptDismissed localStorage key for dismiss state
- ChatPanel tracks agentResponseCount from assistant messages and renders prompt
- Install idb package (missing dependency from plan 26-00 prerequisites)
2026-04-04 03:55:48 +00:00
Nexus Dev
471a9daaa6 feat(26): merge worktree code from plans 26-00, 26-01, 26-03
SW cache-first rewrite, React.lazy code splitting, PWA types/test stubs,
install prompt, offline banner, offline queue, ChatPanel wiring.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:48 +00:00
Nexus Dev
6b7e54bbf9 feat(26-02): create MobileChatView and wire ChatPanel for responsive layout
- MobileChatView: full-screen mobile chat using 100dvh, back button, safe-area input
- ChatPanel: conditionally renders MobileChatView on mobile via useMediaQuery
- ChatConversationList: wraps ScrollArea in PullToRefresh for mobile
- ChatInput: pb-[env(safe-area-inset-bottom)] padding + 44px Send button touch target
- ChatConversationItem: min-h-[48px] touch target per UI-SPEC
2026-04-04 03:55:48 +00:00