a3s_code_core/agent_api.rs
1//! Agent Facade API
2//!
3//! High-level, ergonomic API for using A3S Code as an embedded library.
4//!
5//! ## Example
6//!
7//! ```rust,no_run
8//! use a3s_code_core::Agent;
9//!
10//! # async fn run() -> anyhow::Result<()> {
11//! let agent = Agent::new("agent.acl").await?;
12//! let session = agent.session_async("/my-project", None).await?;
13//! let result = session.send("Explain the auth module", None).await?;
14//! println!("{}", result.text);
15//! # Ok(())
16//! # }
17//! ```
18
19use crate::agent::{AgentConfig, AgentEvent, AgentResult};
20use crate::commands::CommandRegistry;
21use crate::config::CodeConfig;
22use crate::error::Result;
23use crate::hitl::PendingConfirmationInfo;
24use crate::llm::{LlmClient, Message};
25use crate::prompts::{PlanningMode, SystemPromptSlots};
26use crate::queue::{
27 ExternalTask, ExternalTaskResult, LaneHandlerConfig, SessionLane, SessionQueueConfig,
28 SessionQueueStats,
29};
30use crate::tools::{ToolContext, ToolExecutor};
31use a3s_lane::{DeadLetter, MetricsSnapshot};
32use a3s_memory::MemoryStore;
33use std::collections::HashMap;
34use std::path::{Path, PathBuf};
35use std::sync::{Arc, RwLock};
36use tokio::sync::mpsc;
37use tokio::task::JoinHandle;
38mod agent_binding;
39mod agent_bootstrap;
40mod agent_facade;
41mod agent_loop_runtime;
42mod agent_sessions;
43mod capabilities;
44mod capability_facade;
45mod command_runtime;
46mod conversation_runtime;
47mod direct_tool_facade;
48mod direct_tools;
49mod governance_facade;
50mod hook_control;
51mod project_instructions;
52mod run_admission;
53mod run_facade;
54mod run_lifecycle;
55mod runtime;
56mod runtime_events;
57mod session_builder;
58mod session_clock;
59mod session_close;
60mod session_commands;
61mod session_config;
62mod session_extensions;
63mod session_facade;
64mod session_hitl;
65mod session_options;
66mod session_persistence;
67mod session_queue;
68mod session_runs;
69mod session_runtime;
70mod session_save;
71mod session_verification;
72mod session_view;
73mod workflow_facade;
74pub use agent_facade::{Agent, SessionBuilder};
75use direct_tools::DirectToolRuntime;
76use hook_control::HookControl;
77use runtime_events::ActiveToolState;
78use session_close::SessionCloseHandle;
79use session_extensions::SessionExtensionRuntime;
80use session_hitl::HitlControl;
81use session_queue::QueueControl;
82use session_runs::RunControl;
83use session_verification::VerificationRuntime;
84use session_view::SessionView;
85
86/// Canonicalize a path, stripping the Windows `\\?\` UNC prefix to avoid
87/// polluting workspace strings throughout the system (prompts, session data, etc.).
88fn safe_canonicalize(path: &Path) -> PathBuf {
89 match std::fs::canonicalize(path) {
90 Ok(p) => strip_unc_prefix(p),
91 Err(_) => path.to_path_buf(),
92 }
93}
94
95/// Strip the Windows extended-length path prefix (`\\?\`) that `canonicalize()` adds.
96/// On non-Windows this is a no-op.
97fn strip_unc_prefix(path: PathBuf) -> PathBuf {
98 #[cfg(windows)]
99 {
100 let s = path.to_string_lossy();
101 if let Some(stripped) = s.strip_prefix(r"\\?\") {
102 return PathBuf::from(stripped);
103 }
104 }
105 path
106}
107
108// ============================================================================
109// ToolCallResult
110// ============================================================================
111
112/// Result of a direct tool execution (no LLM).
113#[derive(Debug, Clone)]
114pub struct ToolCallResult {
115 pub name: String,
116 pub output: String,
117 pub exit_code: i32,
118 pub metadata: Option<serde_json::Value>,
119 /// Structured discriminant for tool failures. `None` when the tool
120 /// either succeeded or failed without a typed reason (the message in
121 /// `output` is then the only diagnostic). Populated for known
122 /// kinds such as `VersionConflict` so SDK callers can branch on the
123 /// `type` field instead of regex-matching `output`.
124 pub error_kind: Option<crate::tools::ToolErrorKind>,
125}
126
127/// Result of admitting a host-selected run identity for detached execution.
128///
129/// `Started` owns a supervisor task that drains the ordinary Code event stream
130/// into the authoritative run store. Dropping the handle detaches the task;
131/// call [`AgentSession::close`] for graceful cancellation and cleanup.
132pub enum AgentRunSpawn {
133 Started {
134 snapshot: crate::run::RunSnapshot,
135 worker: JoinHandle<()>,
136 },
137 Replayed {
138 snapshot: crate::run::RunSnapshot,
139 },
140}
141
142impl AgentRunSpawn {
143 pub const fn replayed(&self) -> bool {
144 matches!(self, Self::Replayed { .. })
145 }
146
147 pub fn snapshot(&self) -> &crate::run::RunSnapshot {
148 match self {
149 Self::Started { snapshot, .. } | Self::Replayed { snapshot } => snapshot,
150 }
151 }
152}
153
154// ============================================================================
155// ReadFileOptions
156// ============================================================================
157
158/// Optional line-range controls for direct `read` tool calls.
159#[derive(Debug, Clone, Copy, Default)]
160pub struct ReadFileOptions {
161 /// 0-indexed line offset to start reading from.
162 pub offset: Option<usize>,
163 /// Maximum number of lines to read.
164 pub limit: Option<usize>,
165}
166
167// ============================================================================
168// SessionOptions
169// ============================================================================
170
171/// Optional per-session overrides.
172#[derive(Clone, Default)]
173pub struct SessionOptions {
174 /// Override the default model. Format: `"provider/model"` (e.g., `"openai/gpt-4o"`).
175 pub model: Option<String>,
176 /// Priority used for top-level operations created by this session.
177 pub task_priority: crate::task_scheduler::TaskPriority,
178 /// Extra directories to scan for agent files.
179 /// Merged with any global `agent_dirs` from [`CodeConfig`].
180 pub agent_dirs: Vec<PathBuf>,
181 /// Reproducible disposable workers registered for task delegation.
182 /// Explicit session workers override agents loaded from directories by name.
183 pub worker_agents: Vec<crate::subagent::WorkerAgentSpec>,
184 /// Optional queue configuration for lane-based tool execution.
185 ///
186 /// When set, enables priority-based tool scheduling with parallel execution
187 /// of read-only (Query-lane) tools, DLQ, metrics, and external task handling.
188 pub queue_config: Option<SessionQueueConfig>,
189 /// Optional security provider for taint tracking and output sanitization
190 pub security_provider: Option<Arc<dyn crate::security::SecurityProvider>>,
191 /// Optional host-supplied LLM client.
192 ///
193 /// When set, it is used directly, overriding the `provider/model`
194 /// factory resolution — the one Action-layer backend that was previously
195 /// only injectable in test code. Lets a host plug in a provider the
196 /// built-in factory does not cover, a deterministic record/replay client,
197 /// or an HTTP-layer proxy/audit wrapper. Mirrors `workspace_services`.
198 pub llm_client: Option<Arc<dyn crate::llm::LlmClient>>,
199 /// Optional context providers for RAG
200 pub context_providers: Vec<Arc<dyn crate::context::ContextProvider>>,
201 /// One exact-generation cognitive package supplied by the embedding host.
202 ///
203 /// This is separate from general-purpose RAG providers: Code persists the
204 /// immutable package binding and treats provider failure as fatal. The host
205 /// retains A3S Use lease, Registry, installation, and lifecycle authority.
206 pub cognitive_context: Option<crate::cognitive_context::CognitiveContextSession>,
207 /// Optional confirmation manager for HITL
208 pub confirmation_manager: Option<Arc<dyn crate::hitl::ConfirmationProvider>>,
209 /// Optional confirmation policy (will be used to create ConfirmationManager if confirmation_manager is not set)
210 pub confirmation_policy: Option<crate::hitl::ConfirmationPolicy>,
211 /// Optional permission checker
212 pub permission_checker: Option<Arc<dyn crate::permissions::PermissionChecker>>,
213 /// Serializable permission policy used to build the checker, when available.
214 pub permission_policy: Option<crate::permissions::PermissionPolicy>,
215 /// Enable planning
216 pub planning_mode: PlanningMode,
217 /// Enable goal tracking
218 pub goal_tracking: bool,
219 /// Extra directories to scan for skill files (*.md).
220 /// Merged with any global `skill_dirs` from [`CodeConfig`].
221 pub skill_dirs: Vec<PathBuf>,
222 /// Optional skill registry for instruction injection
223 pub skill_registry: Option<Arc<crate::skills::SkillRegistry>>,
224 /// Whether active skill `allowed-tools` restrict ordinary session tool calls.
225 ///
226 /// Defaults to false so ordinary tools continue through permission policy,
227 /// hooks, and HITL. Set true to restore the legacy global active-skill
228 /// restriction behavior.
229 pub enforce_active_skill_tool_restrictions: Option<bool>,
230 /// Custom memory store override for long-term memory persistence.
231 ///
232 /// Sessions resolve a default store when this is not set.
233 pub memory_store: Option<Arc<dyn MemoryStore>>,
234 /// Host observers notified after successful durable memory writes.
235 pub memory_observers: Vec<Arc<dyn crate::memory::MemoryObserver>>,
236 /// Deferred file memory directory — constructed async in `build_session()`
237 pub(crate) file_memory_dir: Option<PathBuf>,
238 /// Optional session store for persistence
239 pub session_store: Option<Arc<dyn crate::store::SessionStore>>,
240 /// Deferred file session-store directory, initialized by the async builder.
241 pub(crate) file_session_store_dir: Option<PathBuf>,
242 /// Explicit session ID (auto-generated if not set)
243 pub session_id: Option<String>,
244 /// Multi-tenant identifier. Framework only transports this string;
245 /// the host decides what "tenant" means and how to
246 /// aggregate/bill on it. Emitted to hooks/traces, persisted in
247 /// `SessionData`, never interpreted by core.
248 pub tenant_id: Option<String>,
249 /// Identity of the principal that triggered this session (user id,
250 /// service account, etc). Treated as opaque.
251 pub principal: Option<String>,
252 /// Logical identifier of the agent template / definition the session
253 /// was instantiated from. Lets the host aggregate sessions by
254 /// "which agent recipe" independent of the concrete session id.
255 pub agent_template_id: Option<String>,
256 /// Distributed-trace correlation id. Propagated through hooks/traces
257 /// so a session's events join with upstream/downstream work in the
258 /// host's observability pipeline.
259 pub correlation_id: Option<String>,
260 /// Optional host-supplied budget / quota guard. The framework calls
261 /// into it before each LLM call (and reports actuals after) so the
262 /// host can refuse or rate-limit at the cluster level. Default is
263 /// `None` (no enforcement — equivalent to
264 /// [`NoopBudgetGuard`](crate::budget::NoopBudgetGuard)).
265 pub budget_guard: Option<Arc<dyn crate::budget::BudgetGuard>>,
266 /// Optional host-provided ID/Clock pair. Replaces the default
267 /// random-UUID + wall-clock pair, enabling deterministic replay
268 /// on another node. `None` keeps pre-P2 behaviour.
269 pub host_env: Option<Arc<crate::host_env::HostEnv>>,
270 /// Optional FIFO retention caps on the session's in-memory stores
271 /// (run records, run events, trace events, terminal subagent
272 /// tasks). `None` selects [`SessionRetentionLimits::default`](crate::retention::SessionRetentionLimits::default),
273 /// which is finite. Pass [`SessionRetentionLimits::unbounded`](crate::retention::SessionRetentionLimits::unbounded)
274 /// explicitly to retain everything.
275 pub retention_limits: Option<crate::retention::SessionRetentionLimits>,
276 /// Optional structured JSONL trajectory config.
277 ///
278 /// When set, a3s-code records user prompts, LLM turns, tool calls,
279 /// tool observations, token usage, and execution end status for RL
280 /// training or service data collection. If unset, the same config can
281 /// be enabled by `A3S_CODE_TRAJECTORY_PATH`.
282 pub rl_trajectory: Option<crate::rl_trajectory::RlTrajectoryConfig>,
283 /// Request token-level log probabilities from compatible LLM providers.
284 ///
285 /// This is off by default because many public providers reject logprob
286 /// requests with tool calls. Training/evaluation harnesses using compatible
287 /// OpenAI-style backends can enable it explicitly.
288 pub llm_logprobs: Option<bool>,
289 /// Number of alternative token logprobs to request per generated token.
290 pub llm_top_logprobs: Option<usize>,
291 /// Auto-save after each completed `send()` or default-history `stream()` call.
292 pub auto_save: bool,
293 /// Optional artifact retention limits for large tool/program outputs.
294 pub artifact_store_limits: Option<crate::tools::ArtifactStoreLimits>,
295 /// Host-pinned deterministic policy for projecting large tool results.
296 ///
297 /// `None` selects the conservative compatibility profile. The resolved
298 /// policy is persisted and cannot drift when the session is resumed.
299 pub tool_result_transform_policy: Option<crate::tools::ToolResultTransformPolicyV1>,
300 /// Max consecutive parse errors before aborting (overrides default of 2).
301 /// `None` uses the `AgentConfig` default.
302 pub max_parse_retries: Option<u32>,
303 /// Per-tool execution timeout in milliseconds.
304 /// `None` = no timeout (default).
305 pub tool_timeout_ms: Option<u64>,
306 /// Per-model API HTTP timeout in milliseconds.
307 /// `None` = no timeout (default).
308 pub llm_api_timeout_ms: Option<u64>,
309 /// Circuit-breaker threshold: max consecutive LLM API failures before
310 /// aborting in non-streaming mode (overrides default of 3).
311 /// `None` uses the `AgentConfig` default.
312 pub circuit_breaker_threshold: Option<u32>,
313 /// Max consecutive identical tool signatures before the guard turns the
314 /// duplicate call into a failed tool observation.
315 ///
316 /// `None` uses the `AgentConfig` default. Hosts can raise this for long
317 /// research sessions where repeating a read/search with the same arguments
318 /// is wasteful but should not make the whole run brittle.
319 pub duplicate_tool_call_threshold: Option<u32>,
320 /// Optional concrete sandbox implementation.
321 ///
322 /// When set, `bash` tool commands are routed through this sandbox instead
323 /// of `std::process::Command`. The host application constructs and owns
324 /// the implementation (e.g., an A3S Box–backed handle).
325 pub sandbox_handle: Option<Arc<dyn crate::sandbox::BashSandbox>>,
326 /// Optional host-provided workspace backend.
327 ///
328 /// When set, built-in tools such as `read`, `write`, `ls`, and `bash`
329 /// execute against these workspace capabilities instead of assuming the
330 /// server-local filesystem. This is the primary extension point for DFS,
331 /// browser, container, and remote workspace deployments.
332 pub workspace_services: Option<Arc<crate::workspace::WorkspaceServices>>,
333 /// Enable auto-compaction when context usage exceeds threshold.
334 pub auto_compact: bool,
335 /// Context usage percentage threshold for auto-compaction (0.0 - 1.0).
336 /// Default: 0.80 (80%).
337 pub auto_compact_threshold: Option<f32>,
338 /// Model context window used for automatic compaction accounting.
339 ///
340 /// When omitted, the configured model's declared context limit is used,
341 /// falling back to the agent default when the model has no declaration.
342 pub max_context_tokens: Option<usize>,
343 /// Inject a continuation message when the LLM stops without completing the task.
344 /// `None` uses the `AgentConfig` default (true).
345 pub continuation_enabled: Option<bool>,
346 /// Maximum continuation injections per execution.
347 /// `None` uses the `AgentConfig` default (3).
348 pub max_continuation_turns: Option<u32>,
349 /// Maximum execution time in milliseconds.
350 /// `None` = no timeout (default).
351 /// When set, the execution loop will abort if it exceeds this duration.
352 pub max_execution_time_ms: Option<u64>,
353 /// Optional MCP manager for connecting to external MCP servers.
354 ///
355 /// When set, all tools from connected MCP servers are registered and
356 /// available during agent execution with names like `mcp__server__tool`.
357 pub mcp_manager: Option<Arc<crate::mcp::manager::McpManager>>,
358 /// Sampling temperature (0.0–1.0). Overrides the provider default.
359 pub temperature: Option<f32>,
360 /// Extended thinking budget in tokens (Anthropic only).
361 pub thinking_budget: Option<usize>,
362 /// Per-session tool round limit override.
363 ///
364 /// When set, overrides the agent-level `max_tool_rounds` for this session only.
365 /// Maps directly from [`AgentDefinition::max_steps`](crate::subagent::AgentDefinition::max_steps)
366 /// when creating sessions
367 /// via [`Agent::session_for_agent`].
368 pub max_tool_rounds: Option<usize>,
369 /// Per-session parallel fan-out limit override.
370 ///
371 /// Applies to delegated `task` fan-out, the legacy `parallel_task` alias,
372 /// plan wave execution, and safe parallel write batches.
373 pub max_parallel_tasks: Option<usize>,
374 /// Per-session automatic subagent delegation override.
375 pub auto_delegation: Option<crate::config::AutoDelegationConfig>,
376 /// Per-session switch for model-visible manual child-agent tools.
377 ///
378 /// This overlays the effective automatic delegation config instead of
379 /// replacing it, so callers can hide `task` and its compatibility alias
380 /// while preserving other delegation settings.
381 pub manual_delegation_enabled: Option<bool>,
382 /// Per-session kill switch for automatic parallel child-agent fan-out.
383 ///
384 /// This overlays the effective automatic delegation config instead of
385 /// replacing it, so callers can disable auto fan-out without disabling
386 /// automatic delegation itself.
387 pub auto_parallel_delegation: Option<bool>,
388 /// Slot-based system prompt customization.
389 ///
390 /// When set, overrides the agent-level prompt slots for this session.
391 /// Users can customize role, guidelines, response style, and extra instructions
392 /// without losing the core agentic capabilities.
393 pub prompt_slots: Option<SystemPromptSlots>,
394 /// Optional external hook executor.
395 ///
396 /// When set, it replaces the built-in `HookEngine` for this session.
397 pub hook_executor: Option<Arc<dyn crate::hooks::HookExecutor>>,
398}
399
400// ============================================================================
401// AgentSession
402// ============================================================================
403
404/// Workspace-bound session. All LLM and tool operations happen here.
405///
406/// History is automatically accumulated after each `send()` call and after
407/// `stream()` completes when no custom history is supplied.
408/// Use `history()` to retrieve the current conversation log.
409///
410/// Conversation operations are single-flight, including slash commands and
411/// checkpoint resume. An overlapping call returns
412/// [`CodeError::SessionBusy`](crate::error::CodeError::SessionBusy) immediately.
413/// A streaming operation remains active until its returned handle completes.
414pub struct AgentSession {
415 llm_client: Arc<dyn LlmClient>,
416 /// Provider-reported generation capacity shared by every loop and
417 /// host-direct tool call created for this session.
418 model_generation_admission: crate::llm::ModelGenerationAdmission,
419 /// Agent-wide execution admission shared across sibling sessions.
420 task_scheduler: Arc<crate::task_scheduler::TaskScheduler>,
421 /// Base priority for this session's top-level operations.
422 task_priority: crate::task_scheduler::TaskPriority,
423 tool_executor: Arc<ToolExecutor>,
424 tool_context: ToolContext,
425 config: AgentConfig,
426 tool_result_transform_policy: crate::tools::ToolResultTransformPolicyV1,
427 /// Host provider plus the durable exact-generation package binding.
428 cognitive_context: Option<crate::cognitive_context::CognitiveContextSession>,
429 workspace: PathBuf,
430 /// Unique session identifier.
431 session_id: String,
432 /// Internal conversation history, auto-updated after each `send()` and default-history `stream()`.
433 history: Arc<RwLock<Vec<Message>>>,
434 /// Fail-fast single-flight admission for transcript-affecting operations.
435 run_admission: Arc<run_admission::RunAdmission>,
436 /// Optional lane queue for priority-based tool execution.
437 command_queue: Option<Arc<crate::session_lane_queue::SessionLaneQueue>>,
438 /// Long-term memory handle.
439 ///
440 /// Built sessions resolve a default memory store. This remains optional for
441 /// compatibility with lower-level/manual construction paths.
442 memory: Option<Arc<crate::memory::AgentMemory>>,
443 /// Optional session store for persistence.
444 session_store: Option<Arc<dyn crate::store::SessionStore>>,
445 /// Runtime-owned fields used to build lossless persistence generations.
446 persistence_state: Arc<RwLock<session_persistence::SessionPersistenceState>>,
447 /// Auto-save after each completed `send()` or default-history `stream()`.
448 auto_save: bool,
449 /// Hook engine for lifecycle event interception.
450 hook_engine: Arc<crate::hooks::HookEngine>,
451 /// Optional external hook executor. When set, replaces `hook_engine` as the
452 /// executor passed to each `AgentLoop`.
453 hook_executor: Option<Arc<dyn crate::hooks::HookExecutor>>,
454 /// Deferred init warning: emitted as PersistenceFailed on first send() if set.
455 init_warning: Option<String>,
456 /// Slash command registry for `/command` dispatch.
457 /// Uses interior mutability so commands can be registered on a shared `Arc<AgentSession>`.
458 command_registry: std::sync::Mutex<CommandRegistry>,
459 /// Model identifier for display (e.g., "anthropic/claude-sonnet-4-20250514").
460 model_name: String,
461 /// Session-private MCP manager used by live add/remove operations.
462 mcp_manager: Arc<crate::mcp::manager::McpManager>,
463 /// Read-only MCP sources inherited from the parent agent and session options.
464 inherited_mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
465 /// Ordered MCP capability sources inherited by delegated child runs.
466 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
467 /// Shared agent registry — populated at session creation; extended via register_agent_dir().
468 agent_registry: Arc<crate::subagent::AgentRegistry>,
469 /// Cancellation token for the current operation (send/stream).
470 /// Stored so that cancel() can abort ongoing LLM calls.
471 cancel_token: Arc<tokio::sync::Mutex<Option<tokio_util::sync::CancellationToken>>>,
472 /// ID of the run currently attached to the active cancellation token.
473 current_run_id: Arc<tokio::sync::Mutex<Option<String>>>,
474 /// In-memory run snapshots and event replay buffer for this session.
475 run_store: Arc<crate::run::InMemoryRunStore>,
476 /// Materialized view of delegated subagent task lifecycle, populated from runtime events.
477 subagent_tasks: Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>,
478 /// Currently executing tools observed from runtime events.
479 active_tools: Arc<tokio::sync::RwLock<HashMap<String, ActiveToolState>>>,
480 /// Compact execution traces for this session.
481 trace_sink: crate::trace::InMemoryTraceSink,
482 /// Structured completion evidence collected from agent and explicit verification runs.
483 verification_reports: Arc<RwLock<Vec<crate::verification::VerificationReport>>>,
484 /// Set once `close()` has been called. Subsequent send/stream calls
485 /// fast-fail with [`crate::error::CodeError::SessionClosed`].
486 closed: Arc<std::sync::atomic::AtomicBool>,
487 /// Session-level parent cancellation token.
488 ///
489 /// Every in-flight run (blocking send, stream, delegated subagent task)
490 /// derives its per-operation token from this one via `child_token()`,
491 /// so `session_cancel.cancel()` cascades to all of them. `close()` fires
492 /// this token first, after which any new `child_token()` returns an
493 /// already-cancelled token (defending against close/spawn races).
494 pub(crate) session_cancel: tokio_util::sync::CancellationToken,
495 /// Shared `Arc`-handle used by both [`AgentSession::close`] and the
496 /// parent [`Agent`]'s registry. The handle bundles every field needed
497 /// to perform the close sequence so the two entry points cannot drift.
498 close_handle: Arc<SessionCloseHandle>,
499 /// Runtime-mutable override for the budget guard. When set, takes
500 /// precedence over `config.budget_guard` on the next agent-loop
501 /// build. Lets SDK callers (Node especially) install a host-side
502 /// guard after `session()` has returned without ever putting a
503 /// JS callable into `SessionOptions`.
504 runtime_budget_guard: std::sync::Mutex<Option<Arc<dyn crate::budget::BudgetGuard>>>,
505 /// Multi-tenant label. Framework only carries the string; semantics
506 /// belong to the host.
507 pub(crate) tenant_id: Option<String>,
508 /// Principal that triggered the session (user / service / etc.).
509 pub(crate) principal: Option<String>,
510 /// Logical identifier of the agent template the session was
511 /// instantiated from.
512 pub(crate) agent_template_id: Option<String>,
513 /// Distributed-trace correlation id propagated to hooks / traces.
514 pub(crate) correlation_id: Option<String>,
515}
516
517// ============================================================================
518// Tests
519// ============================================================================
520
521#[cfg(test)]
522mod replacement_tests;
523#[cfg(test)]
524mod tests;