Files
openhuman/gitbooks/developing/subconscious.md
T

12 KiB

description, icon
description icon
Background loop that evaluates user / system tasks against the workspace and decides what to do. loader

Subconscious Loop

Background task evaluation and execution system. Periodically checks user-defined and system tasks against the current workspace state, decides what to do, and either acts autonomously or escalates to the user.


Architecture

┌─────────────────────────────────────────────────────────┐
│                    Heartbeat Engine                      │
│              (sleeps N minutes between ticks)            │
└──────────────────────┬──────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────┐
│                  Subconscious Engine                     │
│                                                         │
│  1. Load due tasks from SQLite                          │
│  2. Insert in_progress log entries                      │
│  3. Build situation report (memory + workspace state)   │
│  4. Evaluate tasks with local Ollama model              │
│  5. Execute decisions (act / noop / escalate)           │
│  6. Update log entries in place                         │
└─────────────────────────────────────────────────────────┘
                       │
           ┌───────────┼───────────┐
           ▼           ▼           ▼
         noop         act       escalate
        (skip)    (execute)   (agentic-v1)

Key files

File Purpose
src/openhuman/heartbeat/engine.rs Periodic scheduler, delegates to subconscious engine
src/openhuman/subconscious/engine.rs Core tick logic, state management, overlap guard
src/openhuman/subconscious/executor.rs Task execution routing (local model vs agentic-v1)
src/openhuman/subconscious/prompt.rs Prompt builders for evaluation and execution
src/openhuman/subconscious/store.rs SQLite persistence (tasks, log, escalations)
src/openhuman/subconscious/types.rs Data types and enums
src/openhuman/subconscious/situation_report.rs Builds context from memory and workspace state
src/openhuman/subconscious/global.rs Global singleton shared between heartbeat and RPC
src/openhuman/subconscious/schemas.rs RPC endpoint handlers
app/src/hooks/useSubconscious.ts Frontend hook for data fetching and actions
app/src/pages/Intelligence.tsx UI rendering (Subconscious tab)
app/src/utils/tauriCommands/subconscious.ts TypeScript RPC wrappers

Task Types

System tasks

Seeded automatically on engine initialization. Cannot be deleted, only disabled.

Default system tasks:

  • Check connected skills for errors or disconnections
  • Review new memory updates for actionable items
  • Monitor system health (Ollama, memory, connections)

Additional system tasks are imported from HEARTBEAT.md in the workspace directory (one task per - line).

User tasks

Added manually via the UI. Can be toggled on/off and deleted.

Examples:

  • "Check urgent emails" (read-only)
  • "Send daily summary to Slack" (write intent)
  • "Summarize Notion updates" (read-only)

Tick Lifecycle

1. Overlap guard

Each tick gets a monotonically increasing generation counter. If a new tick starts while the old one is still running (e.g., slow LLM call), the old tick's results are discarded and its in_progress log entries are marked as cancelled.

The heartbeat uses tokio::time::sleep (not interval) so ticks never stack up.

2. Load due tasks

Query: enabled, not completed, next_run_at <= now or never run.

3. Log as in_progress

Each due task gets a single log row inserted with decision = "in_progress". This row is updated in place as the task progresses — no duplicate rows.

4. Evaluate with local model

The local Ollama model receives all due tasks + a situation report and returns a per-task decision:

Decision Meaning
noop Nothing relevant right now
act Something relevant found — execute the task
escalate Needs deeper reasoning — hand off to agentic-v1

5. Execute

Routing depends on the decision and the task's intent:

Decision: noop
  → Update log to "noop", advance schedule

Decision: act
  ├─ Task has write intent (needs_tools = true)
  │   → Execute with local model
  │
  └─ Task is read-only
      → Execute with local model

Decision: escalate
  ├─ Task has write intent (needs_tools = true)
  │   → Run agentic-v1 with full permissions
  │   → No approval needed (user explicitly asked for the write action)
  │
  └─ Task is read-only
      → Run agentic-v1 in analysis-only mode
      → If response contains "RECOMMENDED ACTION:" (unsolicited write)
      │   → Create escalation for user approval
      │   → On approval → run agentic-v1 with full permissions
      └─ Otherwise → log result, done

6. Update log entry

The same row inserted in step 3 is updated to the final state:

Decision Dot color Text
in_progress Blue (pulsing) "Evaluating..."
act Green Result text
noop Gray "Nothing new"
escalate Amber "Waiting for approval"
failed Coral Error message
cancelled Gray "Cancelled"
dismissed Gray "Skipped"

Execution Models

Local Ollama model

Used for:

  • Task evaluation (all tasks, every tick)
  • Text-only task execution (summarize, check, monitor, review)

No cost, no rate limits, runs on-device.

agentic-v1 (cloud)

