Aleph
Architecture

Orchestrator

AgentDef and FlowSpec resolution, HarnessDeps assembly, and dispatch to the Harness runtime.

Orchestrator

The Orchestrator is the bridge between the Gateway (chat ingress, protocol adapters) and the AgentHarness (Think→Act loop). It receives a FlowRequest, assembles execution context, and dispatches via the HarnessRunner trait to the Phase-4 AgentHarness. The orchestrator keeps "routing" and "execution" cleanly separated: the gateway only owns the former, AgentHarness only owns the latter.

Role in the Architecture

Gateway FlowRequest


┌─────────────┐
│ Orchestrator │
│              │
│ • Resolve FlowSpec / FlowInput │
│ • SessionLock + channel events │
│ • Dispatch to HarnessRunner    │
└──────┬───────┘
       │ HarnessRunner::run (returns FlowOutcome)

┌─────────────┐
│   AgentHarness (Think→Act)  │
└─────────────┘

Location: src/orchestrator/

  • mod.rs — public re-exports + module declarations
  • dispatch.rsOrchestrator / FlowRequest / FlowHandle / FlowOutcome / HarnessRunner trait
  • harness_bridge/AgentHarnessRunner (the production HarnessRunner implementation)
  • flow_spec.rs — declarative FlowSpec / FlowInput / BrainRef / SandboxKind / SessionStrategy / FlowOverrides
  • flow_registry.rs / resolver.rs — routing resolution + session resolution
  • sandbox_factory.rsWorkspaceSandbox / DenyAllSandbox / WorkspaceBuilder
  • deps_builder.rs — boot-time provider chain / context budget / strategy planner assembly
  • loader.rs / presets/ / errors.rs / flow_run_tool.rs / summary_format.rs

Boundary: Orchestrator ≠ Harness

The orchestrator only does:

  1. ResolveFlowSpec (declarative TOML) + FlowInput (Prompt / Messages / History / Multimodal / Resume) + runtime overrides coming from FlowRequest.
  2. Assemble — pick Arc<dyn AiProvider> (per BrainRef::Default / Preferred / Strict), build Arc<dyn Sandbox> (per SandboxKind::Workspace / None), take a SessionLockGuard (mutual exclusion per session).
  3. Dispatch — call HarnessRunner::run(...) and wrap the result in a FlowHandle { session_key, events, completion, cancel } returned to the gateway.

The orchestrator does not call the LLM, does not invoke tools, does not judge completion. That work lives entirely inside AgentHarness (Phase 4). HarnessDeps injects those capabilities as Arc<dyn Trait> into the harness, but the struct definition and field shape live in src/harness/deps.rs; the orchestrator only holds references and constructs AgentHarness instances on demand.


FlowSpec Resolution

FlowSpec (declarative, loaded from TOML) is the execution specification of a single flow:

FieldDescription
idFlow identifier
descriptionFlow purpose
agentTarget AgentId
brainBrainRef: Default / Preferred { provider } / Strict { provider, model }
sandbox_kindNone / Workspace
session_strategyReuse / Fresh / Child { parent_session_key? }
priorityDispatch priority (0-255, default 128)
overridesFlowOverrides { max_iterations, context_mode, extra_system_prompt }

FlowRequest (runtime) merges request-level overrides with the declarative FlowSpec and carries optional tool_service / trace_sink / interaction_manifest / sandbox_override / workspace_override / max_iterations_override / transient_context / think_level / envelope / model_directive so the HarnessRunner implementation can override defaults per call.


HarnessDeps (Phase-4 Injection)

The HarnessRunner implementation converts the 13 parameters of Arc<dyn HarnessRunner>::run(...) into HarnessDeps (defined in src/harness/deps.rs) and hands it to AgentHarness::new. The current field shape (only optional fields shown — required fields like session / tools / llm are in the source):

pub struct HarnessDeps {
    pub session: Arc<dyn SessionService>,
    pub tools: Arc<dyn ToolService>,
    pub llm: Arc<dyn AiProvider>,
    pub robustness_profile: ModelRobustnessProfile,

