Aleph
Architecture

Harness

Think→Act loop driver, 12-file / line-count ratchet, resource-claim Act concurrency, and tool-loop verifier.

Harness

AgentHarness drives the Think → Act loop for every flow dispatched by the Orchestrator. All agent execution — including subagent spawning — routes through Orchestrator::dispatchAgentHarnessRunner::runAgentHarness. The harness is constructed by the orchestrator and receives pre-resolved dependencies (SessionService, ToolService, Sandbox, AiProvider, plus optional PowerCapability).

Location: src/harness/


Inner Loop

┌────────────────────────────────────────────────┐
│                AgentHarness                    │
├────────────────────────────────────────────────┤
│  ┌──────────┐   ┌──────────┐   ┌──────────┐    │
│  │  PREPARE │──▶│  THINK   │──▶│ RESOLVE  │    │
│  │ • Budget │   │ • LLM    │   │ • Parse  │    │
│  │ • Context│   │ • Decide │   │ • Tools  │    │
│  │ • Verify │   └──────────┘   └────┬─────┘    │
│  └──────────┘                      │           │
│                             ┌──────┴─────┐     │
│                             │    ACT     │     │
│                             │ • Execute  │     │
│                             │ • Parallel │     │
│                             └──────┬─────┘     │
│                                    │           │
│                             ┌──────┴─────┐     │
│                             │ FINALIZE   │     │
│                             │ • Trace    │     │
│                             │ • Persist  │     │
│                             │ • Decision │     │
│                             └────────────┘     │
└────────────────────────────────────────────────┘

PREPARE asks the context budget for pressure signals, picks the right paradigm, and consults the verifier chain. THINK calls the provider, applies input/output guardrails, and folds back transient context. RESOLVE parses the response and decides whether to act. ACT executes tool calls — serial or parallel by resource claim. FINALIZE flushes trace events, persists events into SessionService, and emits a TurnStep.


Core Structure

pub struct AgentHarness {
    deps: HarnessDeps,                         // All dependencies injected at construction
    stall_tracker: Option<StallTracker>,       // Activity tracker for stall detection
    hit_limit: AtomicBool,                     // Set when a cap forces early exit
    total_tokens: AtomicU64,                   // Cumulative token usage across the run
    terminate_reason: Mutex<TerminateReason>,  // Precise loop-exit cause
    tool_timeline: Mutex<Vec<ToolInvocation>>,  // Per-tool duration / success
}

AgentHarness is stateless beyond counters — all real state lives in SessionService. Dependencies are injected at construction via HarnessDeps.


Loop Outcome Types

src/harness/trait_def.rs defines the loop-control surface:

pub enum TurnState {
    Continue,   // Run another Think→Act turn
    Done,       // Loop is complete; no further turns needed
}

pub struct TurnStep {
    pub state: TurnState,
    pub executed: usize,         // Successful tool calls this turn
    pub vetoed: bool,            // A verifier vetoed the turn (forces Continue + retry)
    pub split_child: Option<SessionId>, // Sub-session id if the turn forked
}

pub enum HarnessError {
    Llm(AlephError),
    Tool(ToolError),
    Session(SessionError),
    Cancelled,
    StalledTurn { elapsed: Duration }, // Think exceeded turn_timeout; Act has its own per-tool clock
}

The polymorphic seams are SessionDriver and Arc<dyn HarnessRunner> (in the orchestrator) — there is no Harness trait. The earlier trait and its default run() were deleted as zero-consumer abstractions.

The outer loop in AgentHarness::run repeatedly calls run_turn_internal until it returns TurnState::Done or the CancellationToken fires.


Source Map

src/harness/ is locked at 12 files by the tests/budget.rs ratchet (CEILING = 5055, 2026-07-26). A 13th file or any line growth fails cargo test -p alephcore --lib.

FileRole
src/harness/mod.rsModule entry, re-exports
src/harness/agent.rsAgentHarness struct + outer run() loop + grace-turn slot
src/harness/agent/think.rsThink phase — LLM call, guardrails, verifier dispatch, reactive-compact rescue
src/harness/agent/act.rsAct phase — tool execution + resource-claim parallel dispatch
src/harness/agent/prompt.rsPer-turn prompt assembly
src/harness/agent/guardrails.rsTool-call Sanitize / Block outcomes
src/harness/deps.rsHarnessDeps — dependency bundle
src/harness/trait_def.rsHarnessError, TurnState, TurnStep
src/harness/callback.rsHarnessCallback — event streaming to the gateway
src/harness/chain_context.rsTurn-to-turn state relay
src/harness/trace.rsTrace event types (DTO conversion lives in src/gateway/trace_protocol.rs)
src/harness/trace_sink.rsTraceSink trait for observability

Inline tests were relocated to src/harness/tests/ so the 12-file budget measures production code only.


Act-Stage Parallel Groups

Act partitions tool calls by resource claim and dispatches intra-group concurrently / inter-group serially. Tools declare one of three claim kinds:

ClaimMeaning
SharedNo conflict with any other claim
Exclusive { Global }Mutex over the entire execution (e.g. global config, worktree)
Exclusive { Paths }Mutex over a specific set of filesystem paths

Two calls admitted as disjoint on the model's original args stay disjoint: if any guardrail rewrites args (PII mask), the whole batch is serialized instead — otherwise two distinct paths can collapse onto the same placeholder and race.

Cross-batch dedup: a duplicate (name, args) within or across batches reuses the first result instead of executing twice.

Each call's wall-clock is bounded by its tool's budget in src/tools/scoped/dispatch.rs::execute_inner. The run-level turn_timeout only bounds Think; per-call overruns surface as ToolError::Timeout the next Think reads.


Tool-Loop Verifier

src/verification/ provides a Turn-level post-validation chain consulted between Think and Act. A blocking verdict forces one more Continue so the model reacts to the failure. Verification is not a hard-stop heuristic: only the model's explicit stop ends a turn cleanly.


Sleep Inhibitor

Each Think→Act turn acquires a platform sleep-inhibit via PowerCapability::inhibit_sleep("Aleph agent loop"). On macOS this returns an IOPMAssertion of type PreventUserIdleSystemSleep; on Linux and Windows the equivalent is wired through the desktop platform layer. The returned RAII guard releases the assertion the moment it drops, so a long-running agent turn cannot be silently cut short by the host going idle.

The guard is acquired at the top of the Think phase (src/harness/agent/think.rs) and released automatically when the turn returns — success, error, or cancel.

pmset -g assertions | grep "Aleph agent loop"

The assertion disappears from the list the moment the turn completes.

Implementation files:

  • PowerCapability trait: desktop/shared/src/traits/power.rs
  • macOS IOPMAssertion FFI: desktop/macos/src/sleep_inhibitor.rs
  • Harness wiring: src/harness/agent/think.rs

See Also

  • Thinker — Prompt assembly + LLM interaction
  • DispatcherOrchestrator::dispatch (FlowRequest → HarnessRunner)
  • Session Service — Append-only event log per session
  • Memory — Dream daemon + SkillOpt evolution gate

On this page