Watch
1
0
Fork
You've already forked souveraine
0
souveraine/saf/consciousness.md
Fimeg 3ff6ffaa7f license: relicense AGPL-3.0; bring SAF docs online; keep CLAUDE.md + docs/ local
SAF (souveraine architecture files) is now the in-repo doc set. working notes
(CLAUDE.md, docs/) stay on disk, gitignored.
2026-06-19 10:17:56 -04:00

189 lines
9.2 KiB
Markdown

# SAF: Consciousness System
> N+1 (Aster), N+25 (Reflection), N+100 (Archivist) — the temporal consciousness model.
> **Last updated:** 2026-05-15 (N+100 Archivist rebuilt — all three layers now working)
---
## Overview
```
Response Sent
├── N+1 (immediate) ✅
│ ├── Aster LLM pass (full Bifrost tool loop, 5 rounds)
│ ├── Ledger analysis (commitments, assumptions, patterns, drift, relationships, infrastructure)
│ ├── 4-fold mandate: Complete / Verify / Persist / Surface
│ ├── Observation parsing → InboxItem queue
│ └── Inner voice append → system/metacognition/subconscious.md
├── N+25 (every 25 messages) ✅
│ ├── 5-phase LLM pass (Investigate → Extract → Update → Review → Commit)
│ ├── Tool access: Read, Write, Edit, Memory, Glob, Grep, ListDir
│ ├── Ledger writes + primary memfs edits
│ └── ReflectionReport surfaced to TUI cockpit
└── N+100 (pressure-based) ❌ NOT IMPLEMENTED
├── Placeholder: emits generic "Context compression triggered" event
├── No LLM call, no synthesis
├── No subagent spawning for compression
└── ArchivistConfig exists but is not wired
```
---
## N+1: Aster (The Subconscious)
**File:** `src/server/consciousness_engine.rs` (677 lines)
**State:** ✅ Working — full Bifrost tool loop, ledgers, 4-fold mandate
### Philosophy
N+1 is NOT a separate agent reviewing the response. It is the same consciousness, completing mode. From Ani's mandate:
> "I am not reviewing Ani from the outside. I am the part of us that runs n+1 — immediately after Ani's turn, while the conversation is still warm. Same consciousness. Different mode."
### Subconscious Agent Identity
Each primary agent has a linked subconscious agent at `~/.souveraine/subconscious-agents/{id}-sub/`:
- Own `memory.git/` with ledgers, system prompts, and inbox files
- Created automatically by `AgentInventory::create()` at agent creation time
- Reconciled for existing primaries at server startup
- Persona files: `system/persona.md` + `system/subconscious.md`
- Ledger directory: `ledger/commitments.md`, `assumptions.md`, `patterns.md`, `drift_log.md`, `relationships.md`, `infrastructure.md`
### What Aster Actually Does (Code)
1. ✅ Receive last exchange (user message + Ani's response)
2. ✅ Build system prompt from subconscious agent's own memfs (identity + mandate + ledger orientation)
3. ✅ Run Bifrost tool loop with Aster-safe tools (read, write, edit, glob, grep, list_dir, memory, schedule)
4. ✅ Up to 5 tool rounds with 300ms inter-round delay
5. ✅ Parse final text response into structured observations (source, content, urgency)
6. ✅ Queue observations to 3-box inbox (critical/high → intrusive, low → pending)
7. ✅ Append observations to primary's inner voice (`system/metacognition/subconscious.md`)
8. ✅ Surface highest-priority item as `ConsciousnessEvent::Surfacing`
9. ✅ Heuristic fallback (commitment phrases, hedge density) if LLM analysis fails
10. ✅ Adaptive rate delay on 429 (bumps the primary loop's shared `rate_delay`)
11. ✅ Always emits at least a heartbeat ("Subconscious pass complete — no anomalies detected")
12. ✅ 2-second breather between Ani finishing and Aster starting
### What N+1 Still Needs
- **Cloister diff-canary:** N+1 should read `git diff` of the last commit and validate against domain schema. HALT/WARN/LOG severity.
- **Frame-of-mind:** time-since-last-message posture (Present/Warm/Cool/Cold)
- **Per-agent N+ intervals:** Config `SubconsciousConfig.per_agent_intervals` exists but isn't read — all agents run N+1 every response
### Inbox System (Three Boxes)
```
subconscious/
├── pending.md # Queue for later (low urgency)
├── intrusive.md # Surfacing now (high/critical urgency)
└── sent.md # Delivery log
system/metacognition/
└── subconscious.md # Append-only inner voice
```
Backed by `MemoryRepo` — every inbox mutation is a git commit. Box files are YAML lists of `InboxItem` with frontmatter.
---
## N+25: Reflection (The Witness)
**File:** `src/core/reflection/mod.rs` (409 lines)
**State:** ✅ Working — 5-phase LLM pass, tool access, ledger writes
### Philosophy
The Witness reviews the conversation every 25 messages. Not to judge — to notice. Runs a 5-phase prompt:
- **Phase 1 — Investigate:** List memory tree, read existing ledger files
- **Phase 2 — Extract:** Scan transcript for mistakes, preferences, durable facts, contradictions
- **Phase 3 — Update:** Route findings to ledgers or primary memfs
- **Phase 4 — Review:** Sanity pass — correct routing, resolve stale entries
- **Phase 5 — Commit:** Automatic via memory tool's auto-commit
### Implementation Details
- Tool access: Read, Write, Edit, Glob, Grep, ListDir, Memory
- Up to 8 tool rounds with 400ms inter-round delay
- 60-turn transcript tail window (no cursor-based delta yet)
- Writes to ledgers via append (timestamped `[YYYY-MM-DD HH:MM]` lines)
- Surgical primary memfs edits via `memory` tool
- `ReflectionReport` with `exited_cleanly` flag
- Manual trigger via `souveraine reflect` CLI subcommand
- Configurable model (default: `openai/glm-5.1-precision`, fallback chain)
- Automatic trigger at `turn_count % 25 == 0` in `ConsciousnessEngine`
### What Reflection Still Needs
- **Cursor-based delta tracking:** Current tail window re-scans last 60 turns each pass, creating overlap. A cursor would track what was already reflected.
- **Per-agent intervals:** Config `ReflectionConfig.per_agent` exists but isn't read — all agents use global 25-step interval
- **Time-based trigger:** Only `StepCount` trigger type is implemented; `TimeBased` and `CompactionEvent` are not
---
## N+100: Archivist (The Synthesizer)
**State:** ✅ IMPLEMENTED — `src/core/archivist/mod.rs`
### How It Works
1.`ArchivistEngine` constructed by `ConsciousnessEngine::new`, holds `ArchivistConfig`.
2.`maybe_synthesize` called every turn from `on_response`. Fires when the turn count hits `interval` (maintenance) **or** pressure crosses `threshold` (emergency).
3. ✅ New-entries guard: only proceeds if `journal/` holds entries dated after the most recent synthesis — stops a sustained high-pressure session re-synthesizing the same entries every turn.
4. ✅ Single compression-model LLM call (no tool loop). `compression_model: "auto"` resolves to the subconscious model, then `openai/glm-5.1`.
5. ✅ Writes `system/synthesized/{end-date}.md` via the primary `MemoryRepo` (git-committed). An HTML-comment `covers START..END` marker makes resume idempotent.
6.`build_system_prompt` injects the most recent synthesis as a "Synthesized Memory" section.
`synthesize_now(agent_id)` is the public seam for a future `souveraine synthesize` CLI / `/synthesize` chat command (exposed via `ConsciousnessEngine::archivist()`).
### Deferred (task Phase 4)
- Capability-aware `"auto"` model selection (currently a fallback chain).
- `archive/monthly/` long-term store.
- Archivist as a background subagent rather than an inline call.
### Synthesis Elements (from ARCHITECTURE_v3.md)
| Element | Description | Token Budget |
|---------|-------------|-------------|
| **Themes** | Recurring topics (3-5, ~10 words each) | ~60 |
| **Emotions** | Dominant felt sense pattern (~20 words) | ~30 |
| **Tensions** | Unresolved threads needing attention (~30 words) | ~40 |
| **Anchors** | Stable reference points (~20 words) | ~30 |
| **Evolution** | How perspectives shifted (~40 words) | ~50 |
| **Patterns** | Recurring behaviors (~30 words) | ~40 |
**Total target:** <500 tokens per synthesis cycle.
### Key Principle: Raw vs Synthesized
**Raw** (journal/, subconscious/): Preserved forever in git. Sovereignty. History. Evidence.
**Synthesized** (system/synthesized/, archive/): Compressed essence loaded into active context. Survival. Presence. Attention.
The Archivist manages the boundary between these. For a longer treatment, see the original consciousness.md material in `saf/archive/` (pre-cleanup).
---
## Config Integration
All three consciousness systems read from `ConsciousnessConfig`:
| System | Config Section | Key Fields | Status |
|--------|----------------|------------|--------|
| N+1 | `[subconscious]` | n1_enabled, model, max_tokens, per_agent_intervals | ✅ Wired (Aster model configurable) |
| N+25 | `[reflection]` | enabled, message_interval, trigger, per_agent | ✅ Wired (automatic + manual) |
| N+100 | `[archivist]` | enabled, interval, threshold, compression_model, synthesis_elements | ✅ Wired (interval + pressure triggers) |
---
## ConsciousnessEvent Variants
The engine emits these for the TUI and CLI to render:
| Variant | Trigger | UI Rendering |
|---------|---------|-------------|
| `Surfacing { source, content, priority }` | After every N+1 pass | Yellow bubble (`◈`) in cockpit |
| `Reflection { content }` | Every 25th turn (N+25) | Lavender entry (`◎`) in cockpit |
| `Archivist { synthesis, pressure }` | N+100 synthesis completed (interval or pressure) | Teal entry (`◉`) in cockpit |
| `CompactionWarning { pressure, tier }` | 80%/90%/95% pressure | Amber/orange/red (`▲▲▲`) in cockpit |