6.3 KiB
Subconscious orchestration layer
The orchestration layer is OpenHuman's split-brain coordinator for wrapped
Claude Code / Codex sessions that talk to the owner agent over tiny.place
Signal-encrypted DMs. It turns each inbound session DM into one autonomous
wake cycle driven by a single tinyagents graph, keeps a durable per-session
chat model, and runs an offline subconscious that reflects on how the world
is trending and steers later cycles.
Domain root: src/openhuman/orchestration/.
Design spec: docs/arch-subconscious.md and
the staged plan under docs/plans/subconscious-orchestration/.
End-to-end flow
Claude Code / Codex session
└─ tinyplace harness wrapper — tails the session JSONL → SessionEnvelopeV1
└─ Signal E2E DM → owner agent's tiny.place inbox [tagged sessionId, source, role]
└─ ingest (decrypt-once → classify → persist → ack) orchestration/ingest.rs
└─ OrchestrationSessionMessage event → debounced wake
└─ THE WAKE GRAPH (one tinyagents CompiledGraph) orchestration/graph/
normalize → frontend(1) → execute → compress → world_diff
▲ │
└───────────────────────────────────┘
│
└─(channel_response)─► send_dm ─► context_guard ─► done
└─ subconscious tick (offline, cron/heartbeat) — reviews compressed history +
cumulative world diff → emits a steering directive that later cycles inject
UI: Brain → Orchestration tab (orchestration.* RPC + orchestration:message socket)
The wake graph (stages 4 to 5)
One OrchestrationState (graph/state.rs) flows through the whole cycle and is
checkpointed at every super-step boundary under thread orchestration:<session_id>
by SqlRunLedgerCheckpointer. frontend is the router (command-routing): when
channel_response is present it wraps up (send_dm), otherwise it hands macro
instructions to execute and loops back. The reasoning core always sets
agent_reply, so the second front-end pass compiles a channel_response; a hard
max_supersteps backstop guarantees termination (spec §5 loop continuity).
Every behaviour-bearing node is bundled behind one injected OrchestrationRuntime
(graph/mod.rs): the two-pass front end (Quick LLM, hint:chat), the reasoning
core (hint:reasoning, spawns worker sub-agents), 20:1 compression, the
append-only world-state diff, utilization + eviction, and the DM reply. As a
result, the graph mechanics are hermetically unit-testable with a single stub
while production wires the real agents / store / memory in ops.rs.
Memory mechanics (spec §3 to §4):
compresscondenses the cycle's execution trace to a strictinput/20token budget (200-token floor only when still compressive), retry-once-then-truncate, persisted idempotently bycycle_idto thecompressed_historytable.world_diffappends one entry to an append-only timeline (monotonicseqfrom genesis,terminal_statekv), idempotent bycycle_id.context_guardruns after all mutations, before END: at ≥context_evict_threshold(clamped 0.8 to 0.9) it evicts the oldest compressed entries to memory RAG underpath_scope = orchestration/<session>and resets utilization.
The subconscious steering loop (stage 6)
The existing SubconsciousEngine tick gains an orchestration_review stage that
runs fully offline: a single tool-free provider chat on the subconscious
route under SubconsciousTainted origin. It reads unreviewed compressed_history
plus the cumulative world-diff timeline and emits at most one dense
STEERING_DIRECTIVE (with expires_after_cycles) into the append-only
steering_directives store (supersede chain + cycle-count expiry). At the start of
each wake cycle ops::seed_state bumps a global reasoning-cycle counter and loads
the current non-expired directive into state.subconscious_steering, which the
execute node weaves into its system prompt via a task-local. The subconscious is
a decoupled writer: never an edge in the wake graph, never a channel/effect.
RPC + UI (stage 7)
Renderer-only controllers (internal registry) in orchestration/schemas.rs:
openhuman.orchestration_{sessions_list, messages_list, send_master_message, mark_read, status}. Live updates ride an orchestration:message socket event
(bus.rs broadcast → core/socketio.rs bridge) fanned out for every persisted
chat message. The Brain → Orchestration tab (TinyPlaceOrchestrationTab.tsx +
useOrchestrationChats.ts) reads real store classification, live-updates, and lets
the owner steer the front-end agent from the Master composer.
Running unattended (stage 8)
- No message loss: ingest dedupes by relay
message_idbefore decrypt (the Signal ratchet is never advanced twice); a relay/decrypt error leaves the message un-acked for a clean retry. - No duplicate DM: the idempotence cursor advances only after a completed,
DM-sent cycle;
dm_sentis checkpointed and the deterministiccycle_idkeeps thecompressed_history/world_diffstore writes idempotent across a checkpoint resume. - Backpressure: each cycle awaits
scheduler_gate::wait_for_capacity(), so aPaused/Throttledgate defers the cycle rather than dropping it. - Malformed input: any non-envelope / malformed DM body falls back to the peer's Master window; the parser never panics.
- Observability: nodes log entry/exit with
session_id/cycle_id/tick_idcorrelation ids;orchestration.statusexposes the current steering directive, last subconscious tick, ingest-cursor lag, and last error. Message bodies / decrypted plaintext / seeds are never logged (guarded by a source-scan test).
Configuration
The [orchestration] config block (src/openhuman/config/schema/orchestration.rs):
enabled, debounce_ms, max_supersteps, message_window,
context_evict_threshold (clamped 0.8 to 0.9), subagent_concurrency.