2 Data Flow 07 Sleep Mode Transitions
Blightbow edited this page 2025-12-09 04:34:35 -05:00

Data Flow 07: Sleep Mode Transitions

Engineering documentation series - Data flows in the AI Assistant system


Overview

This document describes the data flows for sleep mode transitions, including:

  • Agent-initiated sleep (go_to_sleep tool)
  • Scheduled sleep (time-based automation)
  • Wake conditions and transitions
  • Phase transitions within sleep mode
Document Description
Architecture-Memory-and-Sleep Sleep architecture overview
Data-Flow-03-Memory-Consolidation Memory operations during sleep
Data-Flow-01-Context-Compaction Context compaction during dreaming

1. Agent-Initiated Sleep (go_to_sleep)

The assistant can choose when to sleep using the go_to_sleep tool.

Trigger Point

LLM response → tool_call(go_to_sleep, {...})
  └─> tools/sleep.py:GoToSleepTool.execute()

Safeguards

Before sleep can be entered, three safeguards are checked:

┌─────────────────────────────────────────────────────────────────────────────┐
│ SAFEGUARD 1: Not Already Sleeping                                           │
│ ─────────────────────────────────────────────────────────────────────────── │
│ Check: operating_mode != "sleep"                                            │
│   └─> If sleeping: return error "Already in sleep mode"                     │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ SAFEGUARD 2: Cooldown Period                                                │
│ ─────────────────────────────────────────────────────────────────────────── │
│ Check: now >= sleep_cooldown_until                                          │
│   └─> If in cooldown: return error with minutes remaining                   │
│                                                                             │
│ Default cooldown: 60 minutes after waking                                   │
│ Purpose: Prevent rapid sleep/wake cycling                                   │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ SAFEGUARD 3: Minimum Activity                                               │
│ ─────────────────────────────────────────────────────────────────────────── │
│ Check: activity_since_wake >= min_activity_before_sleep                     │
│   └─> If insufficient: return error with activity/required counts           │
│                                                                             │
│ Default: 10 interactions required before sleep is allowed                   │
│ Purpose: Ensure assistant is productive before resting                      │
└─────────────────────────────────────────────────────────────────────────────┘

Data Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ GO_TO_SLEEP TOOL EXECUTION                                                  │
│ tools/sleep.py:GoToSleepTool.execute()                                      │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ PARAMETER EXTRACTION                                                        │
│ ─────────────────────────────────────────────────────────────────────────── │
│ Parameters:                                                                 │
│   duration_hours: 1-24 (default: 4)                                         │
│   depth: "light" | "deep" (default: "light")                                │
│   reason: required string (audit trail)                                     │
│                                                                             │
│ Light sleep: wakes on urgent events (direct messages)                       │
│ Deep sleep: only wakes when timer expires                                   │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ CALCULATE CONSOLIDATION WORKLOAD                                            │
│ ─────────────────────────────────────────────────────────────────────────── │
│ journal = character.db.journal                                              │
│ consolidated_ids = journal["consolidated_entry_ids"]                        │
│                                                                             │
│ unconsolidated_count = count entries where:                                 │
│   - id NOT in consolidated_ids                                              │
│   - content does NOT start with "[SYNTHESIS]"                               │
│                                                                             │
│ This determines how long "compacting" phase will take                       │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ SET SLEEP STATE                                                             │
│ ─────────────────────────────────────────────────────────────────────────── │
│ script.db.scheduled_wake_time = now + duration_hours                        │
│ script.db.sleep_depth = depth                                               │
│ script.db.sleep_phase = "compacting"                                        │
│ script.db.sleep_initiated_by = "tool"                                       │
│ script.db.sleep_reason = reason                                             │
│ script.db.consolidation_progress = {                                        │
│     started_at: now,                                                        │
│     total_entries: unconsolidated_count,                                    │
│     processed_entries: 0,                                                   │
│     completed: unconsolidated_count == 0                                    │
│ }                                                                           │
│                                                                             │
│ If scheduled sleep enabled:                                                 │
│   script.db._schedule_was_enabled = True                                    │
│   schedule["enabled"] = False  # Temporarily disable                        │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ TRANSITION TO SLEEP MODE                                                    │
│ operating_mode.py:transition_mode(script, "sleep", reason)                  │
│ ─────────────────────────────────────────────────────────────────────────── │
│ 1. Calculate tick rates:                                                    │
│    - old_rate = tick_rate (e.g., 5 seconds)                                 │
│    - new_rate = sleep_schedule["tick_rate_sleep"] (e.g., 60 seconds)        │
│                                                                             │
│ 2. Update TICKER_HANDLER:                                                   │
│    - TICKER_HANDLER.remove(interval=old_rate, ...)                          │
│    - TICKER_HANDLER.add(interval=new_rate, ...)                             │
│                                                                             │
│ 3. Update state:                                                            │
│    - script.db.operating_mode = "sleep"                                     │
│    - script.db.mode_transition_reason = reason                              │
│    - script.db.last_mode_transition = now                                   │
│                                                                             │
│ 4. Notify character: "😴 Entering sleep mode: {reason}"                     │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ PHASE SHORTCUT                                                              │
│ ─────────────────────────────────────────────────────────────────────────── │
│ If unconsolidated_count == 0:                                               │
│   script.db.sleep_phase = "dreaming"  # Skip compacting                     │
│                                                                             │
│ No journal entries to consolidate = straight to dreaming                    │
└─────────────────────────────────────────────────────────────────────────────┘

