Skip to content

Commit 50383c2

Browse files
stonks-gitclaude
andcommitted
Document session I: dashboard v1 deployed, v2 committed locally
- devlog: 9 entries covering dashboard v1/v2, consciousness log, KB updates - handoff: session I with Phase 1 (deployed) and Phase 2 (local-only) - KB_05: rewritten for v2 architecture (layout, SSE events, API routes) - roadmap: added DASH-001 (done) and DASH-002 (done) tasks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 427d213 commit 50383c2

4 files changed

Lines changed: 230 additions & 103 deletions

File tree

KB/KB_05_dashboard.md

Lines changed: 120 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,119 +4,167 @@
44

55
Localhost web dashboard for real-time agent introspection. Serves at `http://0.0.0.0:8080`, accessible via Tailscale at `http://norisor:8080`.
66

7-
Built with `aiohttp` — runs as another coroutine in `asyncio.gather` alongside the cognitive loop, consolidation, and peripherals. Dashboard crash does not kill the agent.
7+
Built with `aiohttp` — runs as another coroutine in `asyncio.gather`. Dashboard crash does not kill the agent.
88

9-
## Architecture
9+
## Version History
1010

11-
```
12-
AgentState (dataclass)
13-
created in main.py
14-
written by cognitive_loop (assigns attention, gut, safety, etc.)
15-
read by dashboard handlers
16-
conversation list is shared (same object reference)
17-
SSE broadcast via asyncio.Queue per subscriber
18-
19-
run_dashboard()
20-
aiohttp web.Application
21-
binds 0.0.0.0:8080
22-
waits on shutdown_event
23-
access_log disabled
24-
```
11+
- **v1** (commit d685e49, 1c5c005): 4-panel grid (Live Feed, Status, Context Window, Memory Store). Deployed on norisor.
12+
- **v2** (commit 427d213): Terminal-style consciousness monitor. Two-column asymmetric layout. LOCAL ONLY, not yet deployed.
2513

26-
## Data Sharing
14+
## v2 Architecture (Current Code)
2715

28-
`AgentState` is created in `main.py` and passed to both `cognitive_loop()` and `run_dashboard()`.
16+
```
17+
+----------------------------------------------------------------------+
18+
| [*] Agent Consciousness gut:0.42 boot:5/10 $0.003 esc:0 q:1 | 28px header
19+
+--------------------------------------------------+-------------------+
20+
| | |
21+
| CONSCIOUS MIND (65%) | ATTENTION (35%) |
22+
| SSE-driven cycle blocks: | Full text of |
23+
| - INPUT (what won attention) | all candidates |
24+
| - SYSTEM PROMPT (collapsible) | |
25+
| - CONVERSATION (collapsible) +-------------------+
26+
| - RESPONSE S1/S2 [model] conf:X | |
27+
| | MEMORY SEARCH |
28+
| Auto-scrolls, scroll-lock on manual scroll | [search input] |
29+
| | semantic results |
30+
+--------------------------------------------------+-------------------+
31+
```
2932

30-
- Loop assigns internal objects after creation: `agent_state.attention = attention`, etc.
31-
- Conversation list is shared by reference: `conversation = agent_state.conversation`
32-
- Exchange count synced: `agent_state.exchange_count = exchange_count`
33-
- All reads are safe (single event loop, no thread contention).
33+
## Data Flow
3434

35-
## SSE Events
35+
```
36+
cognitive_loop
37+
-> agent_state.publish_event(cycle_start) # winner + losers + full content
38+
-> agent_state.publish_event(context_assembled) # full system prompt + conversation
39+
-> agent_state.publish_event(llm_response) # full reply + model + confidence
40+
-> agent_state.publish_event(escalation) # triggers + confidence (if S2)
41+
-> agent_state.publish_event(gate_flush) # persisted + dropped counts
42+
-> _log_consciousness(...) # persistent NDJSON log
43+
44+
Dashboard frontend (EventSource)
45+
-> cycle_start => create cycle block, show INPUT, update Attention Queue
46+
-> context_assembled => add collapsible SYSTEM PROMPT + CONVERSATION sections
47+
-> llm_response => add RESPONSE section (green=S1, orange=S2)
48+
-> escalation => add ESCALATION notice (red)
49+
-> gate_flush => standalone entry in conscious mind stream
50+
```
3651

