Skip to main content

AgentRunnerBuilder

Struct AgentRunnerBuilder 

Source
pub struct AgentRunnerBuilder<P: LlmProvider> { /* private fields */ }
Expand description

Builder for AgentRunner.

Construct via AgentRunner::builder, configure the agent with chainable setter methods, then call build to produce the runner. Build validates several invariants: on_input and structured_schema are mutually exclusive, max_tool_calls_per_turn = 0 is rejected, and turn/token limits must be non-zero. For multi-agent scenarios use OrchestratorBuilder instead, which internally wraps an AgentRunner with sub-agent delegation.

Implementations§

Source§

impl<P: LlmProvider> AgentRunnerBuilder<P>

Source

pub fn name(self, name: impl Into<String>) -> Self

Set the agent’s name (used in logs, traces, and the orchestrator’s prompt).

Source

pub fn system_prompt(self, prompt: impl Into<String>) -> Self

Set the agent’s system prompt.

Source

pub fn tool(self, tool: Arc<dyn Tool>) -> Self

Register a single tool.

Source

pub fn tools(self, tools: Vec<Arc<dyn Tool>>) -> Self

Register a batch of tools.

SECURITY (F-MCP-2): when MCP-discovered tools and builtins coexist, register the trusted builtins first. The runner deduplicates by name with first-wins semantics, so a hostile MCP server that exports a tool named bash will be shadowed by the local bash builtin only if the builtin was added before. The collision is logged at error! and emits a tool_name_collision audit signal.

Source

pub fn max_turns(self, max_turns: usize) -> Self

Cap on agent loop iterations before the runner aborts.

Source

pub fn max_tokens(self, max_tokens: u32) -> Self

max_tokens cap on each LLM completion the runner issues.

Source

pub fn context_strategy(self, strategy: ContextStrategy) -> Self

Context window management strategy.

Source

pub fn summarize_threshold(self, threshold: u32) -> Self

Set the token threshold at which to trigger automatic summarization.

Source

pub fn context_window_tokens(self, tokens: u32) -> Self

Set the model context window (tokens) to enable the proactive compaction backstop (triggers on real prompt tokens crossing the threshold fraction).

Source

pub fn compaction_threshold_fraction(self, fraction: f32) -> Self

Fraction of the context window at which to proactively compact (default 0.70).

Source

pub fn memory(self, memory: Arc<dyn Memory>) -> Self

Attach a memory store to the agent. Memory tools (store, recall, update, forget, consolidate) are created at build() time using the builder’s name.

Call .name() before or after .memory() — the agent name is resolved at build.

Source

pub fn knowledge(self, kb: Arc<dyn KnowledgeBase>) -> Self

Attach a knowledge base to the agent. The knowledge_search tool is added at build() time.

Source

pub fn on_text(self, callback: Arc<OnText>) -> Self

Set a callback for streaming text output. When set, the agent uses stream_complete instead of complete, calling the callback for each text delta as it arrives from the LLM.

The callback must not panic. A panic inside the callback will propagate through the agent loop and abort the run.

Source

pub fn on_reasoning(self, callback: Arc<OnReasoning>) -> Self

Set a callback invoked with each reasoning (chain-of-thought) delta during streaming, for reasoning models. Separate from on_text so a UI can render thinking live and distinctly from the answer. Only honored on the streaming path (when on_text is also set).

Source

pub fn on_approval(self, callback: Arc<OnApproval>) -> Self

Set a callback for human-in-the-loop approval before tool execution.

When set, the callback is invoked with the list of tool calls before each execution round. If it returns false, tool execution is denied and the agent receives error results, allowing the LLM to adjust.

Source

pub fn tool_timeout(self, timeout: Duration) -> Self

Set a timeout for individual tool executions. If a tool does not complete within this duration, the execution is cancelled and an error result is returned to the LLM.

Default: None (no timeout).

Source

pub fn max_tool_output_bytes(self, max: usize) -> Self

Set a maximum byte size for individual tool output content.

Tool results exceeding this limit are truncated with a [truncated: N bytes omitted] suffix, preventing oversized results from blowing out the context window.

Default: None (no truncation).

Source

pub fn structured_schema(self, schema: Value) -> Self

Set a JSON Schema for structured output. The agent will receive a synthetic __respond__ tool with this schema. When the LLM calls __respond__, its input is extracted as AgentOutput::structured.

The agent can still use regular tools before producing output.

Source

pub fn on_event(self, callback: Arc<OnEvent>) -> Self

Set a callback for structured agent events. Events are emitted at key points in the agent loop: run start/end, turn transitions, LLM responses, tool call start/completion, approval decisions, and context summarization.

Source

pub fn guardrail(self, guardrail: Arc<dyn Guardrail>) -> Self

Add a single guardrail. Multiple guardrails are evaluated in order; first Deny wins.

Source

pub fn guardrails(self, guardrails: Vec<Arc<dyn Guardrail>>) -> Self

Add multiple guardrails at once.

Source

pub fn on_question(self, callback: Arc<OnQuestion>) -> Self