    pub verifier_chain: Option<Arc<VerifierChain>>,     // between Think and Act
    pub context_budget: Option<Arc<Mutex<ContextBudget>>>,
    pub context_compactor: Option<Arc<ContextCompactor>>,
    pub preflight_pipeline: Option<Arc<PreflightPipeline>>,
    pub trace_sink: Option<Arc<dyn TraceSink>>,
    pub system_prompt: Option<String>,
    pub system_prompt_parts: Option<Vec<SystemPromptPart>>, // stable/dynamic split
    pub recall_context: Option<String>,                  // per-turn trailing recall
    pub chain_context: ChainContext,                     // subagent depth

    pub guardrails: Option<Arc<GuardrailRegistry>>,      // input/output/tool guards
    pub max_iterations: Option<usize>,
    pub power: Option<Arc<dyn PowerCapability>>,         // macOS sleep inhibitor
    pub stall_config: Option<StallConfig>,
    pub consecutive_failure_cap: Option<usize>,
    pub turn_timeout: Option<Duration>,
    pub turn_budget: Option<Arc<TurnResultBudget>>,      // tool-result Layer 3
    pub result_store: Option<Arc<ToolResultStore>>,
    pub session_epoch_registrar: Option<Arc<dyn SessionEpochRegistrar>>,
    pub tool_signal_sink: Arc<dyn ToolSignalSink>,
    pub in_flight_tool_calls: Option<Arc<InFlightToolCalls>>,
    pub parallel_tool_concurrency: Option<usize>,
}

The Arc<dyn Trait> (or Option<Arc<...>>) pattern enables test doubles and hot reload (default_provider): AgentHarnessRunner holds an Arc<dyn DefaultProviderHandle>, re-resolves the current default provider on every run(), so a UI-driven set_default takes effect on the next turn without a restart.


Dispatch Flow

FlowRequest { agent_id, input, channel, ...overrides... }


Orchestrator::dispatch
   • Resolve FlowSpec (TOML) + FlowInput
   • Pick AiProvider (BrainRef) + Sandbox (SandboxKind)
   • SessionLockGuard acquired


HarnessRunner::run(session_key, spec, input, sandbox, events,
                   cancel, tool_service_override?, trace_sink?,
                   interaction_manifest?, workspace_override?,
                   max_iterations_override?, transient_context?,
                   think_level?, envelope?, model_directive?)


FlowHandle { session_key, events, completion, cancel }


FlowOutcome → Gateway renders response

The HarnessRunner trait accepts the 13 parameters above (src/orchestrator/dispatch.rs::HarnessRunner::run) and hands control to the Phase-4 AgentHarness. AgentHarness::run returns a structured FlowOutcome containing final text, token breakdown (cache hits / reasoning), tool timeline, pricing estimate (pricing::CostEstimate), and a precise terminate_reason (replacing the old hit_limit: bool).


Key Types

TypePurposeLocation
FlowRequestIncoming request from Gatewaysrc/orchestrator/dispatch.rs
FlowSpec / FlowInput / BrainRef / SessionStrategyDeclarative flow specsrc/orchestrator/flow_spec.rs
FlowHandleSpawn handle returned to the Gateway (events / completion / cancel)src/orchestrator/dispatch.rs
FlowOutcome / FlowStreamEvent / TerminateReasonExecution result (with precise exit reason)src/orchestrator/dispatch.rs
HarnessRunnerExecution entry traitsrc/orchestrator/dispatch.rs
AgentHarnessRunnerProduction HarnessRunner implementation → AgentHarnesssrc/orchestrator/harness_bridge/
HarnessDepsHarness dependency bundle (struct definition)src/harness/deps.rs

See Also


26.7.x Addendum

Real max_iterations

26.7.15+: spawned subagents inherit the operator's [execution] max_iterations (previously falling back to 200). The HarnessRunner::default_max_iterations hook is now correctly overridden.

Subagent Interruptible

26.7.x: A2A subagents are mid-turn interruptible — Interrupt truly cancels fan-out.

Subagent-Scoped Tool Output

26.7.15+: child harnesses get a ToolResultStore handle scoped to the parent session — previously process-wide, causing silent zero-hit recall.

Harness Ratchet — CEILING Is the Authority

26.7.21+: the DiminishingReturnsDetector hard stop was removed (R10 forbids completion judgment inside the loop). Stuck runs are now bounded by max_iterations / ToolLoopVerifier / the model's own stop. The line-budget ratchet is locked by src/harness/tests/budget.rs::CEILING; this page does not copy a current count, defer to the constant.

See Also

On this page