37-
The cognitive loop publishes events at 4 points via `agent_state.publish_event()`:
52+
## SSE Events (5 types)
3853

39-
| Event | When | Data |
40-
|-------|------|------|
41-
| `cycle_start` | After winner selected | source, content preview, salience, queue_size |
42-
| `llm_response` | After LLM response | reply preview, confidence, escalated |
43-
| `escalation` | Before System 2 call | triggers, confidence |
44-
| `gate_flush` | After periodic flush | persisted count, dropped count |
54+
| Event | Emitted When | Key Data |
55+
|-------|-------------|----------|
56+
| `cycle_start` | After attention selects winner | winner{source, content, salience}, losers[]{source, content, salience}, queue_size |
57+
| `context_assembled` | After system prompt built | system_prompt (full text), conversation[], identity_tokens, context_shift |
58+
| `llm_response` | After LLM returns | reply (full text), escalated (bool), model (string), confidence |
59+
| `escalation` | Before System 2 call | triggers[], confidence |
60+
| `gate_flush` | After periodic scratch flush | persisted (int), dropped (int) |
4561

46-
Each browser tab gets its own `asyncio.Queue(maxsize=200)`. Events are fire-and-forget: `QueueFull` silently drops the subscriber. SSE keepalive every 15s.
62+
All SSE events carry full content — no truncation.
4763

48-
## API Routes
64+
## API Routes (v2)
4965

5066
```
51-
GET / -> HTML dashboard (inline, dark theme)
52-
GET /events -> SSE stream (real-time cognitive events)
53-
GET /api/status -> JSON agent state snapshot
54-
GET /api/memories -> JSON paginated memory list (?limit=20&offset=0)
55-
GET /api/memory/{id} -> JSON single memory detail
56-
GET /api/attention -> JSON attention queue contents
57-
GET /api/gut -> JSON gut feeling state + delta log
58-
GET /api/conversation -> JSON current conversation window
59-
GET /api/energy -> JSON energy tracker breakdown
67+
GET / -> HTML dashboard
68+
GET /events -> SSE stream
69+
GET /api/status -> JSON header bar data (agent_id, phase, models, gut, bootstrap, energy, escalation)
70+
GET /api/attention -> JSON attention queue (full text of all candidates)
71+
GET /api/memories/search -> JSON semantic search (?q=query, uses search_hybrid mutate=False)
72+
GET /api/memory/{id} -> JSON single memory full detail
6073
```
6174

62-
## Memory Browser
63-
64-
Uses direct `asyncpg pool.fetch()` with SELECT queries. Does NOT go through `MemoryStore` methods to avoid side effects:
65-
- No access count updates
66-
- No retrieval mutation
67-
- The agent doesn't "feel" you browsing its memories
75+
Removed from v1: `/api/memories` (paginated), `/api/gut`, `/api/conversation`, `/api/energy`
76+
77+
## Memory Search
78+
79+
New endpoint `GET /api/memories/search?q=...`:
80+
- Empty query: returns latest 10 memories via direct `pool.fetch()`
81+
- With query: calls `memory.search_hybrid(query=q, top_k=15, mutate=False)`
82+
- `mutate=False` is critical — no access count updates, no retrieval mutation
83+
- Frontend: 300ms debounce on input, Enter for immediate search
84+
- Click memory to expand inline (fetches full detail via `/api/memory/{id}`)
85+
86+
## Consciousness Log
87+
88+
Persistent NDJSON at `~/.agent/logs/consciousness.ndjson`. Each cycle appends:
89+
90+
```json
91+
{
92+
"ts": "2026-02-14T...",
93+
"source": "external_user",
94+
"salience": 0.847,
95+
"input": "full input text",
96+
"system_prompt_len": 2340,
97+
"conversation_len": 5,
98+
"reply": "full LLM response text",
99+
"escalated": false,
100+
"confidence": 0.72,
101+
"context_shift": 0.45,
102+
"queue_size_after": 0
103+
}
104+
```
68105