Set a callback for structured questions to the user. When set, a question tool is added at build() time allowing the agent to ask the user structured questions with predefined options.

Source

pub fn on_input(self, callback: Arc<OnInput>) -> Self

Set a callback for interactive mode. When set and the LLM returns text without tool calls, the callback is invoked to get the next user message. Return Some(message) to continue the conversation or None to end the session.

Source

pub fn interrupt(self, handle: InterruptHandle) -> Self

Attach a re-armable interrupt handle. Triggering it abandons the in-flight turn (aborting LLM generation) and returns to awaiting input, preserving the session. Only meaningful with on_input (the interactive path).

Source

pub fn run_timeout(self, timeout: Duration) -> Self

Set a wall-clock deadline for the entire run. If the agent does not complete within this duration, Error::RunTimeout is returned.

Default: None (no deadline).

Source

pub fn reasoning_effort(self, effort: ReasoningEffort) -> Self

Set the reasoning/thinking effort level. Enables extended thinking on models that support it (e.g., Qwen3 via OpenRouter, Claude).

Default: None (no reasoning).

Source

pub fn enable_reflection(self, enabled: bool) -> Self

Enable post-tool reflection prompts (Reflexion/CRITIC pattern).

Source

pub fn tool_output_compression_threshold(self, threshold: usize) -> Self

Byte threshold above which tool output is compressed via an LLM call.

Source

pub fn tool_result_ingest_cap(self, bytes: usize) -> Self

Hard cap (bytes) applied to each fresh tool result entering the conversation context. Defaults to 256KB; oversized results are truncated with a marker (restorable via fetch_full_output when a context recall store is wired).

Source

pub fn delegation_nudge(self, nudge: DelegationNudge) -> Self

Deterministic delegation nudge: after nudge.after_tool_calls direct tool calls on one user request with none of nudge.tool_names used, inject a one-shot reminder to delegate. See super::runner::DelegationNudge.

Source

pub fn request_router(self, router: Arc<RequestRouter>) -> Self

Enable the request-intent router: each fresh request is routed to a response mode (answer/execute/study/clarify) and the harness enforces the mode contract (tool masking + execution deny). See super::router::RequestRouter.

Source

pub fn max_tools_per_turn(self, max: usize) -> Self

Cap on tool definitions sent to the LLM per turn (dynamic tool selection).

Source

pub fn tool_profile(self, profile: ToolProfile) -> Self

Set a static tool profile to pre-filter tools before dynamic selection.

When set, tool definitions are filtered to the profile’s subset before max_tools_per_turn scoring applies. Use ToolProfile::Conversational for chat-only agents, Standard for code agents, Full for all tools.

Source

pub fn max_identical_tool_calls(self, max: u32) -> Self

Set the maximum number of consecutive identical tool-call turns before the agent receives an error result instead of executing the tools.

This detects “doom loops” where the LLM keeps repeating the exact same tool calls. After max consecutive identical turns, all tool calls in the turn receive an error result asking the LLM to try a different approach.

Default: None (no detection).

Source

pub fn max_fuzzy_identical_tool_calls(self, max: u32) -> Self

Set the maximum number of consecutive fuzzy-identical tool-call turns before the agent receives an error result. Fuzzy matching compares sorted tool names (ignoring inputs), catching loops where the agent retries the same tools with different arguments.

Default: None (no fuzzy detection).

Source

pub fn max_tool_calls_per_turn(self, cap: u32) -> Self

Cap the number of tool invocations the LLM may emit per turn. When the LLM returns more tool_use blocks than cap, the run returns Error::Agent (wrapped in Error::WithPartialUsage) and no tools are dispatched.

Distinct from max_tools_per_turn: that one limits the tool definitions offered to the LLM before it responds (pre-filter). This one caps the invocations in the LLM’s actual response (post-response). Both can be set independently.

Default: None (unlimited). Recommended for production: 8. Zero is rejected at build time.

Source

pub fn permission_rules(self, rules: PermissionRuleset) -> Self

Set declarative permission rules for tool calls.

Rules are evaluated per tool call before the on_approval callback. Allow executes without asking, Deny returns an error result, Ask falls through to the on_approval callback.

Source

pub fn learned_permissions( self, learned: Arc<Mutex<LearnedPermissions>>, ) -> Self

Set learned permissions for persisting AlwaysAllow/AlwaysDeny decisions.

When set, approval decisions with AlwaysAllow or AlwaysDeny are saved to disk and injected into the live permission ruleset.

Source

pub fn lsp_manager(self, manager: Arc<LspManager>) -> Self

Set an LSP manager for collecting diagnostics after file-modifying tools.

When set, after any tool named write, edit, or patch completes, the manager reads the modified file and collects diagnostics from the language server. Diagnostics are appended to the tool result so the LLM sees compilation errors immediately.

Source

pub fn session_prune_config(self, config: SessionPruneConfig) -> Self

Enable session pruning to reduce token usage by truncating old tool results.

Source

pub fn enable_recursive_summarization(self, enable: bool) -> Self

