ai-agent-session-center 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +484 -429
- package/docs/3D/ADAPTATION_GUIDE.md +592 -0
- package/docs/3D/index.html +754 -0
- package/docs/AGENT_TEAM_TASKS.md +716 -0
- package/docs/CYBERDROME_V2_SPEC.md +531 -0
- package/docs/ERROR_185_ANALYSIS.md +263 -0
- package/docs/PLATFORM_FEATURES_PROMPT.md +296 -0
- package/docs/SESSION_DETAIL_FEATURES.md +98 -0
- package/docs/_3d_multimedia_features.md +1080 -0
- package/docs/_frontend_features.md +1057 -0
- package/docs/_server_features.md +1077 -0
- package/docs/session-duplication-fixes.md +271 -0
- package/docs/session-terminal-linkage.md +412 -0
- package/package.json +63 -5
- package/public/apple-touch-icon.svg +21 -0
- package/public/css/dashboard.css +0 -161
- package/public/css/detail-panel.css +25 -0
- package/public/css/layout.css +18 -1
- package/public/css/modals.css +0 -26
- package/public/css/settings.css +0 -150
- package/public/css/terminal.css +34 -0
- package/public/favicon.svg +18 -0
- package/public/index.html +6 -26
- package/public/js/alarmManager.js +0 -21
- package/public/js/app.js +21 -7
- package/public/js/detailPanel.js +63 -64
- package/public/js/historyPanel.js +61 -55
- package/public/js/quickActions.js +132 -48
- package/public/js/sessionCard.js +5 -20
- package/public/js/sessionControls.js +8 -0
- package/public/js/settingsManager.js +0 -142
- package/server/apiRouter.js +60 -15
- package/server/apiRouter.ts +774 -0
- package/server/approvalDetector.ts +94 -0
- package/server/authManager.ts +144 -0
- package/server/autoIdleManager.ts +110 -0
- package/server/config.ts +121 -0
- package/server/constants.ts +150 -0
- package/server/db.ts +475 -0
- package/server/hookInstaller.d.ts +3 -0
- package/server/hookProcessor.ts +108 -0
- package/server/hookRouter.ts +18 -0
- package/server/hookStats.ts +116 -0
- package/server/index.js +15 -1
- package/server/index.ts +230 -0
- package/server/logger.ts +75 -0
- package/server/mqReader.ts +349 -0
- package/server/portManager.ts +55 -0
- package/server/processMonitor.ts +239 -0
- package/server/serverConfig.ts +29 -0
- package/server/sessionMatcher.js +17 -6
- package/server/sessionMatcher.ts +403 -0
- package/server/sessionStore.js +109 -3
- package/server/sessionStore.ts +1145 -0
- package/server/sshManager.js +167 -24
- package/server/sshManager.ts +671 -0
- package/server/teamManager.ts +289 -0
- package/server/wsManager.ts +200 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# React Error #185: Complete Architecture Analysis
|
|
2
|
+
|
|
3
|
+
> **Error**: "Maximum update depth exceeded" when clicking a 3D robot to open session detail panel.
|
|
4
|
+
> **Impact**: Crashes the WebGL context (`THREE.WebGLRenderer: Context Lost`), requires page reload.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. What is React Error #185?
|
|
9
|
+
|
|
10
|
+
React Error #185 fires when a component triggers a state update during rendering, which triggers another render, which triggers another update — an infinite `setState → render → setState` loop. React detects this after ~50 nested updates and throws.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 2. Current Click-to-Detail Architecture
|
|
15
|
+
|
|
16
|
+
### Component Tree
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
App.tsx (React DOM root)
|
|
20
|
+
├── AppLayout
|
|
21
|
+
│ ├── Header, NavBar, ActivityFeed (React DOM)
|
|
22
|
+
│ ├── <LiveView /> → <CyberdromeScene />
|
|
23
|
+
│ │ ├── <Canvas> ← R3F RECONCILER BOUNDARY
|
|
24
|
+
│ │ │ ├── SceneContent
|
|
25
|
+
│ │ │ │ ├── SessionRobot ×N (memo'd)
|
|
26
|
+
│ │ │ │ │ ├── Robot3DModel (pure Three.js meshes)
|
|
27
|
+
│ │ │ │ │ ├── RobotLabel (drei <Text> + <Billboard>)
|
|
28
|
+
│ │ │ │ │ ├── RobotDialogue (drei <Text> + <Billboard>)
|
|
29
|
+
│ │ │ │ │ └── StatusParticles (Three.js Points)
|
|
30
|
+
│ │ │ │ ├── SubagentConnections (Three.js Lines)
|
|
31
|
+
│ │ │ │ └── CyberdromeEnvironment (Three.js meshes)
|
|
32
|
+
│ │ │ ├── CameraController
|
|
33
|
+
│ │ │ └── OrbitControls
|
|
34
|
+
│ │ ├── MapControls (DOM overlay, outside Canvas)
|
|
35
|
+
│ │ ├── SceneOverlay (DOM overlay, outside Canvas)
|
|
36
|
+
│ │ └── RobotListSidebar (DOM overlay, outside Canvas)
|
|
37
|
+
│ ├── DetailPanel ← OUTSIDE Canvas, subscribes to selectedSessionId
|
|
38
|
+
│ ├── SettingsPanel
|
|
39
|
+
│ └── Modals (Kill, Alert, Summarize)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### The Click Flow
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
1. User clicks robot mesh
|
|
46
|
+
↓
|
|
47
|
+
2. R3F raycasts and fires onClick on <group> in SessionRobot
|
|
48
|
+
↓
|
|
49
|
+
3. handleClick: setTimeout(() => onSelect(session.sessionId), 0)
|
|
50
|
+
↓ (deferred to next event loop tick)
|
|
51
|
+
4. onSelect = handleSelect in SceneContent:
|
|
52
|
+
a. selectSession(sessionId) → updates sessionStore.selectedSessionId
|
|
53
|
+
b. flyTo(pos, lookAt) → updates cameraStore.animation
|
|
54
|
+
↓
|
|
55
|
+
5. Zustand notifies ALL subscribers synchronously:
|
|
56
|
+
↓
|
|
57
|
+
SUBSCRIBERS TO selectedSessionId (sessionStore):
|
|
58
|
+
a. DetailPanel (React DOM) → re-renders, shows slide-in panel
|
|
59
|
+
b. RobotListSidebar (React DOM) → re-renders, highlights selected entry
|
|
60
|
+
c. SummarizeModal (React DOM) → conditional re-render
|
|
61
|
+
d. KillConfirmModal (React DOM) → conditional re-render
|
|
62
|
+
e. AlertModal (React DOM) → conditional re-render
|
|
63
|
+
f. useKeyboardShortcuts hook → no visual re-render
|
|
64
|
+
|
|
65
|
+
SUBSCRIBERS TO sessions (sessionStore):
|
|
66
|
+
a. SceneContent (R3F) → NOT triggered (sessions Map unchanged)
|
|
67
|
+
b. SubagentConnections (R3F) → NOT triggered (sessions Map unchanged)
|
|
68
|
+
c. RobotListSidebar (React DOM) → also subscribes to sessions
|
|
69
|
+
|
|
70
|
+
SUBSCRIBERS TO animation (cameraStore):
|
|
71
|
+
a. CameraController (R3F) → triggers useFrame lerp animation
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### The Dual-Reconciler Problem
|
|
75
|
+
|
|
76
|
+
React Three Fiber maintains its OWN React reconciler separate from React DOM's reconciler. Both share the same React runtime. When a Zustand store update fires:
|
|
77
|
+
|
|
78
|
+
1. **React DOM reconciler** processes subscribers (DetailPanel, RobotListSidebar, modals)
|
|
79
|
+
2. **R3F reconciler** processes subscribers (SceneContent, CameraController)
|
|
80
|
+
|
|
81
|
+
These two reconcilers can **interleave** — one reconciler's flush can trigger effects that cause the other to flush, creating a ping-pong cascade.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 3. Why Previous Fixes Failed
|
|
86
|
+
|
|
87
|
+
### Fix Attempt 1: `startTransition(() => onSelect(...))`
|
|
88
|
+
**Why it failed**: `startTransition` defers the update within ONE reconciler, but doesn't prevent cross-reconciler cascades. React DOM's deferred update can still trigger R3F's reconciler to flush synchronously.
|
|
89
|
+
|
|
90
|
+
### Fix Attempt 2: `setTimeout(() => onSelect(...), 0)`
|
|
91
|
+
**Why it partially works**: Moves the store update completely out of R3F's render loop. But the problem persists because:
|
|
92
|
+
- The `setTimeout` fires and calls `selectSession(sessionId)` + `flyTo()`
|
|
93
|
+
- `selectSession` synchronously notifies Zustand subscribers
|
|
94
|
+
- DetailPanel (React DOM) re-renders → mounts heavy child components
|
|
95
|
+
- R3F's animation frame may be processing simultaneously
|
|
96
|
+
- If any R3F component also triggers a state update (even indirectly), cascade begins
|
|
97
|
+
|
|
98
|
+
### Fix Attempt 3: `memo(SessionRobot)` with granular comparator
|
|
99
|
+
**Why it helps but isn't sufficient**: Prevents SessionRobots from re-rendering when `sessions` Map gets a new reference but their specific session data hasn't changed. However, the `sessions` Map reference doesn't change on `selectSession` (only `selectedSessionId` changes), so this memo was never the actual trigger.
|
|
100
|
+
|
|
101
|
+
### Fix Attempt 4: `seatedRef` / `isHoveredRef` / `dialogueRef`
|
|
102
|
+
**Why it helps but isn't the root cause**: Eliminates all `useState` from SessionRobot's render cycle. No React state updates happen inside R3F's tree from these refs. Good defensive measure, but the crash still happens because the problem is in the cross-reconciler interaction triggered by Zustand, not by local state.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 4. Root Cause: The Zustand Cross-Reconciler Bridge
|
|
107
|
+
|
|
108
|
+
The **fundamental** problem is:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
sessionStore.selectSession(id)
|
|
112
|
+
→ Zustand.setState({ selectedSessionId: id })
|
|
113
|
+
→ Zustand notifies ALL subscribers SYNCHRONOUSLY
|
|
114
|
+
→ React DOM subscribers (DetailPanel) flush
|
|
115
|
+
→ React DOM flush may trigger layout effects
|
|
116
|
+
→ R3F's next frame picks up any pending work
|
|
117
|
+
→ If any R3F component has pending effects → cascade
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Specifically, when Zustand notifies subscribers, it calls `useSyncExternalStore` in both reconcilers. React 19's concurrent features mean the DOM reconciler may start a render that the R3F reconciler then sees as a pending update.
|
|
121
|
+
|
|
122
|
+
### The Dangerous Components Inside R3F's Canvas
|
|
123
|
+
|
|
124
|
+
Even after all fixes, these components inside `<Canvas>` still subscribe to Zustand stores:
|
|
125
|
+
|
|
126
|
+
| Component | Store | Selector | Risk |
|
|
127
|
+
|-----------|-------|----------|------|
|
|
128
|
+
| SceneContent | sessionStore | `s.sessions` | Medium — triggers on any session update |
|
|
129
|
+
| SceneContent | sessionStore | `s.selectSession` | Low — stable fn ref |
|
|
130
|
+
| SceneContent | cameraStore | `s.flyTo` | Low — stable fn ref |
|
|
131
|
+
| SubagentConnections | sessionStore | `s.sessions` | Medium — triggers on any session update |
|
|
132
|
+
| Robot3DModel | settingsStore | `s.animationIntensity` | Low |
|
|
133
|
+
| Robot3DModel | settingsStore | `s.animationSpeed` | Low |
|
|
134
|
+
| SessionRobot | roomStore | `s.getRoomForSession` | Low — stable fn ref |
|
|
135
|
+
| SessionRobot | settingsStore | `s.characterModel` | Low |
|
|
136
|
+
| SessionRobot | settingsStore | `s.labelSettings` | Low |
|
|
137
|
+
| CameraController | cameraStore | `s.animation` | **HIGH — triggers on flyTo()** |
|
|
138
|
+
| SceneThemeSync | settingsStore | `s.themeName` | Low |
|
|
139
|
+
|
|
140
|
+
**CameraController** is the likely remaining trigger. When `handleSelect` calls `flyTo()`, CameraController re-renders because `s.animation` changes. This re-render happens **inside R3F's reconciler** at the same time that DetailPanel is re-rendering in React DOM's reconciler.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 5. The Remaining Hazard: `SubagentConnections`
|
|
145
|
+
|
|
146
|
+
`SubagentConnections` subscribes to `useSessionStore((s) => s.sessions)`. While `selectSession` doesn't change `sessions`, the `updateSession` action (from WebSocket) creates a new `Map` reference every time. If a WebSocket update arrives at the exact moment the click handler fires, SubagentConnections re-renders inside R3F while DetailPanel re-renders in DOM — cross-reconciler cascade.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 6. Solution: Decouple the Two Reconcilers Completely
|
|
151
|
+
|
|
152
|
+
The only reliable fix is to ensure **NO shared reactive state** bridges the R3F and DOM reconcilers. The R3F tree should read data imperatively (via refs or callbacks), never via Zustand subscriptions that can trigger simultaneous re-renders.
|
|
153
|
+
|
|
154
|
+
### Option A: Event-Based Communication (Recommended)
|
|
155
|
+
|
|
156
|
+
Replace the Zustand `selectedSessionId` bridge with a custom event:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
Robot click → dispatch CustomEvent('select-session', { sessionId })
|
|
160
|
+
↓
|
|
161
|
+
DOM listener (outside Canvas) → updates local React state
|
|
162
|
+
↓
|
|
163
|
+
DetailPanel renders from local state (never touches R3F)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
R3F components never subscribe to `selectedSessionId`. The click handler dispatches a DOM event. A DOM-only listener (in CyberdromeScene's wrapper or App) catches it and calls `selectSession`. This ensures:
|
|
167
|
+
|
|
168
|
+
- R3F's reconciler has ZERO work to do when a session is selected
|
|
169
|
+
- React DOM's reconciler handles DetailPanel independently
|
|
170
|
+
- No cross-reconciler flush interleaving
|
|
171
|
+
|
|
172
|
+
### Option B: Ref-Based Bridge
|
|
173
|
+
|
|
174
|
+
Store `selectedSessionId` in a `useRef` at the Canvas boundary. R3F reads it imperatively in `useFrame`. DOM components use the normal Zustand subscription. No R3F component subscribes to `selectedSessionId`.
|
|
175
|
+
|
|
176
|
+
### Option C: Move ALL Zustand Subscriptions Outside Canvas
|
|
177
|
+
|
|
178
|
+
Move `SceneContent` and `SubagentConnections` to read sessions via a ref that's updated outside Canvas. Pass session data as props computed in the DOM layer. This eliminates all Zustand subscriptions from inside `<Canvas>`.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 7. Additional Hazards to Fix
|
|
183
|
+
|
|
184
|
+
### A. `SubagentConnections` sessions subscription
|
|
185
|
+
Subscribes to `s.sessions` inside Canvas. Every WebSocket `updateSession` creates a new Map, causing re-render. Should use a ref or compute connections outside Canvas.
|
|
186
|
+
|
|
187
|
+
### B. `SceneContent` sessions subscription
|
|
188
|
+
Same issue — `useSessionStore((s) => s.sessions)` re-renders SceneContent on every session update. Should receive sessions as a prop from outside Canvas, or use a ref.
|
|
189
|
+
|
|
190
|
+
### C. `Robot3DModel` settingsStore subscriptions
|
|
191
|
+
`animationIntensity` and `animationSpeed` subscriptions cause Robot3DModel to re-render when settings change. Should read via `useSettingsStore.getState()` inside useFrame instead of subscribing.
|
|
192
|
+
|
|
193
|
+
### D. `CameraController` cameraStore subscription
|
|
194
|
+
Re-renders when `flyTo()` is called. The animation data should be read via ref in useFrame, not via subscription.
|
|
195
|
+
|
|
196
|
+
### E. `RobotListSidebar` calls `selectSession` directly
|
|
197
|
+
Line 202: `selectSession(sessionId)` — this DOM component calls the same function that triggers the cross-reconciler cascade. Should use the same decoupled mechanism as the robot click.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 8. Files Involved
|
|
202
|
+
|
|
203
|
+
| File | Role | Issues |
|
|
204
|
+
|------|------|--------|
|
|
205
|
+
| `src/App.tsx:53` | Mounts `<DetailPanel />` | None — correct placement outside Canvas |
|
|
206
|
+
| `src/routes/LiveView.tsx` | Wraps `<CyberdromeScene />` | None |
|
|
207
|
+
| `src/components/3d/CyberdromeScene.tsx:89-103` | `SceneContent` subscribes to `sessions` + calls `selectSession` + `flyTo` | **PRIMARY ISSUE**: Zustand subscriptions inside Canvas |
|
|
208
|
+
| `src/components/3d/CyberdromeScene.tsx:74-130` | `SceneContent` function | Sessions subscription triggers re-render |
|
|
209
|
+
| `src/components/3d/SessionRobot.tsx:431-438` | `handleClick` with setTimeout | Good fix but insufficient |
|
|
210
|
+
| `src/components/3d/SessionRobot.tsx:668-692` | `memo` wrapper | Good but sessions Map ref unchanged on select |
|
|
211
|
+
| `src/components/3d/Robot3DModel.tsx:100-102` | Settings subscriptions | Should use getState() in useFrame |
|
|
212
|
+
| `src/components/3d/RobotLabel.tsx` | Pure WebGL (drei Text) | **SAFE** — no Html portals, no store subscriptions |
|
|
213
|
+
| `src/components/3d/RobotDialogue.tsx` | Pure WebGL (drei Text) | **SAFE** — ref-based, no store subscriptions |
|
|
214
|
+
| `src/components/3d/SubagentConnections.tsx:89` | `sessions` subscription | **DANGEROUS** — re-renders inside Canvas on every session update |
|
|
215
|
+
| `src/components/3d/CameraController.tsx` | `animation` subscription | **DANGEROUS** — re-renders inside Canvas on flyTo |
|
|
216
|
+
| `src/components/session/DetailPanel.tsx:68` | `selectedSessionId` subscription | Fine — outside Canvas |
|
|
217
|
+
| `src/components/3d/RobotListSidebar.tsx:180-183` | Multiple store subscriptions | Fine — outside Canvas (DOM overlay) |
|
|
218
|
+
| `src/stores/sessionStore.ts:56` | `selectSession` action | Triggers all subscribers synchronously |
|
|
219
|
+
| `src/stores/cameraStore.ts` | `flyTo` action | Triggers CameraController re-render inside Canvas |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 9. Recommended Redevelopment Plan
|
|
224
|
+
|
|
225
|
+
### Phase 1: Eliminate ALL Zustand subscriptions from inside `<Canvas>`
|
|
226
|
+
|
|
227
|
+
1. **SceneContent**: Remove `useSessionStore` and `useCameraStore` subscriptions. Receive `sessions`, `selectSession`, `flyTo` as props from CyberdromeScene (DOM layer).
|
|
228
|
+
|
|
229
|
+
2. **SubagentConnections**: Remove `useSessionStore` subscription. Receive `sessions` or precomputed connections as props.
|
|
230
|
+
|
|
231
|
+
3. **Robot3DModel**: Replace `useSettingsStore` subscriptions with `useSettingsStore.getState()` reads inside useFrame.
|
|
232
|
+
|
|
233
|
+
4. **CameraController**: Replace `useCameraStore((s) => s.animation)` subscription with polling in useFrame via `useCameraStore.getState()`.
|
|
234
|
+
|
|
235
|
+
5. **SceneThemeSync**: Replace `useSettingsStore` subscription with receiving theme as a prop.
|
|
236
|
+
|
|
237
|
+
### Phase 2: Replace cross-reconciler selection bridge
|
|
238
|
+
|
|
239
|
+
1. Robot click dispatches `window.dispatchEvent(new CustomEvent('robot-select', { detail: { sessionId } }))`.
|
|
240
|
+
|
|
241
|
+
2. CyberdromeScene (DOM wrapper) listens for 'robot-select' and calls `selectSession()` + `flyTo()`.
|
|
242
|
+
|
|
243
|
+
3. RobotListSidebar dispatches same event instead of calling `selectSession()` directly.
|
|
244
|
+
|
|
245
|
+
4. DetailPanel remains unchanged — subscribes to `selectedSessionId` in React DOM, which is safe.
|
|
246
|
+
|
|
247
|
+
### Phase 3: Verify no cross-reconciler state bridges remain
|
|
248
|
+
|
|
249
|
+
- Grep for `useSessionStore`, `useCameraStore`, `useSettingsStore`, `useRoomStore` inside any component rendered within `<Canvas>`.
|
|
250
|
+
- Each must use either `.getState()` in useFrame/callbacks, or receive data as props from the DOM layer.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 10. Summary
|
|
255
|
+
|
|
256
|
+
The error persists because **Zustand store updates synchronously notify subscribers in BOTH React reconcilers** (R3F and DOM). When the user clicks a robot:
|
|
257
|
+
|
|
258
|
+
1. `selectSession()` notifies DOM subscribers (DetailPanel) → heavy re-render
|
|
259
|
+
2. `flyTo()` notifies R3F subscribers (CameraController) → re-render inside Canvas
|
|
260
|
+
3. If a WebSocket `updateSession` arrives simultaneously, SceneContent + SubagentConnections re-render
|
|
261
|
+
4. Both reconcilers flush work, potentially interleaving, causing the infinite loop
|
|
262
|
+
|
|
263
|
+
**The fix is architectural**: no component inside `<Canvas>` should subscribe to Zustand stores. All data flows into the Canvas via props or is read imperatively via `.getState()` in useFrame callbacks.
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Prompt: Write Comprehensive Platform Features Document
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
You are documenting a full-featured **AI Agent Session Center** — a localhost dashboard (port 3333) that monitors all active AI coding agent sessions (Claude Code, Gemini CLI, Codex) in real-time via hook scripts. The platform has both a **legacy vanilla JS + CSS animations frontend** and a **React Three Fiber 3D Cyberdrome scene** built with TypeScript + Vite. The document must serve as a complete specification for rebuilding the platform from scratch.
|
|
6
|
+
|
|
7
|
+
## Deliverable
|
|
8
|
+
|
|
9
|
+
Write a single Markdown file `docs/PLATFORM_FEATURES.md` that exhaustively documents every feature. The document must be detailed enough that a developer who has never seen the codebase can rebuild the entire platform feature-for-feature.
|
|
10
|
+
|
|
11
|
+
## Required Sections
|
|
12
|
+
|
|
13
|
+
### 1. Platform Overview
|
|
14
|
+
- Purpose, target users, supported AI CLIs (Claude Code, Gemini CLI, Codex, OpenClaw)
|
|
15
|
+
- Tech stack: Node.js 18+ ESM, Express 5, ws 8, node-pty, better-sqlite3, React 19 + TypeScript + Vite, React Three Fiber, Zustand, IndexedDB, xterm.js
|
|
16
|
+
- Architecture diagram (ASCII): Hook script → File MQ → Server → WebSocket → Browser
|
|
17
|
+
- Latency expectations per stage (jq 2-5ms, file append 0.1ms, fs.watch 0-10ms, processing 0.5ms, total 3-17ms)
|
|
18
|
+
|
|
19
|
+
### 2. Hook Delivery Pipeline
|
|
20
|
+
- **Primary path**: bash hook script → jq enrichment (PID, TTY, terminal detection, tab_id, tmux, agent metadata) → atomic POSIX append to `/tmp/claude-session-center/queue.jsonl` → background subshell + disown
|
|
21
|
+
- **Fallback**: curl HTTP POST to `localhost:PORT/api/hooks` (1s connect, 3s total timeout)
|
|
22
|
+
- **MQ Reader**: fs.watch() + 10ms debounce, 500ms fallback poll, 5s health check, byte-offset tracking, partial line handling, 1MB truncation threshold
|
|
23
|
+
- **Hook validation**: session_id required (max 256), event name in known set, optional claude_pid/timestamp validation
|
|
24
|
+
- **Enriched fields**: claude_pid, hook_sent_at, tty_path, term_program/version, vscode_pid, tab_id (iTerm/kitty/Warp/WezTerm/Apple Terminal), tmux session/pane, terminal env vars, agent metadata (parent_session_id, team_name, agent_name/type/id/color)
|
|
25
|
+
- **TTY detection cache**: per-PPID in `/tmp/claude-tty-cache/` to avoid repeated ps calls
|
|
26
|
+
- **Tab title management**: escape sequences to terminal for state-changing events
|
|
27
|
+
|
|
28
|
+
### 3. Hook Events and Density Levels
|
|
29
|
+
- All 14 Claude events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, Stop, Notification, SubagentStart, SubagentStop, TeammateIdle, TaskCompleted, PreCompact, SessionEnd
|
|
30
|
+
- 7 Gemini events: SessionStart, BeforeAgent, BeforeTool, AfterTool, AfterAgent, SessionEnd, Notification
|
|
31
|
+
- 1 Codex event: agent-turn-complete
|
|
32
|
+
- 3 density levels (high=all 14, medium=12 excl TeammateIdle/PreCompact, low=5 Start/Prompt/Permission/Stop/End)
|
|
33
|
+
- Hook installation: copies scripts to `~/.claude/hooks/`, atomic JSON merge into `~/.claude/settings.json`, Gemini `~/.gemini/settings.json`, Codex `~/.codex/config.toml`, Windows PowerShell variant
|
|
34
|
+
|
|
35
|
+
### 4. Session State Machine
|
|
36
|
+
- Full state diagram: idle → prompting → working → approval/input → waiting → idle → ended
|
|
37
|
+
- All transitions with trigger events
|
|
38
|
+
- Session object: 50+ fields (sessionId, projectPath, projectName, label, title, status, animationState, emote, timestamps, promptHistory[50], toolUsage Map, totalToolCalls, model, subagentCount, toolLog[200], responseLog[50], events[50], archived, source, pendingTool, waitingDetail, cachedPid, queueCount, terminalId, sshHost/Command/Config, teamId/Role, agentName/Type/Color, characterModel, accentColor, summary, previousSessions[5], tmuxPaneId, backendType)
|
|
39
|
+
- Event ring buffer (500 events) for WebSocket replay on reconnect
|
|
40
|
+
- Snapshot persistence: atomic write every 10s to `/tmp/claude-session-center/sessions-snapshot.json`, loaded on restart
|
|
41
|
+
- Debounced broadcast: 50ms window per sessionId
|
|
42
|
+
|
|
43
|
+
### 5. Session Matching (5-Priority System)
|
|
44
|
+
- Priority 0: pendingResume + terminal ID or workDir match
|
|
45
|
+
- Priority 0.5: auto-link to snapshot-restored ended session by projectPath (within 30 min, single candidate)
|
|
46
|
+
- Priority 1: agent_terminal_id env var direct match → re-key session
|
|
47
|
+
- Priority 2: tryLinkByWorkDir() pending link from sshManager
|
|
48
|
+
- Priority 3: scan CONNECTING sessions by normalized path (exactly 1 match)
|
|
49
|
+
- Priority 4: PID parent check via getTerminalByPtyChild()
|
|
50
|
+
- Fallback: display-only card with detected terminal source (VS Code, JetBrains IDEs, iTerm, Warp, Kitty, Ghostty, Alacritty, WezTerm, Hyper, Apple Terminal, tmux)
|
|
51
|
+
- Session re-keying: transfers old→new sessionId, resets state, preserves previousSessions chain, migrates SQLite FK
|
|
52
|
+
|
|
53
|
+
### 6. Approval Detection
|
|
54
|
+
- Tool category timeouts: fast(3s: Read/Write/Edit/Grep/Glob/NotebookEdit), userInput(3s: AskUser/EnterPlan/ExitPlan), medium(15s: WebFetch/WebSearch), slow(8s: Bash/Task)
|
|
55
|
+
- hasChildProcesses check for slow tools (pgrep -P)
|
|
56
|
+
- PermissionRequest event as reliable signal (medium+ density)
|
|
57
|
+
- Auto-idle timeouts: prompting→30s→waiting, waiting→120s→idle, working→180s→idle, approval/input→600s→idle
|
|
58
|
+
|
|
59
|
+
### 7. Team and Subagent Tracking
|
|
60
|
+
- Auto-detection by matching cwd paths (parent/child within 10s window)
|
|
61
|
+
- Explicit via CLAUDE_CODE_PARENT_SESSION_ID env var
|
|
62
|
+
- Team config reader: `~/.claude/teams/{name}/config.json` with member metadata
|
|
63
|
+
- Team terminal: attach to member's tmux pane
|
|
64
|
+
- Cleanup: 15s delay after parent ends if all children also ended
|
|
65
|
+
- Parent tracks subagentCount
|
|
66
|
+
|
|
67
|
+
### 8. SSH/PTY Terminal Management
|
|
68
|
+
- 4 creation modes: direct SSH, tmux attach, tmux wrap, resume with fallback (`claude --resume ID || claude --continue`)
|
|
69
|
+
- node-pty for PTY multiplexing, max 10 concurrent
|
|
70
|
+
- Shell ready detection: watches for prompt patterns ($/%/#/>), 100ms settle, 5s/15s timeouts
|
|
71
|
+
- 128KB ring buffer per terminal, replayed on (re)subscribe
|
|
72
|
+
- Pending workDir links for session matching (60s expiry)
|
|
73
|
+
- API key injection via PTY env vars
|
|
74
|
+
- AGENT_MANAGER_TERMINAL_ID env var exported on remote shell
|
|
75
|
+
- Input validation: rejects shell metacharacters
|
|
76
|
+
|
|
77
|
+
### 9. WebSocket Protocol
|
|
78
|
+
- Server→Client: snapshot, session_update, session_removed, team_update, hook_stats, terminal_output (base64), terminal_ready, terminal_closed, clearBrowserDb, replay
|
|
79
|
+
- Client→Server: terminal_input, terminal_resize, terminal_disconnect, terminal_subscribe, update_queue_count, replay (sinceSeq)
|
|
80
|
+
- Heartbeat: ping 30s, pong timeout 10s
|
|
81
|
+
- Backpressure: skip non-critical if send buffer >1MB
|
|
82
|
+
- hook_stats throttled to 1/s
|
|
83
|
+
|
|
84
|
+
### 10. REST API (All Endpoints)
|
|
85
|
+
- Hook/Stats: GET/POST hook-stats, GET mq-stats
|
|
86
|
+
- Sessions: GET source, POST resume, POST kill, DELETE, PUT title, PUT label, PUT accent-color, POST summarize (rate limited 2 concurrent)
|
|
87
|
+
- Terminals: POST create (max 10), GET list, DELETE close
|
|
88
|
+
- SSH: GET ssh-keys, POST tmux-sessions
|
|
89
|
+
- Teams: GET config, POST member terminal
|
|
90
|
+
- SQLite DB: GET/DELETE sessions (search/filter/paginate), GET projects, GET search (full-text), GET/POST/DELETE notes
|
|
91
|
+
- Analytics: GET summary, tools, projects, heatmap (7×24)
|
|
92
|
+
- Admin: POST reset, GET/POST/POST hooks status/install/uninstall
|
|
93
|
+
- Auth: GET status, POST login, POST logout
|
|
94
|
+
|
|
95
|
+
### 11. Authentication
|
|
96
|
+
- Optional password protection via server-config.json passwordHash
|
|
97
|
+
- scrypt-based hashing with timing-safe comparison
|
|
98
|
+
- 24-hour in-memory tokens, hourly cleanup
|
|
99
|
+
- Token sources: HTTP cookie, Authorization Bearer header, ?token= query (for WS)
|
|
100
|
+
- Hook endpoints bypass auth
|
|
101
|
+
|
|
102
|
+
### 12. SQLite Persistence
|
|
103
|
+
- better-sqlite3, WAL mode, `data/sessions.db`
|
|
104
|
+
- Tables: sessions, prompts, responses, tool_calls, events, notes (all with UNIQUE constraints)
|
|
105
|
+
- Upsert pattern, batch insert transactions, cascade delete
|
|
106
|
+
- Session ID migration for resume re-keying
|
|
107
|
+
- Full-text LIKE search across prompts/responses
|
|
108
|
+
- Analytics: prepared statements for summary, tools, projects, heatmap
|
|
109
|
+
|
|
110
|
+
### 13. IndexedDB Client Persistence
|
|
111
|
+
- Database: claude-dashboard v2
|
|
112
|
+
- 12 object stores: sessions, prompts, responses, toolCalls, events, notes, promptQueue, alerts, sshProfiles, settings, summaryPrompts, teams
|
|
113
|
+
- Batched writes: 200ms flush or 20-item threshold
|
|
114
|
+
- Session dedup by timestamp on persist
|
|
115
|
+
- Queue CRUD with reorder/move-between-sessions
|
|
116
|
+
- Full-text search with `<mark>` highlighting
|
|
117
|
+
- Timeline queries with granularity (hour/day/week/month)
|
|
118
|
+
- Default seeding: 6 settings + 5 summary prompt templates
|
|
119
|
+
|
|
120
|
+
### 14. CSS Character System (2D View)
|
|
121
|
+
- 20 pure-CSS characters: robot, cat, alien, ghost, orb, dragon, penguin, octopus, mushroom, fox, unicorn, jellyfish, owl, bat, cactus, slime, pumpkin, yeti, crystal, bee
|
|
122
|
+
- 8-color accent palette, round-robin assignment
|
|
123
|
+
- Per-session character override + global setting
|
|
124
|
+
- Status→animation mapping: idle→Idle, prompting→Wave+Walking, working→Running, approval/input→Waiting, waiting→ThumbsUp+Waiting, ended→Death
|
|
125
|
+
- Character lifecycle: create, update (data-status attr), switch, markChecked, remove
|
|
126
|
+
|
|
127
|
+
### 15. 3D Cyberdrome Scene
|
|
128
|
+
- React Three Fiber + Three.js, zero useState inside Canvas (all refs), zero Zustand subscriptions inside Canvas
|
|
129
|
+
- CustomEvent pattern: robot click → window.dispatchEvent('robot-select') → DOM handler → Zustand update
|
|
130
|
+
- Canvas: PCFSoftShadowMap, ACESFilmicToneMapping, FOV 50, OrbitControls with damping
|
|
131
|
+
- Map controls overlay: zoom in/out, top-down, reset view
|
|
132
|
+
- Dynamic room system: 4 rooms/row, ROOM_SIZE=12, ROOM_GAP=5, 8 desks/room (3 north + 2 west + 3 east)
|
|
133
|
+
- Two doors per room (north + south, DOOR_GAP=4)
|
|
134
|
+
- Corridor workstations: 10 desks south of rooms
|
|
135
|
+
- Casual areas north: Coffee Lounge (6 seats, zone -2), Gym (10 stations, zone -3)
|
|
136
|
+
- Wall collision rects for navigation
|
|
137
|
+
- Door waypoints with inside/outside vectors, nearest-door pathfinding
|
|
138
|
+
- Robot navigation AI: 4 modes (WALK/GOTO/SIT/IDLE), status-based desk seeking, cross-room pathfinding via doors, position persistence to sessionStorage
|
|
139
|
+
- Robot dialogue bubbles: status-persistent + tool-transient, zero useState (ref-based)
|
|
140
|
+
- Robot labels: Billboard+Text WebGL rendering, fontSize from settings, title+status dot+alert banner
|
|
141
|
+
- Status particles per robot
|
|
142
|
+
- Subagent connection beams between parent/child robots
|
|
143
|
+
- Scene themes synced from UI themeName setting
|
|
144
|
+
- Camera controller: flyTo with smooth interpolation
|
|
145
|
+
- Room labels in 3D space
|
|
146
|
+
- RobotListSidebar: DOM overlay with close button, status sorting, fly-to-robot on click
|
|
147
|
+
|
|
148
|
+
### 16. Session Cards (2D View)
|
|
149
|
+
- Card creation with 100ms debounce
|
|
150
|
+
- Pinned sessions (localStorage), muted sessions (per-session + global)
|
|
151
|
+
- Top-5 tool bars with proportional fill
|
|
152
|
+
- Toast notifications (error always, info configurable)
|
|
153
|
+
- Label badge, team indicator, editable title
|
|
154
|
+
- Status border glow pulsing
|
|
155
|
+
- Drag-and-drop reorder within groups
|
|
156
|
+
|
|
157
|
+
### 17. Session Detail Panel
|
|
158
|
+
- Slide-in right overlay, resizable (min 320px, max 95vw), width persisted
|
|
159
|
+
- Header: project name, status, model, duration, character selector, title input, label input with chips
|
|
160
|
+
- 6 tabs: Conversation (prompts numbered + timestamped + COPY, previous sessions collapsible), Activity (merged events+tools+responses color-coded), Terminal (xterm.js + reconnect + theme), Notes (CRUD), Queue (per-session), Summary (AI-generated + re-summarize)
|
|
161
|
+
- Tab/selection persistence in localStorage
|
|
162
|
+
- Auto-attach terminal on tab switch
|
|
163
|
+
- History view mode: loads from SQLite for ended sessions
|
|
164
|
+
- Search integration with highlight
|
|
165
|
+
|
|
166
|
+
### 18. Session Controls
|
|
167
|
+
- Resume (ended sessions → terminal + `claude --resume`)
|
|
168
|
+
- Kill (SIGTERM → 3s → SIGKILL, confirm modal)
|
|
169
|
+
- Archive (marks archived, removes from live, plays sound)
|
|
170
|
+
- Delete (permanent, cascade SQLite delete, confirm dialog)
|
|
171
|
+
- Summarize (prompt template selector modal with CRUD, calls Claude haiku, rate limited)
|
|
172
|
+
- Alert (timed notification modal)
|
|
173
|
+
- Notes CRUD
|
|
174
|
+
- Label system: 3 built-in (ONEOFF/HEAVY/IMPORTANT) + custom, chip quick-select, autocomplete history (30 max)
|
|
175
|
+
- Title system: free-text, saves on blur/Enter
|
|
176
|
+
|
|
177
|
+
### 19. Quick Actions
|
|
178
|
+
- + NEW SESSION: full SSH modal (host/port/user/auth/key/workdir/command/tmux mode/API key/theme/title/label)
|
|
179
|
+
- QUICK SESSION: minimal modal reusing last SSH config
|
|
180
|
+
- ONEOFF/HEAVY/IMPORTANT: quick-launch with label pre-filled (HEAVY/IMPORTANT auto-pin)
|
|
181
|
+
- MUTE ALL toggle, ARCHIVE ENDED bulk action
|
|
182
|
+
- Working directory history (20 max, dropdown with delete), label history (30 max MRU)
|
|
183
|
+
- Mobile FAB with slide-up panel
|
|
184
|
+
- Shortcuts panel (? key)
|
|
185
|
+
|
|
186
|
+
### 20. Prompt Queue
|
|
187
|
+
- Per-session numbered list with SEND/expand/EDIT/MOVE/DEL per item
|
|
188
|
+
- Drag-to-reorder (HTML5 drag)
|
|
189
|
+
- Move mode: multi-select + choose destination session
|
|
190
|
+
- Global queue view: all sessions grouped with headers
|
|
191
|
+
- Auto-send: when autoSendQueue on + session→waiting, pop first item to terminal
|
|
192
|
+
- IndexedDB persistence with orderIndex
|
|
193
|
+
|
|
194
|
+
### 21. Session Groups
|
|
195
|
+
- Default 4 groups: Priority, Active, Background, Review
|
|
196
|
+
- CSS Grid 12-column layout, configurable colSpan per group
|
|
197
|
+
- 5 layout presets: 1-col, 2-col, 3-col, 1/3+2/3, 2/3+1/3
|
|
198
|
+
- Group CRUD, drag-reorder, resize handles
|
|
199
|
+
- Session-to-group assignment via detail panel dropdown
|
|
200
|
+
- Persistence in localStorage
|
|
201
|
+
|
|
202
|
+
### 22. Sound System
|
|
203
|
+
- 16 synthesized Web Audio API sounds: chirp, ping, chime, ding, blip, swoosh, click, beep, warble, buzz, cascade, fanfare, alarm, thud, urgentAlarm, none
|
|
204
|
+
- 20 action-sound mappings (configurable per-action)
|
|
205
|
+
- Per-CLI sound profiles (claude/gemini/codex/openclaw) with default overrides
|
|
206
|
+
- Volume 0-1, global enable/disable
|
|
207
|
+
- AudioContext unlock on first interaction
|
|
208
|
+
- Ambient presets with room sounds option
|
|
209
|
+
|
|
210
|
+
### 23. Movement Effects
|
|
211
|
+
- 18 CSS effects: none, sweat, energy-ring, sparks, steam, eye-cycle, think-pulse, head-tilt, float, breathe, sway, sparkle, bounce, flash, shake, fade, shrink, dissolve, questions
|
|
212
|
+
- Applied via data-movement attribute, auto-clear after 3.5s
|
|
213
|
+
- Per-action configurable mappings
|
|
214
|
+
|
|
215
|
+
### 24. Alarm System
|
|
216
|
+
- Approval alarm: urgentAlarm + shake, repeats every 10s while in approval
|
|
217
|
+
- Input notification: single sound on entering input state
|
|
218
|
+
- Event-based: maps hook events to configured sounds/movements
|
|
219
|
+
- Label completion alerts: per-label sound + movement + frame effect (fire/electric/chains/liquid/plasma)
|
|
220
|
+
- Duration alerts: timed, stored in IndexedDB, checked periodically
|
|
221
|
+
|
|
222
|
+
### 25. Settings
|
|
223
|
+
- Appearance: 9 themes (command-center/cyberpunk/warm/dracula/solarized/nord/monokai/light/blonde), font size, card size, scanlines, activity feed, toasts
|
|
224
|
+
- Sound: global enable/volume, per-action sound grid, per-CLI profiles
|
|
225
|
+
- Animation: character model, intensity %, speed %, per-action movement grid
|
|
226
|
+
- Hooks: density selector, install/uninstall buttons with status
|
|
227
|
+
- API Keys: Anthropic/OpenAI/Gemini with show/hide
|
|
228
|
+
- Label settings: per-label frame effect + sound + movement
|
|
229
|
+
- Summary prompts: CRUD templates, star default, 5 built-in templates
|
|
230
|
+
- Import/Export: JSON download/upload, reset to defaults
|
|
231
|
+
- All settings persisted to IndexedDB with 200ms batching
|
|
232
|
+
|
|
233
|
+
### 26. Terminal Manager (Frontend)
|
|
234
|
+
- xterm.js with Canvas renderer, FitAddon, Unicode11Addon, WebLinksAddon
|
|
235
|
+
- 10000 line scrollback, JetBrains Mono font, responsive font size
|
|
236
|
+
- 8 terminal themes + auto theme (reads CSS variables)
|
|
237
|
+
- Canvas repaint workaround (resize cols-1 → restore in 2 frames)
|
|
238
|
+
- Fullscreen mode with Alt+F11
|
|
239
|
+
- WS reconnect: clear + re-subscribe (128KB replay)
|
|
240
|
+
- Pending output buffer (500 chunks max)
|
|
241
|
+
- Per-terminal theme persistence
|
|
242
|
+
- Resize observer with 50ms debounce
|
|
243
|
+
- Team member terminal: attach to tmux pane
|
|
244
|
+
|
|
245
|
+
### 27. History Panel
|
|
246
|
+
- Server-side SQLite queries (not IndexedDB)
|
|
247
|
+
- Filters: search (300ms debounce), project dropdown, status, date range, archived flag
|
|
248
|
+
- Sort: date/duration/prompts/tools + direction toggle
|
|
249
|
+
- Pagination: 50/page with numbered buttons
|
|
250
|
+
- Row click → detail panel from SQLite
|
|
251
|
+
- Delete with cascade fade-out
|
|
252
|
+
|
|
253
|
+
### 28. Analytics Panel
|
|
254
|
+
- Custom SVG charts (no library)
|
|
255
|
+
- 5 sections: summary stats (6 cards), tool usage bar chart (top 15), duration trends area chart, active projects bar chart, daily heatmap (7×24 Mon-first)
|
|
256
|
+
- All data from server SQLite analytics endpoints
|
|
257
|
+
|
|
258
|
+
### 29. Timeline Panel
|
|
259
|
+
- Grouped bar chart: sessions/prompts/tool calls per time bucket
|
|
260
|
+
- Filters: granularity (hour/day/week/month), project, date range (default 30 days)
|
|
261
|
+
- Data from IndexedDB getTimeline()
|
|
262
|
+
|
|
263
|
+
### 30. Keyboard Shortcuts
|
|
264
|
+
- `/` focus search, Escape close/deselect, `?` shortcuts panel, `S` settings, `K` kill, `A` archive, `T` new terminal, `M` mute all
|
|
265
|
+
|
|
266
|
+
### 31. Server Infrastructure
|
|
267
|
+
- Port conflict auto-resolution (kills occupying process)
|
|
268
|
+
- Graceful shutdown (SIGTERM/SIGINT → save snapshot → close SQLite → server.close → 5s force exit)
|
|
269
|
+
- Process monitor: PID liveness check every 15s
|
|
270
|
+
- Auto-idle manager: per-session timers
|
|
271
|
+
- Hook stats: per-event counts + latency/processing histograms
|
|
272
|
+
- Rate limiting: in-memory sliding window
|
|
273
|
+
- Logger: debug-aware with pretty JSON
|
|
274
|
+
- Server config: port, enabledClis, hookDensity, debug, sessionHistoryHours, passwordHash
|
|
275
|
+
|
|
276
|
+
### 32. CLI and Setup
|
|
277
|
+
- `npx` entry: auto-setup wizard on first run, `--setup` flag to re-run
|
|
278
|
+
- 6-step wizard: port, CLIs, density, debug, retention, password
|
|
279
|
+
- Saves to `data/server-config.json`
|
|
280
|
+
- Auto-install hooks on every server startup (quiet mode)
|
|
281
|
+
|
|
282
|
+
### 33. Testing
|
|
283
|
+
- Vitest unit tests (407+ tests)
|
|
284
|
+
- Playwright E2E tests: groups, kill-session, navigation, session-lifecycle, settings, smoke, terminal
|
|
285
|
+
|
|
286
|
+
## Instructions for Agent Team
|
|
287
|
+
|
|
288
|
+
Split the work across agents:
|
|
289
|
+
|
|
290
|
+
1. **Agent 1 (Server Features)**: Sections 1-12, 31-32 — everything server-side
|
|
291
|
+
2. **Agent 2 (Frontend Features)**: Sections 13-21, 27-30 — UI components, panels, views
|
|
292
|
+
3. **Agent 3 (3D Scene + Multimedia)**: Sections 15, 22-26, 33 — Cyberdrome, sound, animations, settings, testing
|
|
293
|
+
|
|
294
|
+
Each agent should read the actual source files to verify details and add any missing specifics. The final document should be merged into a single `docs/PLATFORM_FEATURES.md` file.
|
|
295
|
+
|
|
296
|
+
Write in precise technical language. Include exact values (timeouts, buffer sizes, limits). Use tables for mappings and enums. Use code blocks for data structures. Use ASCII diagrams for architecture flows.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Session Detail Features
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
When a user clicks on an agent robot in the 3D Cyberdrome scene, a detail panel slides in from the right showing comprehensive session information. This document describes the features, architecture, and data flow.
|
|
6
|
+
|
|
7
|
+
## Detail Panel Contents
|
|
8
|
+
|
|
9
|
+
### Header
|
|
10
|
+
- **Mini robot icon**: Shows first letter of model type with status-colored border
|
|
11
|
+
- **Project name**: `session.projectName`
|
|
12
|
+
- **Session title**: `session.title` (italic, smaller)
|
|
13
|
+
- **Status badge**: Color-coded label (idle/prompting/working/waiting/approval/input/ended/connecting)
|
|
14
|
+
- **Model name**: `session.model` (e.g., "claude-opus-4-6")
|
|
15
|
+
- **Duration**: Time since `session.startedAt`
|
|
16
|
+
|
|
17
|
+
### Session Control Bar
|
|
18
|
+
- Kill, Archive, Resume buttons
|
|
19
|
+
- Labels (ONEOFF, HEAVY, IMPORTANT)
|
|
20
|
+
- Notes toggle
|
|
21
|
+
- Summarize trigger
|
|
22
|
+
|
|
23
|
+
### Tabs
|
|
24
|
+
| Tab | Content |
|
|
25
|
+
|-----|---------|
|
|
26
|
+
| **Terminal** | xterm.js terminal connected via WebSocket relay. Shows reconnect button for SSH sessions. |
|
|
27
|
+
| **Prompts** | Scrollable prompt history (`session.promptHistory`). Includes previous sessions if resumed. |
|
|
28
|
+
| **Activity** | Interleaved events, tool calls, and response excerpts from `session.events`, `session.toolLog`, `session.responseLog`. |
|
|
29
|
+
| **Notes** | Per-session markdown notes stored in IndexedDB. |
|
|
30
|
+
| **Summary** | AI-generated session summary (`session.summary`). |
|
|
31
|
+
| **Queue** | Prompt queue management — compose, reorder, send, move between sessions. |
|
|
32
|
+
|
|
33
|
+
### Modals (lazy-mounted)
|
|
34
|
+
- **KillConfirmModal**: Confirms session termination
|
|
35
|
+
- **AlertModal**: Displays alarm notifications
|
|
36
|
+
- **SummarizeModal**: Triggers AI summarization
|
|
37
|
+
|
|
38
|
+
## Selection Architecture
|
|
39
|
+
|
|
40
|
+
### Data Flow (Click → Detail Panel)
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
[R3F Canvas] [DOM Layer]
|
|
44
|
+
Robot <group onClick> ──→ onSelect() │
|
|
45
|
+
└─ SceneContent.handleSelect() │
|
|
46
|
+
└─ setTimeout(() => { │
|
|
47
|
+
dispatchEvent('robot-select') ──→ CyberdromeScene useEffect handler
|
|
48
|
+
}) ├─ selectSession(sessionId) [sessionStore]
|
|
49
|
+
└─ flyTo(pos + offset) [cameraStore]
|
|
50
|
+
│
|
|
51
|
+
┌───────────┘
|
|
52
|
+
▼
|
|
53
|
+
DetailPanel (DOM)
|
|
54
|
+
└─ useSessionStore(selectedSessionId)
|
|
55
|
+
└─ Renders session details
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Key Principles
|
|
59
|
+
|
|
60
|
+
1. **ZERO Zustand subscriptions inside `<Canvas>`**: All store reads happen in the DOM-side `CyberdromeScene` wrapper. Data flows into Canvas via props only.
|
|
61
|
+
|
|
62
|
+
2. **CustomEvent bridge**: Robot clicks dispatch a `CustomEvent('robot-select')` which is caught by a DOM-side `useEffect`. This ensures Zustand store updates happen exclusively in the DOM React reconciler, never in R3F's reconciler.
|
|
63
|
+
|
|
64
|
+
3. **setTimeout deferral**: The CustomEvent dispatch is wrapped in `setTimeout(0)` to ensure it fires after R3F's pointer event processing completes.
|
|
65
|
+
|
|
66
|
+
4. **Imperative store reads**: Components inside Canvas that need store data (CameraController, Robot3DModel) use `useStore.getState()` inside `useFrame` instead of subscriptions.
|
|
67
|
+
|
|
68
|
+
5. **Ref-based state**: Interactive state inside Canvas (hover, seated, dialogue) uses `useRef` instead of `useState` to prevent React re-renders in the R3F tree.
|
|
69
|
+
|
|
70
|
+
## Store Dependencies
|
|
71
|
+
|
|
72
|
+
| Component | Location | Store Subscriptions |
|
|
73
|
+
|-----------|----------|-------------------|
|
|
74
|
+
| CyberdromeScene | DOM wrapper | sessionStore, roomStore, settingsStore, cameraStore |
|
|
75
|
+
| DetailPanel | DOM (App.tsx) | sessionStore, uiStore, wsStore |
|
|
76
|
+
| RobotListSidebar | DOM overlay | sessionStore |
|
|
77
|
+
| SceneOverlay | DOM overlay | roomStore, sessionStore, cameraStore, settingsStore |
|
|
78
|
+
| MapControls | DOM overlay | cameraStore |
|
|
79
|
+
| SceneContent | Canvas | NONE (props only) |
|
|
80
|
+
| SessionRobot | Canvas | NONE (props only) |
|
|
81
|
+
| CameraController | Canvas | NONE (imperative reads) |
|
|
82
|
+
| Robot3DModel | Canvas | NONE (imperative reads) |
|
|
83
|
+
| SubagentConnections | Canvas | NONE (props only) |
|
|
84
|
+
| RoomLabels | Canvas | NONE (props only) |
|
|
85
|
+
| CyberdromeEnvironment | Canvas | NONE (props only) |
|
|
86
|
+
|
|
87
|
+
## Status Colors
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
idle: #00ff88 (green)
|
|
91
|
+
prompting: #00e5ff (cyan)
|
|
92
|
+
working: #ff9100 (orange)
|
|
93
|
+
waiting: #00e5ff (cyan)
|
|
94
|
+
approval: #ffdd00 (yellow)
|
|
95
|
+
input: #aa66ff (purple)
|
|
96
|
+
ended: #ff4444 (red)
|
|
97
|
+
connecting: #666 (gray)
|
|
98
|
+
```
|