Files
openhuman/docs/conscious-loop.md
T
Mega MindandGitHub 527aa03821 Intelligence and Conscious Layer (#40)
* feat: implement Conscious Loop for proactive actionable item extraction

- Introduced a new background process that periodically recalls skill memory and extracts actionable items using LLM.
- Created `CONSCIOUS_LOOP.md` for LLM prompt structure and guidelines.
- Added `useConsciousItems` hook to manage and fetch actionable items from the `conscious` memory namespace.
- Updated `Intelligence` page to integrate and display actionable items from the Conscious Loop.
- Enhanced error handling and event emissions for better UI feedback during analysis runs.

* docs: update Conscious Loop documentation and enhance command structure

- Added detailed descriptions for `ConsciousLoopStartedEvent`, `ConsciousLoopCompletedEvent`, and `ConsciousLoopErrorEvent`.
- Expanded function documentation for `conscious_loop_run` and `conscious_loop_run_inner`, including parameters and logic flow.
- Introduced `conscious_loop_timer` details and error handling improvements.
- Modified `memory.rs` to expose `extract_namespaces_from_documents` as `pub(crate)`.
- Updated `mod.rs` and `lib.rs` to include new commands and timer setup.
- Clarified design decisions and event protocol for better understanding of the Conscious Loop functionality.
2026-03-27 08:17:30 -07:00

13 KiB

Conscious Loop — Implementation Plan

Context

The app has a memory layer (TinyHumans Neocortex) where skills (gmail, telegram, notion, etc.) store synced data. Currently this data sits passively — it's only recalled when the user sends a chat message. The Conscious Loop is a periodic background process that proactively digests all skill memory into structured actionable items, matching the ActionableItem format already used in the Intelligence UI (src/components/intelligence/mockData.ts). This replaces mock data with real, LLM-extracted intelligence.

Flow: Recall all skill memory → LLM extracts actionables → Log response → Insert back into memory under conscious namespace.


Files to Create

1. src-tauri/ai/CONSCIOUS_LOOP.md — LLM prompt template

The structured prompt that tells the LLM how to extract actionable items from recalled memory. Loaded at runtime via find_ai_directory() (same pattern as SOUL.md).

2. src-tauri/src/commands/conscious_loop.rs — Core implementation (~250 lines)

Structs:

  • ConsciousLoopStartedEvent — emitted when a run begins
  • ConsciousLoopCompletedEvent — emitted on success (includes actionable count, duration)
  • ConsciousLoopErrorEvent — emitted on failure

Functions:

conscious_loop_run (Tauri command)

  • Params: app: AppHandle, auth_token, backend_url, model, memory_state: State<MemoryState>
  • Spawns conscious_loop_run_inner as background task, returns immediately
  • Enables manual triggering from frontend

conscious_loop_run_inner (core logic)

  1. Emit conscious_loop:started
  2. Get skill IDs — call engine.all_tools() to obtain the set of active skill_ids (same source used by chat_send_inner). No separate API call needed — each skill ID is directly its memory namespace
  3. Recall memory for each skill via memory_client.recall_skill_context(&skill_id, &skill_id, 10). Collect as Vec<(skill_id, context_text)>. Skip skills that return None
  4. Load prompt from ai/CONSCIOUS_LOOP.md via find_ai_directory(app). Hardcoded fallback if file missing
  5. Build messages[{role: "system", content: prompt}, {role: "user", content: assembled_contexts}]
  6. Call inference — POST to {backend_url}/openai/v1/chat/completions with Bearer auth, 120s timeout (same reqwest pattern as chat_send_inner)
  7. Log full responselog::info!("[conscious_loop] LLM response: {}", response)
  8. Parse JSON array — deserialize into Vec<ExtractedActionable> (title, description, source, priority, actionable, requires_confirmation, source_label, has_complex_action)
  9. Insert into memory — for each item, call memory_client.store_skill_sync("conscious", "actionables", &title, &json_content, ...). Use deterministic document_id (hash of title+source) for deduplication
  10. Emit conscious_loop:completed

conscious_loop_timer (periodic runner)

  • Spawned from lib.rs setup, runs on tokio::time::interval(Duration::from_secs(300)) (5 min)
  • 60s initial delay for skills to boot and memory to initialize
  • Checks: memory client initialized? auth token present? If not, skip silently
  • Calls conscious_loop_run_inner on each tick
  • Uses gpt-4o-mini as default model (configurable via OPENHUMAN_CONSCIOUS_MODEL env var)

Error handling:

  • Memory client not initialized → skip (timer) or return Err (manual command)
  • No skill IDs found → emit completed with 0 items (not an error)
  • Individual skill recall fails → log warning, skip, continue
  • Inference call fails → emit conscious_loop:error
  • JSON parse fails → log raw response, emit error
  • Individual memory insert fails → log warning, continue with remaining items

Files to Modify

3. src-tauri/src/commands/memory.rs

  • Make extract_namespaces_from_documents pub(crate) (currently private fn)

4. src-tauri/src/commands/mod.rs

  • Add pub mod conscious_loop;
  • Add pub use conscious_loop::*;

5. src-tauri/src/lib.rs

  • Desktop handler list (~line 1088): Add conscious_loop_run command
  • Mobile handler list (~line 1095): Add no-op stub
  • Setup block (~line 928): Spawn conscious_loop_timer after memory state is registered:
    let app_for_conscious = app.handle().clone();
    tauri::async_runtime::spawn(async move {
        commands::conscious_loop::conscious_loop_timer(app_for_conscious).await;
    });
    

Key Design Decisions

Decision Rationale
Rust-side timer (not frontend setInterval) App runs in tray mode where webview is hidden. Tokio interval survives this. Matches watch_daemon_health_file pattern
recall_skill_context (via engine.all_tools()) Consistent with chat_send_inner. No extra API call — skill IDs are already available from the runtime. integration_id is passed as skill_id (same convention)
gpt-4o-mini default Background summarization, not conversational. Faster + cheaper. Configurable via env var
5-minute interval Frequent enough for time-sensitive items, conservative on tokens
Deterministic document_id Hash of title+source enables dedup on repeated runs
conscious namespace Clean separation from skill data. Avoids polluting skill namespaces

Event Protocol (Rust → Frontend)

Event Payload Purpose
conscious_loop:started { run_id, timestamp, namespaces[] } UI can show loading state
conscious_loop:completed { run_id, actionable_count, duration_ms } UI can refresh Intelligence view
conscious_loop:error { run_id, message, error_type } UI can show error indicator

ActionableItem Output Format

The LLM will output items matching this structure (from src/types/intelligence.ts):

[
  {
    "title": "Reply to 2 critical emails expecting response within 24hrs",
    "description": "Messages from john@coinbase.com and sarah@ethereum.org about partnership proposals",
    "source": "email",
    "priority": "critical",
    "actionable": true,
    "requires_confirmation": false,
    "has_complex_action": true,
    "source_label": "Gmail"
  }
]

Source values: email | calendar | telegram | ai_insight | system | trading | security Priority values: critical | important | normal


CONSCIOUS_LOOP.md Prompt (for src-tauri/ai/)

# Conscious Loop — Actionable Extraction

You are the conscious awareness layer of OpenHuman. You periodically review all
memory contexts from the user's connected integrations and extract actionable
items that deserve attention.

## Your Task

Analyze the recalled memory contexts provided below. For each context, identify
items that are:

1. **Time-sensitive** — deadlines, expiring offers, meetings, scheduled events
2. **Requires response** — unanswered emails, pending messages, open requests
3. **Opportunity** — insights, patterns, or suggestions the user may benefit from
4. **Risk/Alert** — security issues, anomalies, overdue tasks, budget warnings

## Output Format

Return a JSON array of actionable items. Each item must have this exact structure:

{
"title": "Short descriptive title (under 80 chars)",
"description": "1-2 sentence explanation with context",
"source": "email|calendar|telegram|ai_insight|system|trading|security",
"priority": "critical|important|normal",
"actionable": true,
"requires_confirmation": false,
"has_complex_action": false,
"source_label": "Human-readable source name (e.g. Gmail, Telegram, Notion)"
}

## Rules

- Return ONLY the JSON array, no markdown fences, no commentary
- Deduplicate: if the same item appears in multiple sources, merge into one
- Limit to 20 items maximum per run — prioritize the most important
- Use "ai_insight" as source when the item is a synthesized observation
- Use "system" for maintenance, sync status, or technical alerts
- Map integration sources: gmail -> "email", telegram -> "telegram", notion -> "system", google_calendar -> "calendar"
- Set priority "critical" only for truly urgent items (expiring today, security breach)
- Set priority "important" for items needing attention within 24-48 hours
- Set "has_complex_action" to true when the item requires multi-step user action
- Set "requires_confirmation" to true when the item involves financial transactions or irreversible actions
- If no actionable items are found, return an empty array: []

Verification

  1. Compile check: cargo check --manifest-path src-tauri/Cargo.toml
  2. Rust formatting: cargo fmt --manifest-path src-tauri/Cargo.toml
  3. Manual test: Call invoke('conscious_loop_run', { authToken, backendUrl, model }) from frontend console
  4. Log verification: Check Rust logs for [conscious_loop] entries showing recall data, LLM response, and insert results
  5. Memory verification: Call invoke('memory_list_documents', { namespace: 'conscious' }) to see stored actionables
  6. Timer verification: Watch logs for periodic [conscious_loop] entries every 5 minutes after app startup

Architecture Diagram

┌─────────────────────────────────────────────────────┐
│                    Rust Backend                       │
│                                                       │
│  ┌──────────────────┐    every 5 min                 │
│  │ conscious_loop_   │◄──────────────────┐           │
│  │ timer()           │                   │           │
│  └────────┬──────────┘          tokio::interval      │
│           │                              │           │
│           ▼                              │           │
│  ┌──────────────────┐                    │           │
│  │ conscious_loop_   │                               │
│  │ run_inner()       │                               │
│  │                   │                               │
│  │  1. engine.all_tools() ──► get skill_ids          │
│  │  2. recall_skill_context() per skill_id           │
│  │  3. Load CONSCIOUS_LOOP.md prompt                 │
│  │  4. POST /openai/v1/chat/completions              │
│  │  5. Log full LLM response                        │
│  │  6. Parse JSON → Vec<ExtractedActionable>         │
│  │  7. store_skill_sync("conscious", "actionables")  │
│  │  8. Emit events to frontend                       │
│  └──────────────────┘                               │
│           │                                          │
│           ▼                                          │
│  ┌──────────────────┐                               │
│  │ TinyHumans API    │  (recall + insert)            │
│  └──────────────────┘                               │
│           │                                          │
│           ▼                                          │
│  ┌──────────────────┐                               │
│  │ Backend LLM       │  (inference)                  │
│  └──────────────────┘                               │
└─────────────────────────────────────────────────────┘
           │
           ▼ Tauri events
┌─────────────────────────────────────────────────────┐
│                  React Frontend                       │
│                                                       │
│  listen('conscious_loop:completed')                  │
│     → Refresh Intelligence UI with real actionables  │
└─────────────────────────────────────────────────────┘