Used for:

  • Tool-required task execution (send, post, delete, create)
  • Analysis-only mode for read-only tasks escalated by the local model

Rate-limit retry: up to 3 attempts with exponential backoff (2s, 4s, 8s) on 429 errors.


Approval Gate

Approval is only required when the AI wants to take a write action that the user didn't explicitly request.

Task intent AI wants to write Approval needed?
"Send digest to Slack" (write) Yes No — user asked for it
"Check urgent emails" (read) No No — read-only result
"Check urgent emails" (read) Yes (wants to forward them) Yes — unsolicited write

The approval flow:

  1. agentic-v1 runs in analysis-only mode
  2. Response contains RECOMMENDED ACTION: Forward 3 urgent emails to #team-alerts
  3. Escalation card appears in UI under "Approval Needed"
  4. User clicks "Go ahead" → agentic-v1 runs again with full permissions
  5. Or user clicks "Skip" → nothing happens

Escalations related to skills (detected by keywords: skill, oauth, notion, gmail, integration, disconnect, re-auth) show a "Fix in Skills" button that navigates to the Skills page instead of "Go ahead".


Failure Handling

Consecutive failure counter

Tracked in EngineState.consecutive_failures. Increments when the entire LLM evaluation fails (Ollama down, network error). Resets to 0 on any successful tick. Surfaced in the UI status bar as "N failed" in coral.

Individual task execution failures do NOT increment this counter — they are logged per-task but the tick itself is considered successful.

last_tick_at advancement

last_tick_at only advances on successful ticks. If the LLM evaluation fails or the tick is cancelled, last_tick_at stays unchanged so the next tick's situation report covers the same time range — nothing is missed.


Configuration

In config.toml under [heartbeat]:

[heartbeat]
enabled = true              # Enable the heartbeat loop
interval_minutes = 5        # Tick interval (minimum 5)
inference_enabled = true    # Enable local model evaluation
context_budget_tokens = 40000  # Max tokens for situation report

Defaults: enabled = true, interval_minutes = 5, inference_enabled = true.


SQLite Schema

Database: {workspace_dir}/subconscious/subconscious.db

subconscious_tasks

Column Type Description
id TEXT PK UUID
title TEXT Task description
source TEXT "system" or "user"
recurrence TEXT "pending", "once", or "cron:expr"
enabled INTEGER 1 = active, 0 = paused
last_run_at REAL Unix timestamp of last evaluation
next_run_at REAL Unix timestamp of next scheduled run
completed INTEGER 1 = done (one-off tasks)
created_at REAL Unix timestamp

subconscious_log

Column Type Description
id TEXT PK UUID
task_id TEXT FK to tasks
tick_at REAL Unix timestamp of the tick
decision TEXT in_progress, act, noop, escalate, failed, cancelled, dismissed
result TEXT Result text or error message
duration_ms INTEGER Execution duration
created_at REAL Unix timestamp

subconscious_escalations

Column Type Description
id TEXT PK UUID
task_id TEXT FK to tasks
log_id TEXT FK to log entry
title TEXT Escalation title
description TEXT What needs approval
priority TEXT critical, important, normal
status TEXT pending, approved, dismissed
created_at REAL Unix timestamp
resolved_at REAL When approved/dismissed

RPC Endpoints

All under openhuman.subconscious_*:

Method Description
subconscious_status Get engine status (enabled, ticks, failures)
subconscious_trigger Manually trigger a tick (runs in background, returns immediately)
subconscious_tasks_list List all tasks
subconscious_tasks_add Add a user task
subconscious_tasks_update Update task (title, enabled, recurrence)
subconscious_tasks_remove Remove a user task (system tasks can only be disabled)
subconscious_log_list List activity log entries
subconscious_escalations_list List escalations (filterable by status)
subconscious_escalations_approve Approve and execute an escalation
subconscious_escalations_dismiss Dismiss an escalation

UI (Intelligence Page → Subconscious Tab)

Status bar

Shows: task count, total ticks, last tick time, consecutive failures (if > 0).

Active Tasks

  • System tasks: displayed as plain text with green dot and "default" badge. No controls.
  • User tasks: toggle switch (enable/disable) + delete button on hover.
  • Add task: text input + "Add" button at the bottom.

Approval Needed

Amber cards for pending escalations. Each shows title, description, priority badge.

  • "Go ahead": approve and execute the write action.
  • "Fix in Skills": shown for skill-related escalations, navigates to Skills page.
  • "Skip": dismiss without executing.

Activity Log

Chronological list of task evaluations. Each entry shows timestamp, colored dot, and result text. Auto-polls every 2s while any entries are in_progress.

Run Now

Triggers a manual tick. The tick runs in the background — the RPC returns immediately and the UI polls for updates.