---
phase: 33-persistent-memory
plan: 02
subsystem: ui
tags: [personal-assistant, mode-gating, react-query, chat-ui, nexus-mode]

requires:
  - phase: 30-hardware-detection-mode-selection
    provides: fetchNexusSettings, NexusMode types, /api/nexus/settings endpoint
  - phase: 21-chat-foundation
    provides: chatApi, ChatConversationListItem, ChatMessage types
provides:
  - useNexusMode hook with isAssistantEnabled flag
  - assistantMemoryApi client for /assistant-memory/:companyId endpoints
  - PersonalAssistant page with full-page chat UI and mode gating
  - /assistant and /assistant/:conversationId routes in boardRoutes()
  - Sidebar Assistant link gated by isAssistantEnabled
affects:
  - ui/src/App.tsx
  - ui/src/components/Sidebar.tsx
  - ui/src/lib/queryKeys.ts

tech-stack:
  added: []
  patterns:
    - useNexusMode wraps fetchNexusSettings with useQuery, defaults to 'both' while loading
    - Mode gate: isAssistantEnabled = mode !== 'project_builder'
    - PersonalAssistant uses chatApi directly (no ChatPanel dependency) for standalone full-page chat
    - Optimistic user message insertion + SSE streaming via chatApi.postMessageAndStream
    - "Turn into project" button present but disabled with Coming soon tooltip (Plan 03 wires handoff)

key-files:
  created:
    - ui/src/hooks/useNexusMode.ts
    - ui/src/api/assistantMemory.ts
    - ui/src/pages/PersonalAssistant.tsx
    - ui/src/api/hardware.ts (checked out from phase-33 branch as prerequisite)
    - ui/src/api/chat.ts (checked out from phase-33 branch as prerequisite)
  modified:
    - ui/src/App.tsx
    - ui/src/components/Sidebar.tsx
    - ui/src/lib/queryKeys.ts

decisions:
  - "PersonalAssistant builds its own chat UI from chatApi directly rather than importing ChatPanel (which lives in phase-21 branch not available in this worktree)"
  - "useNexusMode defaults mode to 'both' while loading to avoid flash-redirect on initial mount"
  - "Assistant NavLink placed between Dashboard and Inbox in sidebar — prominent discovery position"
  - "Prerequisite files (hardware.ts, chat.ts) checked out from gsd/phase-33-persistent-memory branch to satisfy plan dependencies"
  - "nexus.settings query key added to queryKeys.ts for cache coherence with other components that may read nexus settings"

metrics:
  duration: 12
  completed_date: "2026-04-01"
  tasks_completed: 2
  files_changed: 8
---

# Phase 33 Plan 02: Personal Assistant Page + Mode Hook Summary

**One-liner:** Personal Assistant full-page chat UI with useNexusMode hook (isAssistantEnabled), assistantMemoryApi client, /assistant route, and mode-gated sidebar link.

## Performance

- **Duration:** ~12 min
- **Completed:** 2026-04-01
- **Tasks:** 2
- **Files modified:** 8

## Accomplishments

### Task 1: Core hooks, API client, and page

- `ui/src/hooks/useNexusMode.ts` — React Query hook fetching `/api/nexus/settings`, exposes `isAssistantEnabled = mode !== "project_builder"`, defaults to `"both"` while loading
- `ui/src/api/assistantMemory.ts` — `assistantMemoryApi` with `getMemory`, `appendFact`, `clearMemory` methods targeting `/assistant-memory/:companyId`
- `ui/src/pages/PersonalAssistant.tsx` — Full-page chat layout with:
  - Left panel: conversation list with New button
  - Right panel: message display (streaming-ready) + input bar (Enter-to-send, Shift+Enter newline)
  - Header: "Personal Assistant" title + disabled "Turn into project" button (Plan 03 stub)
  - Mode gate: redirects to `/dashboard` when `!isAssistantEnabled`
  - Optimistic user message insertion + SSE streaming via `chatApi.postMessageAndStream`
- Added `nexus.settings` key to `queryKeys.ts`