2. Sleep Phases

Sleep mode has two phases with different capabilities:

┌────────────────────────────────────────────────────────────────────────────────────────┐
│                                     SLEEP MODE                                          │
├────────────────────────────────────┬───────────────────────────────────────────────────┤
│           COMPACTING               │              DREAMING                              │
├────────────────────────────────────┼───────────────────────────────────────────────────┤
│ • Memory consolidation active      │ • Consolidation complete                          │
│ • Importance scoring               │ • Memory link generation (A-MEM)                  │
│ • Journal → Mem0 transfer          │ • Entity consolidation (O-Mem)                    │
│ • Wake deferred if triggered       │ • Generative reflection                           │
│                                    │ • Episodic pruning                                │
│                                    │ • Context compaction                              │
├────────────────────────────────────┴───────────────────────────────────────────────────┤
│ Transition: compacting → dreaming when consolidation_progress.completed = True         │
└────────────────────────────────────────────────────────────────────────────────────────┘

Phase Transition Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ SLEEP TICK (compacting phase)                                               │
│ rag_memory.py:run_sleep_tick()                                              │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPACTING OPERATIONS                                                       │
│ ─────────────────────────────────────────────────────────────────────────── │
│ 1. score_pending_entries()                                                  │
│    - Batch LLM scoring of journal entries                                   │
│    - Upgrades heuristic scores to LLM scores                                │
│                                                                             │
│ 2. run_sleep_consolidation()                                                │
│    - Journal entries → Mem0 semantic memory                                 │
│    - Updates consolidated_entry_ids                                         │
│    - Returns {completed: bool, consolidated: int}                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ CHECK COMPLETION                                                            │
│ ─────────────────────────────────────────────────────────────────────────── │
│ if consolidation_result["completed"]:                                       │
│   script.db.sleep_phase = "dreaming"                                        │
│   log: "Sleep phase transition: compacting -> dreaming"                     │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼ (subsequent sleep ticks)
┌─────────────────────────────────────────────────────────────────────────────┐
│ DREAMING OPERATIONS (10+ tasks per tick)                                    │
│ ─────────────────────────────────────────────────────────────────────────── │
│ 1. run_dreaming_tick()         - A-MEM style memory link generation         │
│ 2. prune_orphaned_memory_links() - Clean up dead references                 │
│ 3. run_entity_consolidation_batch() - O-Mem Pf→Pa synthesis                 │
│ 4. run_reflection() (if triggered) - Generative Agents insights             │
│ 5. prune_low_importance_entries() - Episodic memory cleanup                 │
│ 6. clear_stale_conversations() - Working memory cleanup                     │
│ 7. run_pre_compaction_extraction() - Journal critical facts                 │
│ 8. compact_conversation_history() - Context summarization                   │
└─────────────────────────────────────────────────────────────────────────────┘

3. Wake Conditions

Check Points

Wake conditions are checked after each sleep tick:

assistant_script.py:at_tick() lines 695-713
  └─> operating_mode.py:check_wake_conditions(script)

Wake Decision Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ check_wake_conditions(script)                                               │
│ operating_mode.py lines 301-357                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ CHECK 1: Scheduled Wake Time                                                │
│ ─────────────────────────────────────────────────────────────────────────── │
│ if scheduled_wake_time is set:                                              │
│   wake_time = parse_isoformat(scheduled_wake_time)                          │
│   if now >= wake_time:                                                      │
│     reason = "Scheduled wake time reached"                                  │
│     if phase == "compacting":                                               │
│       return (True, reason, deferred=True)  # Defer wake                    │
│     else:                                                                   │
│       return (True, reason, deferred=False) # Wake now                      │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ CHECK 2: Urgent Events (light sleep only)                                   │
│ ─────────────────────────────────────────────────────────────────────────── │
│ if sleep_depth == "light":                                                  │
│   for event in pending_events:                                              │
│     if is_urgent_event(event):                                              │
│       reason = f"Urgent event: {event_type}"                                │
│       if phase == "compacting":                                             │
│         return (True, reason, deferred=True)                                │
│       else:                                                                 │
│         return (True, reason, deferred=False)                               │
│                                                                             │
│ Urgent events:                                                              │
│   - event_type == "direct_message"                                          │
│   - metadata.urgent == True                                                 │
│   - metadata.priority >= 8                                                  │
│   - classification_reason in ("direct_addressing", "direct_message")        │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ NO WAKE CONDITION MET                                                       │
│ ─────────────────────────────────────────────────────────────────────────── │
│ return (False, None, False)                                                 │
│ Continue sleeping...                                                        │
└─────────────────────────────────────────────────────────────────────────────┘

Deferred Wake

When wake is deferred (during compacting phase):

