#[non_exhaustive]pub struct AgentConfig {
pub max_iterations: usize,
pub max_parallel_tools: usize,
pub tool_timeout_ms: u64,
pub context_budget_chars: usize,
pub max_repeated_batch_steps: Option<usize>,
pub max_stagnation_steps: Option<usize>,
pub prune_keep_tool_messages: usize,
pub prune_keep_tail_messages: usize,
pub observation_tools: Vec<String>,
pub max_observation_steps: Option<usize>,
}Expand description
Configuration that governs a single agentic loop run.
All fields have sensible defaults via Default that match the historical
TypeScript frontend constants (previously in agentLoop.ts, now reflected
in streamAgentChat.ts).
§Serialisation
AgentConfig is intentionally not Deserialize. External callers
(HTTP, future config files) must go through a dedicated DTO that exposes
only the safe subset of fields. This prevents accidental exposure of
internal tuning knobs (pruning parameters, strike limits, etc.) to
untrusted callers.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.max_iterations: usizeMaximum number of LLM→tool→LLM iterations before the loop is aborted.
Frontend constant: DEFAULT_MAX_TOOL_ITERS = 25.
max_parallel_tools: usizeMaximum number of tool calls that may be executed in parallel per iteration.
Dual-purpose: this value is used both as the Semaphore concurrency
cap in tool_execution (limiting simultaneous in-flight calls) and as
an upper bound on the batch size the model may request in a single turn.
If the model emits more tool calls than this limit, the loop terminates
with [AgentError::ParallelToolLimitExceeded] rather than silently
serialising them. Setting this to 1 means the model may only request
one tool call per turn; two calls in a single response will abort the
loop, not run them sequentially.
Frontend constant: MAX_PARALLEL_TOOLS = 5.
tool_timeout_ms: u64Per-tool execution timeout in milliseconds.
Frontend constant: TOOL_TIMEOUT_MS = 30_000.
context_budget_chars: usizeMaximum total character budget across all messages before context pruning is applied.
Frontend constant: MAX_CONTEXT_CHARS = 180_000.
max_repeated_batch_steps: Option<usize>Maximum number of times the same tool-call batch signature may repeat
before the loop is declared stuck and aborted with
crate::ports::AgentError::LoopDetected.
Frontend constant: MAX_SAME_SIGNATURE_HITS = 2 in streamAgentChat.ts.
Set to None to disable loop detection entirely (useful in tests that
deliberately repeat the same tool call).
max_stagnation_steps: Option<usize>Session-wide occurrence limit for identical assistant text before the
loop is considered stagnant and aborted with
crate::ports::AgentError::StagnationDetected.
Semantics: Each occurrence of the same response text increments a
session counter. The error fires when the counter after
incrementing exceeds max_stagnation_steps. With the default value
of 5, stagnation triggers on the sixth identical occurrence.
With max_stagnation_steps = 0, the error fires on the very first
occurrence of any repeated text.
Frontend constant: MAX_STAGNATION_STEPS = 5 in streamAgentChat.ts.
Set to None to disable stagnation detection entirely (useful in tests
that return a fixed LLM response across many iterations).
prune_keep_tool_messages: usizeNumber of most-recent tool-result messages preserved during the first pass of context pruning.
Not exposed as a user-facing option because the value is calibrated
to balance context retention against token budget; changing it
independently of context_budget_chars can produce incoherent
conversation histories.
prune_keep_tail_messages: usizeNumber of non-system messages retained during the emergency tail-prune pass (second pass of context pruning).
Same rationale as Self::prune_keep_tool_messages.
observation_tools: Vec<String>Substring/suffix patterns used to classify tools as exploratory.
“Exploratory” tools are those that drive progress by repeatedly
querying or traversing a stateful source — page snapshots, navigation,
clicks, file reads, directory listings, API pagination calls, etc.
Their repeated invocation with identical arguments is a legitimate
ReAct pattern, not a stuck loop.
A tool call whose lowercased name satisfies
name.ends_with(pattern) || name.contains(pattern) for any pattern in
this list is classified as exploratory. When every call in a batch
matches, Self::max_observation_steps is applied as the loop
detection threshold instead of Self::max_repeated_batch_steps.
Matching semantics — substring/suffix rather than exact string — are
intentional: MCP servers routinely prepend namespace prefixes to tool
names (e.g. playwright_mcp_browser_snapshot), so exact matching would
require users to enumerate every vendor variant. The pattern "navigate"
matches browser_navigate, db_navigate, fs_navigate, etc.
BYO-MCP: users connecting custom MCP servers should extend or replace
this list via AgentConfig::from_user_params to include their own
exploratory tool name fragments (e.g. "get_dom", "fetch_page",
"list_dir").
An empty list means no tools are ever classified as exploratory;
the standard Self::max_repeated_batch_steps threshold applies to all
batches.
Default: ["snapshot", "screenshot", "read_page", "navigate", "click"].
max_observation_steps: Option<usize>Maximum number of times an exploratory-tool-only batch may repeat before loop detection fires.
Applied instead of Self::max_repeated_batch_steps when every
tool call in the current batch matches a pattern in
Self::observation_tools. A higher value (default: 15) gives the
agent room to browse multiple pages, walk directory trees, or paginate
through API results while still eventually aborting a genuinely confused
agent before it exhausts the token budget.
Mixed batches (at least one non-exploratory tool alongside an
exploratory one) always fall back to Self::max_repeated_batch_steps
— the conservative choice.
Clamped to MAX_OBSERVATION_STEPS_CEILING when supplied via
AgentConfig::from_user_params to prevent API callers from providing
a value large enough to neutralise the guard.
Set to None to disable the elevated threshold entirely; exploratory
batches then use Self::max_repeated_batch_steps like any other batch.
Default: Some(15).
Implementations§
Source§impl AgentConfig
impl AgentConfig
Sourcepub fn from_user_params(
max_iterations: Option<usize>,
max_parallel_tools: Option<usize>,
tool_timeout_ms: Option<u64>,
observation_tools: Option<Vec<String>>,
max_observation_steps: Option<usize>,
) -> Result<Self, AgentConfigError>
pub fn from_user_params( max_iterations: Option<usize>, max_parallel_tools: Option<usize>, tool_timeout_ms: Option<u64>, observation_tools: Option<Vec<String>>, max_observation_steps: Option<usize>, ) -> Result<Self, AgentConfigError>
Build an AgentConfig from user-supplied overrides.
Each Some numeric value is clamped to the safe [floor, ceiling]
range before assignment; None fields retain their Default values.
The result is validated before returning.
This is the single entry-point for HTTP, Tauri, and CLI callers, eliminating duplicated clamping logic at every call site.
§Observation-tool parameters
-
observation_tools: Some(vec)— replaces the default pattern list entirely. Pass the complete list you want, including any defaults you wish to preserve.Some(vec![])disables observation classification (standard threshold applies to all batches).Nonekeeps the built-in defaults (["snapshot", "screenshot", "read_page"]). -
max_observation_steps: Some(n)— clamped to[1, MAX_OBSERVATION_STEPS_CEILING].Nonekeeps the built-in default ofSome(10).
§Errors
Returns Err(AgentConfigError) if the clamped config violates any
invariant (defense-in-depth — should never happen given the clamping).
Sourcepub fn validated(self) -> Result<Self, AgentConfigError>
pub fn validated(self) -> Result<Self, AgentConfigError>
Validate all fields that could cause the agent loop to malfunction.
Call this after constructing an AgentConfig from untrusted input.
The Default implementation is always valid; this acts as a safety
net for values assembled by HTTP DTOs or CLI argument parsing.
§Errors
Returns Err(AgentConfigError) if any field violates its invariant.
Trait Implementations§
Source§impl Clone for AgentConfig
impl Clone for AgentConfig
Source§fn clone(&self) -> AgentConfig
fn clone(&self) -> AgentConfig
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more