### Task 2: Route wiring and sidebar link

- Added lazy import for `PersonalAssistant` in `App.tsx`
- Added routes `/assistant` and `/assistant/:conversationId` inside `boardRoutes()`
- Added `Bot` icon import and `useNexusMode` hook to `Sidebar.tsx`
- `isAssistantEnabled` gates the "Assistant" NavLink — hidden when mode is `project_builder`

## Task Commits

1. **Task 1: Core hooks, API client, and PersonalAssistant page** — `cd8d54a5`
2. **Task 2: Route and sidebar wiring** — `1e25b55e`

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] Prerequisite files missing from parallel worktree**

- **Found during:** Task 1 setup
- **Issue:** This worktree (worktree-agent-a9598a48) is based on commit 4c8cfcd8 (phase 4), which predates the phase-21 chat system and phase-30 hardware/nexus settings files. The plan references `hardware.ts` (NexusMode types) and `chatApi` but they didn't exist in the worktree.
- **Fix:** Used `git checkout gsd/phase-33-persistent-memory -- ui/src/api/hardware.ts ui/src/api/chat.ts ui/src/App.tsx ui/src/components/Sidebar.tsx ui/src/lib/queryKeys.ts` to bring prerequisite files into the worktree from the correct branch baseline
- **Files modified:** hardware.ts, chat.ts, App.tsx, Sidebar.tsx, queryKeys.ts (checked out, not created)
- **Commit:** Included in cd8d54a5 (Task 1)

**2. [Rule 1 - Bug] PersonalAssistant uses ChatConversationListItem not ChatConversation**

- **Found during:** Task 1 (type checking)
- **Issue:** `chatApi.listConversations` returns `ChatConversationListResponse` with `items: ChatConversationListItem[]`, not `ChatConversation[]`
- **Fix:** Updated type annotations in ConversationListProps and conversations state variable to use `ChatConversationListItem`
- **Files modified:** ui/src/pages/PersonalAssistant.tsx
- **Commit:** cd8d54a5

**3. [Rule 1 - Bug] Missing required fields in optimistic ChatMessage**

- **Found during:** Task 1 (type checking)
- **Issue:** Optimistic message creation was missing `agentId: null` and `messageType: null` required by the ChatMessage interface
- **Fix:** Added all required ChatMessage fields to the optimistic update object with a `satisfies ChatMessage` assertion
- **Files modified:** ui/src/pages/PersonalAssistant.tsx
- **Commit:** cd8d54a5

## Known Stubs

**"Turn into project" button** — `ui/src/pages/PersonalAssistant.tsx` line 269
- Intentional stub per plan spec: button present but disabled with tooltip "Coming soon"
- Plan 03 wires the actual assistant→project handoff
- Does not prevent the plan's goal (assistant page with chat works fully)

## TypeScript Verification

- Worktree lacks node_modules (parallel execution setup) — cannot run `tsc` directly in worktree
- Verified against main `/opt/nexus` installation: `tsc -b ui/tsconfig.json --noEmit`
- Zero new TypeScript errors introduced by plan-02 changes
- Pre-existing error in AgentConfigForm.tsx (detectModel) is out-of-scope and pre-dates this plan

## Self-Check: PASSED

Files created/modified:
- FOUND: /opt/nexus/.claude/worktrees/agent-a9598a48/ui/src/hooks/useNexusMode.ts
- FOUND: /opt/nexus/.claude/worktrees/agent-a9598a48/ui/src/api/assistantMemory.ts
- FOUND: /opt/nexus/.claude/worktrees/agent-a9598a48/ui/src/pages/PersonalAssistant.tsx
- FOUND: /opt/nexus/.claude/worktrees/agent-a9598a48/ui/src/App.tsx
- FOUND: /opt/nexus/.claude/worktrees/agent-a9598a48/ui/src/components/Sidebar.tsx
- FOUND: /opt/nexus/.claude/worktrees/agent-a9598a48/ui/src/lib/queryKeys.ts

Commits:
- FOUND: cd8d54a5
- FOUND: 1e25b55e