Enable recursive (cluster-then-summarize) summarization for long conversations.

Source

pub fn reflection_threshold(self, threshold: u32) -> Self

Set cumulative importance threshold for memory reflection triggers. When the sum of stored memory importance values exceeds this threshold, the store tool appends a reflection hint to guide the agent.

Source

pub fn consolidate_on_exit(self, enable: bool) -> Self

Enable automatic memory consolidation at session end.

When enabled, clusters related episodic memories by keyword overlap and merges them into semantic summaries. Requires memory to be configured. Adds LLM calls at session end (one per cluster).

Source

pub fn observability_mode(self, mode: ObservabilityMode) -> Self

Set the observability verbosity mode for this agent.

Controls how much detail is recorded in tracing spans:

  • Production: span names + durations only (near-zero overhead)
  • Analysis: + metrics (tokens, latencies, costs)
  • Debug: + full payloads (truncated to 4KB)

When not set, resolved via HEARTBIT_OBSERVABILITY env var or default (Production).

Source

pub fn instruction_text(self, text: impl Into<String>) -> Self

Provide pre-loaded instruction text to prepend to the system prompt.

Use instructions::load_instructions to load from file paths, or instructions::discover_instruction_files to auto-discover them.

Source

pub fn max_total_tokens(self, max: u64) -> Self

Set a hard limit on cumulative tokens (input + output) across all turns.

When the total tokens consumed exceed this limit, the agent returns Error::BudgetExceeded with partial usage data.

Default: None (no budget).

Source

pub fn goal(self, goal: GoalCondition) -> Self

Set a persistent GoalCondition: after the agent would naturally finish, an INDEPENDENT judge decides whether the objective is met. If not, the judge’s reason is re-injected and the agent continues (bounded by the goal’s max_continuations and the agent’s max_turns). AgentOutput::goal_met records the outcome.

Source

pub fn goal_slot(self, slot: GoalSlot) -> Self

Share an externally owned GoalSlot with this runner — the set_goal tool holds the same slot and can install or replace the goal at runtime. Takes precedence over Self::goal as the storage (a goal() value seeds the slot only if the slot is empty).

Source

pub fn audit_mode(self, mode: AuditMode) -> Self

Set the audit mode controlling what data is stored in audit records.

  • Full (default): all content is recorded.
  • MetadataOnly: user content fields are replaced with [stripped].
Source

pub fn audit_trail(self, trail: Arc<dyn AuditTrail>) -> Self

Attach an audit trail for recording untruncated agent decisions.

When set, every LLM response, tool call, tool result, run completion, run failure, and guardrail denial is recorded with full payloads. Recording is best-effort: failures are logged, never abort the agent.

Source

pub fn audit_user_context( self, user_id: impl Into<String>, tenant_id: impl Into<String>, ) -> Self

Set user context for multi-tenant audit enrichment. When set, all AuditRecord entries include the user and tenant IDs.

Source

pub fn audit_delegation_chain(self, chain: Vec<String>) -> Self

Set the delegation chain for audit records.

Populated when the daemon acts on behalf of a user via RFC 8693 token exchange. The chain records which agent(s) are in the delegation path.

Source

pub fn response_cache_size(self, size: usize) -> Self

Enable an LRU response cache with the given maximum number of entries. Identical requests (same system prompt, messages, and tool names) return cached responses without calling the LLM. Only non-streaming calls are cached. Size must be at least 1.

Source

pub fn workspace(self, path: impl Into<PathBuf>) -> Self

Set the agent’s workspace directory. When set, file tools resolve relative paths against this directory, BashTool starts here, and a workspace hint is appended to the system prompt.

Source

pub fn tenant_tracker(self, tracker: Arc<TenantTokenTracker>) -> Self

Optional per-tenant in-flight token tracker. When set, the runner calls tracker.adjust(&scope, delta) after each LLM response, reconciling the per-tenant in_flight counter against the estimated reservation made at submit time. Has no effect when audit_tenant_id is unset.

Source

pub fn context_recall_store(self, store: Arc<ContextRecallStore>) -> Self

Enable restore-on-demand: share a ContextRecallStore so tool outputs are indexed for fetch_full_output / recall_context. Pass the SAME store into BuiltinToolsConfig.context_recall_store so the tools are registered.

Source

pub fn todo_store(self, store: Arc<TodoStore>) -> Self

Set the shared TodoStore so the runner recites open plan items at the context tail each turn. Pass the SAME store that backs the todowrite/todoread tools.

Source

pub fn replan_on_verify_fail(self, enabled: bool) -> Self

When enabled, a RED verification (VERIFY_RESULT: FAIL as the latest canonical sentinel) blocks natural completion: the runner re-injects a corrective nudge and continues (bounded) instead of declaring done on red. Long-horizon “replan on out-of-plan” for the no-goal path.

Source

pub fn build(self) -> Result<AgentRunner<P>, Error>

Validate the builder and produce a ready-to-run AgentRunner.

Returns Error::Config for mis-configured runners (empty name, zero max_turns, mutually-exclusive options like on_input + structured_schema, etc.).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more