69-
## Frontend
106+
Fire-and-forget — logging errors never block the cognitive loop.
70107

71-
Single inline HTML/CSS/JS string (`DASHBOARD_HTML`). Dark theme, vanilla JS with `EventSource`.
108+
## AgentState Dataclass
72109

73-
4 panels:
74-
1. **Live Feed** - scrolling SSE events (attention wins, LLM responses, gate flushes, escalations)
75-
2. **Agent Status** - refreshes every 5s (phase, model, memory count, bootstrap, gut, energy cost)
76-
3. **Context Window** - refreshes every 3s (current conversation messages)
77-
4. **Memory Store** - paginated table with click-to-expand modal (refreshes every 10s)
110+
Created in `main.py`, passed to both `cognitive_loop()` and `run_dashboard()`.
78111

79-
All dynamic content uses `textContent` and DOM construction (no `innerHTML` with API data) to prevent XSS.
112+
```python
113+
@dataclass
114+
class AgentState:
115+
config, layers, memory # Set by main.py
116+
attention, gut, safety # Set by cognitive_loop after init
117+
outcome_tracker, bootstrap # Set by cognitive_loop after init
118+
conversation: list # Shared by reference with loop
119+
exchange_count: int # Synced by loop
120+
escalation_stats: dict # Points to loop's _escalation_stats
121+
_sse_subscribers: list # Per-browser asyncio.Queue(maxsize=200)
122+
```
80123

81124
## Modules
82125

83-
### `src/dashboard.py` (~900 lines)
126+
### `src/dashboard.py` (~1035 lines)
84127

85128
- `AgentState` dataclass with SSE broadcast
86-
- Route handlers for all API endpoints
129+
- Route handlers: index, sse, api_status, api_attention, api_memories_search, api_memory_detail
87130
- `run_dashboard()` coroutine
88-
- Inline HTML/CSS/JS
131+
- `_row_to_memory()` helper
132+
- Inline HTML/CSS/JS (~680 lines)
89133

90134
### `src/loop.py` (modified)
91135

92136
- Signature: `cognitive_loop(..., agent_state=None)`
93-
- Assigns objects to `agent_state` after creation
94-
- Uses shared conversation: `agent_state.conversation if agent_state else []`
95-
- Publishes 4 SSE events at key points
96-
- All agent_state operations guarded by `if agent_state:` (no-op without dashboard)
137+
- Assigns objects to agent_state after creation
138+
- Publishes 5 SSE events at key points (cycle_start, context_assembled, llm_response, escalation, gate_flush)
139+
- Writes to consciousness log each cycle
140+
- Shares conversation list and escalation_stats by reference
97141

98142
### `src/main.py` (modified)
99143

100144
- Creates `AgentState(config=config, layers=layers, memory=memory)`
101145
- Passes `agent_state` to `cognitive_loop()`
102146
- Adds `run_dashboard(agent_state, shutdown_event)` to tasks
103147

104-
## Security
148+
## Frontend Details
105149

106-
- Binds to 0.0.0.0 but only accessible via Tailscale (not exposed to public internet)
107-
- Read-only: no mutation endpoints, no write operations
108-
- No authentication (Tailscale provides network-level auth)
109-
- XSS prevented: all dynamic content via textContent/DOM construction
150+
- Dark theme, monospace font (SF Mono/Fira Code/Cascadia/Consolas)
151+
- All dynamic content via `textContent` + DOM construction (XSS-safe)
152+
- Auto-scroll with scroll-lock: pauses when user scrolls up, resumes at bottom
153+
- Collapsible sections: click label to toggle (triangle indicator)
154+
- Color coding: blue=external_user, purple=internal_dmn, green=S1/winner, orange=S2, red=escalation
155+
- Memory depth bars: green >70%, orange >40%, dim otherwise
156+
- Header polls `/api/status` every 5s
157+
- Attention polls `/api/attention` every 5s (between SSE cycles)
110158

111159
## Port
112160