┌─────────────────────────────────────────────────────────────────────────────┐
│ DEFERRED WAKE HANDLING                                                      │
│ assistant_script.py lines 701-712                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ should_wake, wake_reason, deferred = check_wake_conditions(script)          │
│                                                                             │
│ if should_wake:                                                             │
│   if deferred:                                                              │
│     # Wake after consolidation completes (next tick in dreaming)            │
│     log: "Wake deferred (compacting): {wake_reason}"                        │
│     # Continue sleep tick - wake happens after phase transition             │
│   else:                                                                     │
│     # Wake immediately (dreaming phase)                                     │
│     success, msg = perform_wake_transition(script, wake_reason)             │
└─────────────────────────────────────────────────────────────────────────────┘

4. Wake Transition

perform_wake_transition()

┌─────────────────────────────────────────────────────────────────────────────┐
│ perform_wake_transition(script, reason)                                     │
│ operating_mode.py lines 360-424                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ SET COOLDOWN                                                                │
│ ─────────────────────────────────────────────────────────────────────────── │
│ cooldown_minutes = script.db.sleep_cooldown_minutes or 60                   │
│ cooldown_until = now + timedelta(minutes=cooldown_minutes)                  │
│ script.db.sleep_cooldown_until = cooldown_until.isoformat()                 │
│                                                                             │
│ Purpose: Prevent immediate re-entry to sleep mode                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ RESET ACTIVITY COUNTER                                                      │
│ ─────────────────────────────────────────────────────────────────────────── │
│ script.db.activity_since_wake = 0                                           │
│                                                                             │
│ Resets the counter that gates go_to_sleep availability                      │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLEAR SLEEP STATE                                                           │
│ ─────────────────────────────────────────────────────────────────────────── │
│ script.db.scheduled_wake_time = None                                        │
│ script.db.sleep_depth = None                                                │
│ script.db.sleep_phase = None                                                │
│ script.db.sleep_initiated_by = None                                         │
│ script.db.sleep_reason = None                                               │
│ script.db.consolidation_progress = {                                        │
│     started_at: None, total_entries: 0,                                     │
│     processed_entries: 0, completed: False                                  │
│ }                                                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ RESTORE SCHEDULED SLEEP                                                     │
│ ─────────────────────────────────────────────────────────────────────────── │
│ if script.db._schedule_was_enabled:                                         │
│   schedule = script.db.sleep_schedule                                       │
│   schedule["enabled"] = True                                                │
│   script.db._schedule_was_enabled = False                                   │
│   log: "Restored scheduled sleep after tool-initiated wake"                 │
└─────────────────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ TRANSITION TO AWAKE MODE                                                    │
│ operating_mode.py:transition_mode(script, "awake", reason)                  │
│ ─────────────────────────────────────────────────────────────────────────── │
│ 1. Update TICKER_HANDLER:                                                   │
│    - old_rate = sleep_schedule["tick_rate_sleep"] (e.g., 60s)               │
│    - new_rate = tick_rate (e.g., 5s)                                        │
│                                                                             │
│ 2. Update state:                                                            │
│    - script.db.operating_mode = "awake"                                     │
│    - script.db.mode_transition_reason = reason                              │
│    - script.db.last_mode_transition = now                                   │
│                                                                             │
│ 3. Notify character: "👁 Entering awake mode: {reason}"                     │
└─────────────────────────────────────────────────────────────────────────────┘

5. Scheduled Sleep

Time-based automatic transitions (optional feature).

Configuration

script.db.sleep_schedule = {
    "enabled": True,
    "sleep_start_hour": 2,      # 2:00 AM
    "sleep_duration_hours": 4,  # Until 6:00 AM
    "tick_rate_sleep": 60,      # 60 seconds between sleep ticks
    "min_awake_ticks": 10,      # Minimum ticks before auto-sleep
}

Check Flow

check_sleep_schedule(script)
  └─> Called from at_tick() periodically
        └─> Checks current_hour vs sleep window
              └─> Returns (should_transition, target_mode, reason)

7. Configuration Reference

Attribute Default Description
operating_mode "awake" Current mode: "awake" or "sleep"
sleep_phase None "compacting" or "dreaming"
sleep_depth None "light" or "deep"
scheduled_wake_time None ISO timestamp for scheduled wake
sleep_cooldown_until None ISO timestamp, blocks go_to_sleep
sleep_cooldown_minutes 60 Cooldown duration after waking
activity_since_wake 0 Interactions since last wake
min_activity_before_sleep 10 Required before go_to_sleep allowed
sleep_schedule.enabled False Enable time-based auto-sleep
sleep_schedule.tick_rate_sleep 60 Tick interval during sleep (seconds)

8. Key Files

File Relevant Lines Purpose
tools/sleep.py 25-219 GoToSleepTool
tools/sleep.py 222-325 WakeUpTool
tools/sleep.py 328-471 WaitTool
operating_mode.py 121-193 transition_mode()
operating_mode.py 301-357 check_wake_conditions()
operating_mode.py 360-424 perform_wake_transition()
assistant_script.py 689-713 Sleep tick handling, wake check
rag_memory.py 1052-1234 run_sleep_tick()

Document created: 2025-12-06