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::dispatch → AgentHarnessRunner::run → AgentHarness. 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.
| File | Role |
|---|---|
src/harness/mod.rs | Module entry, re-exports |
src/harness/agent.rs | AgentHarness struct + outer run() loop + grace-turn slot |
src/harness/agent/think.rs | Think phase — LLM call, guardrails, verifier dispatch, reactive-compact rescue |
src/harness/agent/act.rs | Act phase — tool execution + resource-claim parallel dispatch |
src/harness/agent/prompt.rs | Per-turn prompt assembly |
src/harness/agent/guardrails.rs | Tool-call Sanitize / Block outcomes |
src/harness/deps.rs | HarnessDeps — dependency bundle |
src/harness/trait_def.rs | HarnessError, TurnState, TurnStep |
src/harness/callback.rs | HarnessCallback — event streaming to the gateway |
src/harness/chain_context.rs | Turn-to-turn state relay |
src/harness/trace.rs | Trace event types (DTO conversion lives in src/gateway/trace_protocol.rs) |
src/harness/trace_sink.rs | TraceSink 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:
| Claim | Meaning |
|---|---|
Shared | No 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:
PowerCapabilitytrait: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
- Dispatcher —
Orchestrator::dispatch(FlowRequest → HarnessRunner) - Session Service — Append-only event log per session
- Memory — Dream daemon + SkillOpt evolution gate