113161
| Service | Port |
114162
|---------|------|
115163
| Dashboard | 8080 (mapped in docker-compose.yml) |
116164

117-
## Resilience
165+
## Deployment State
118166

119-
- `run_dashboard()` catches exceptions internally
120-
- Dashboard crash logs error and exits without calling `shutdown_event.set()`
121-
- Agent continues operating via Telegram/stdin without dashboard
122-
- `agent_state=None` default means loop works identically without dashboard
167+
- **Norisor currently runs**: v1 (commit 1c5c005)
168+
- **Local has**: v2 (commit 427d213)
169+
- **To deploy v2**: `git push origin main` then SSH pull+restart
170+
- **User instruction**: "don't deploy until I tell you"

state/devlog.ndjson

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,11 @@
2121
{"ts":"2026-02-14T14:01:00+02:00","event":"feature","id":"DASH-001","summary":"Wired dashboard into cognitive loop: agent_state param, shared conversation, 4 SSE events (cycle_start, llm_response, escalation, gate_flush). loop.py + main.py modified."}
2222
{"ts":"2026-02-14T14:02:00+02:00","event":"kb_update","id":"KB_05","summary":"Created KB_05_dashboard.md. Updated KB_01 module map, KB_03 with dashboard integration section, KB_index."}
2323
{"ts":"2026-02-14T14:03:00+02:00","event":"decision","id":"D-007","summary":"Dashboard uses aiohttp (async-native, minimal dep), AgentState shared dataclass, SSE broadcast with per-subscriber asyncio.Queue, read-only memory access via direct pool.fetch()."}
24+
{"ts":"2026-02-14T15:00:00+02:00","event":"feature","id":"DASH-001","summary":"Dashboard v1 deployed to norisor, verified working: /api/status returns correct JSON, 53 memories visible, bootstrap 5/10, Telegram running."}
25+
{"ts":"2026-02-14T15:30:00+02:00","event":"feature","id":"DASH-001","summary":"Added System 1/2 model names and escalation stats (retries, escalations, retry_successes) to status API and dashboard header."}
26+
{"ts":"2026-02-14T16:00:00+02:00","event":"feature","id":"DASH-002","summary":"Dashboard v2 rewrite: terminal-style consciousness monitor. Left 65%=Conscious Mind (full LLM context in+out per cycle, collapsible system prompt, conversation, response with model tag). Right top=Attention Queue (full text of all candidates). Right bottom=Memory Search (semantic search via search_hybrid mutate=False, debounced, click-to-expand)."}
27+
{"ts":"2026-02-14T16:01:00+02:00","event":"feature","id":"DASH-002","summary":"New SSE event: context_assembled (full system_prompt + conversation). Expanded cycle_start to include all candidates with full content. Removed all content truncation from SSE events. Added model name to llm_response events."}
28+
{"ts":"2026-02-14T16:02:00+02:00","event":"feature","id":"DASH-002","summary":"Consciousness log: persistent NDJSON at ~/.agent/logs/consciousness.ndjson. Each cycle records: ts, source, salience, input, system_prompt_len, conversation_len, reply, escalated, confidence, context_shift, queue_size_after."}
29+
{"ts":"2026-02-14T16:03:00+02:00","event":"feature","id":"DASH-002","summary":"Header bar compresses all stats into 28px: gut, bootstrap, cost, escalations, queue size, memory count, model. Status panel eliminated. All metrics via polling, all content via SSE."}
30+
{"ts":"2026-02-14T16:04:00+02:00","event":"kb_update","id":"KB_05","summary":"KB_05_dashboard.md needs update for v2 layout (currently describes v1). KB_03 dashboard integration section still accurate."}
31+
{"ts":"2026-02-14T16:05:00+02:00","event":"handoff","id":"SESSION-I","summary":"Session I: Dashboard v1 built+deployed+verified, then v2 rewrite (terminal consciousness monitor). v2 committed locally (427d213), NOT deployed yet — user is talking to agent via Telegram. Norisor currently runs v1 dashboard (d685e49+1c5c005). Next: user says when to deploy v2, then TEST-001."}

0 commit comments

Comments
 (0)