SAF (souveraine architecture files) is now the in-repo doc set. working notes (CLAUDE.md, docs/) stay on disk, gitignored.
189 lines
9.2 KiB
Markdown
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 |
|