Skip to main content

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;
47pub(crate) use conversation_runtime::{
48    ExactRecoveryError, ExactRecoveryPreparation, PreparedExactRecovery,
49};
50mod direct_tool_facade;
51mod direct_tools;
52mod execution_coordinator;
53mod governance_facade;
54mod hook_control;
55mod project_instructions;
56#[cfg(feature = "dynamic-workflow")]
57mod projected_flow;
58mod projected_host_capability;
59mod projected_ui;
60mod run_admission;
61mod run_facade;
62mod run_hook_executor;
63mod run_lifecycle;
64mod runtime;
65mod runtime_checkpoints;
66mod runtime_events;
67mod session_builder;
68mod session_clock;
69mod session_close;
70mod session_commands;
71mod session_config;
72mod session_extensions;
73mod session_facade;
74mod session_hitl;
75mod session_options;
76mod session_persistence;
77mod session_queue;
78mod session_review_facade;
79mod session_runs;
80mod session_runtime;
81mod session_sandbox;
82mod session_save;
83mod session_verification;
84mod session_view;
85mod workflow_facade;
86pub use agent_facade::{Agent, SessionBuilder};
87use direct_tools::DirectToolRuntime;
88use hook_control::HookControl;
89#[cfg(feature = "dynamic-workflow")]
90pub use projected_flow::ProjectedFlowHandle;
91pub use projected_ui::ProjectedUiHandle;
92use runtime_events::ActiveToolState;
93use session_close::SessionCloseHandle;
94use session_extensions::SessionExtensionRuntime;
95use session_hitl::HitlControl;
96use session_queue::QueueControl;
97use session_runs::RunControl;
98use session_verification::VerificationRuntime;
99use session_view::SessionView;
100
101/// Canonicalize a path, stripping the Windows `\\?\` UNC prefix to avoid
102/// polluting workspace strings throughout the system (prompts, session data, etc.).
103fn safe_canonicalize(path: &Path) -> PathBuf {
104    match std::fs::canonicalize(path) {
105        Ok(p) => strip_unc_prefix(p),
106        Err(_) => path.to_path_buf(),
107    }
108}
109
110/// Strip the Windows extended-length path prefix (`\\?\`) that `canonicalize()` adds.
111/// On non-Windows this is a no-op.
112fn strip_unc_prefix(path: PathBuf) -> PathBuf {
113    #[cfg(windows)]
114    {
115        let s = path.to_string_lossy();
116        if let Some(stripped) = s.strip_prefix(r"\\?\") {
117            return PathBuf::from(stripped);
118        }
119    }
120    path
121}
122
123// ============================================================================
124// ToolCallResult
125// ============================================================================
126
127/// Result of a direct tool execution (no LLM).
128#[derive(Debug, Clone)]
129pub struct ToolCallResult {
130    pub name: String,
131    pub output: String,
132    pub exit_code: i32,
133    pub metadata: Option<serde_json::Value>,
134    /// Structured discriminant for tool failures. `None` when the tool
135    /// either succeeded or failed without a typed reason (the message in
136    /// `output` is then the only diagnostic). Populated for known
137    /// kinds such as `VersionConflict` so SDK callers can branch on the
138    /// `type` field instead of regex-matching `output`.
139    pub error_kind: Option<crate::tools::ToolErrorKind>,
140}
141
142/// Result of admitting a host-selected run identity for detached execution.
143///
144/// `Started` owns a supervisor task that drains the ordinary Code event stream
145/// into the authoritative run store. Dropping the handle detaches the task;
146/// call [`AgentSession::close`] for graceful cancellation and cleanup.
147pub enum AgentRunSpawn {
148    Started {
149        snapshot: crate::run::RunSnapshot,
150        worker: JoinHandle<()>,
151    },
152    Replayed {
153        snapshot: crate::run::RunSnapshot,
154    },
155}
156
157impl AgentRunSpawn {
158    pub const fn replayed(&self) -> bool {
159        matches!(self, Self::Replayed { .. })
160    }
161
162    pub fn snapshot(&self) -> &crate::run::RunSnapshot {
163        match self {
164            Self::Started { snapshot, .. } | Self::Replayed { snapshot } => snapshot,
165        }
166    }
167}
168
169// ============================================================================
170// ReadFileOptions
171// ============================================================================
172
173/// Optional line-range controls for direct `read` tool calls.
174#[derive(Debug, Clone, Copy, Default)]
175pub struct ReadFileOptions {
176    /// 0-indexed line offset to start reading from.
177    pub offset: Option<usize>,
178    /// Maximum number of lines to read.
179    pub limit: Option<usize>,
180}
181
182// ============================================================================
183// SessionOptions
184// ============================================================================
185
186/// Optional per-session overrides.
187#[derive(Clone, Default)]
188pub struct SessionOptions {
189    /// Override the default model. Format: `"provider/model"` (e.g., `"openai/gpt-4o"`).
190    pub model: Option<String>,
191    /// Priority used for top-level operations created by this session.
192    pub task_priority: crate::task_scheduler::TaskPriority,
193    /// Extra directories to scan for agent files.
194    /// Merged with any global `agent_dirs` from [`CodeConfig`].
195    pub agent_dirs: Vec<PathBuf>,
196    /// Reproducible disposable workers registered for task delegation.
197    /// Explicit session workers override agents loaded from directories by name.
198    pub worker_agents: Vec<crate::subagent::WorkerAgentSpec>,
199    /// Optional queue configuration for lane-based tool execution.
200    ///
201    /// When set, enables priority-based tool scheduling with parallel execution
202    /// of read-only (Query-lane) tools, DLQ, metrics, and external task handling.
203    pub queue_config: Option<SessionQueueConfig>,
204    /// Optional per-session web-search configuration.
205    ///
206    /// When set, this value overrides the agent-level `CodeConfig.search`
207    /// configuration for every built-in search/download/web-fetch tool in the
208    /// session. Keeping the value on `SessionOptions` makes the complete
209    /// search surface available to language SDKs without requiring callers to
210    /// rewrite an ACL file.
211    pub search_config: Option<crate::config::SearchConfig>,
212    /// Optional security provider for taint tracking and output sanitization
213    pub security_provider: Option<Arc<dyn crate::security::SecurityProvider>>,
214    /// Optional host-supplied LLM client.
215    ///
216    /// When set, it is used directly, overriding the `provider/model`
217    /// factory resolution — the one Action-layer backend that was previously
218    /// only injectable in test code. Lets a host plug in a provider the
219    /// built-in factory does not cover, a deterministic record/replay client,
220    /// or an HTTP-layer proxy/audit wrapper. Mirrors `workspace_services`.
221    pub llm_client: Option<Arc<dyn crate::llm::LlmClient>>,
222    /// Optional context providers for RAG
223    pub context_providers: Vec<Arc<dyn crate::context::ContextProvider>>,
224    /// One exact-generation cognitive package supplied by the embedding host.
225    ///
226    /// This is separate from general-purpose RAG providers: Code persists the
227    /// immutable package binding and treats provider failure as fatal. The host
228    /// retains A3S Use lease, Registry, installation, and lifecycle authority.
229    pub cognitive_context: Option<crate::cognitive_context::CognitiveContextSession>,
230    /// Optional confirmation manager for HITL
231    pub confirmation_manager: Option<Arc<dyn crate::hitl::ConfirmationProvider>>,
232    /// Optional confirmation policy (will be used to create ConfirmationManager if confirmation_manager is not set)
233    pub confirmation_policy: Option<crate::hitl::ConfirmationPolicy>,
234    /// Optional permission checker
235    pub permission_checker: Option<Arc<dyn crate::permissions::PermissionChecker>>,
236    /// Serializable permission policy used to build the checker, when available.
237    pub permission_policy: Option<crate::permissions::PermissionPolicy>,
238    /// Enable planning
239    pub planning_mode: PlanningMode,
240    /// Enable goal tracking
241    pub goal_tracking: bool,
242    /// Extra directories to scan for skill files (*.md).
243    /// Merged with any global `skill_dirs` from [`CodeConfig`].
244    pub skill_dirs: Vec<PathBuf>,
245    /// Extra environment variables merged into Bash / sandbox command execution.
246    ///
247    /// Hosts use this to inject absolute tool paths (for example `A3S_BOX_CLI`
248    /// and a PATH prefix) without requiring the agent process to inherit a
249    /// full desktop environment. Values are applied on top of the sandbox
250    /// safe-env allowlist.
251    pub command_env: Option<HashMap<String, String>>,
252    /// Optional skill registry for instruction injection
253    pub skill_registry: Option<Arc<crate::skills::SkillRegistry>>,
254    /// Host-owned skills registered after skill directories.
255    ///
256    /// A directory skill with the same name does not replace these. They stay
257    /// protected from later external projection, the same way built-ins are.
258    pub host_skills: Vec<Arc<crate::skills::Skill>>,
259    /// Whether active skill `allowed-tools` restrict ordinary session tool calls.
260    ///
261    /// Defaults to false so ordinary tools continue through permission policy,
262    /// hooks, and HITL. Set true to restore the legacy global active-skill
263    /// restriction behavior.
264    pub enforce_active_skill_tool_restrictions: Option<bool>,
265    /// Custom memory store override for long-term memory persistence.
266    ///
267    /// Sessions resolve a default store when this is not set.
268    pub memory_store: Option<Arc<dyn MemoryStore>>,
269    /// Optional exact V2 durable-memory binding.
270    ///
271    /// Active recall requires explicit evidence-backed activation and records
272    /// final context admission. Extraction may still write Candidate nodes;
273    /// only activated nodes are eligible for recall. The live repository
274    /// remains runtime-only; its secret-free namespace, mode, recall policy,
275    /// and retrieval profile are persisted for exact resume validation.
276    pub durable_memory: Option<crate::durable_memory::DurableMemorySession>,
277    /// Host observers notified after successful durable memory writes.
278    pub memory_observers: Vec<Arc<dyn crate::memory::MemoryObserver>>,
279    /// Typed built-in and host jobs plus shutdown limits for owned memory maintenance.
280    ///
281    /// This runtime-only option is not persisted. A resumed session must inject
282    /// semantic refresh and custom consolidation schedules again.
283    pub memory_maintenance: crate::memory::MemoryMaintenanceOptions,
284    /// Deferred file memory directory — constructed async in `build_session()`
285    pub(crate) file_memory_dir: Option<PathBuf>,
286    /// Optional session store for persistence
287    pub session_store: Option<Arc<dyn crate::store::SessionStore>>,
288    /// Host-owned destination for exact live tool-boundary checkpoints.
289    ///
290    /// This extension does not replace the Session store. Code captures one
291    /// canonical `SessionSnapshotV1` and its matching logical resume boundary,
292    /// then hands the immutable export to this sink. Host storage policy and
293    /// cross-process fencing remain outside Code.
294    pub session_checkpoint_export_sink:
295        Option<Arc<dyn crate::session_checkpoint::SessionCheckpointExportSink>>,
296    /// Deferred file session-store directory, initialized by the async builder.
297    pub(crate) file_session_store_dir: Option<PathBuf>,
298    /// Explicit session ID (auto-generated if not set)
299    pub session_id: Option<String>,
300    /// Multi-tenant identifier. Framework only transports this string;
301    /// the host decides what "tenant" means and how to
302    /// aggregate/bill on it. Emitted to hooks/traces, persisted in
303    /// `SessionData`, never interpreted by core.
304    pub tenant_id: Option<String>,
305    /// Identity of the principal that triggered this session (user id,
306    /// service account, etc). Treated as opaque.
307    pub principal: Option<String>,
308    /// Logical identifier of the agent template / definition the session
309    /// was instantiated from. Lets the host aggregate sessions by
310    /// "which agent recipe" independent of the concrete session id.
311    pub agent_template_id: Option<String>,
312    /// Distributed-trace correlation id. Propagated through hooks/traces
313    /// so a session's events join with upstream/downstream work in the
314    /// host's observability pipeline.
315    pub correlation_id: Option<String>,
316    /// Optional host-supplied budget / quota guard. The framework calls
317    /// into it before each LLM call (and reports actuals after) so the
318    /// host can refuse or rate-limit at the cluster level. Default is
319    /// `None` (no enforcement — equivalent to
320    /// [`NoopBudgetGuard`](crate::budget::NoopBudgetGuard)).
321    pub budget_guard: Option<Arc<dyn crate::budget::BudgetGuard>>,
322    /// Optional host-provided ID/Clock pair. Replaces the default
323    /// random-UUID + wall-clock pair, enabling deterministic replay
324    /// on another node. `None` keeps pre-P2 behaviour.
325    pub host_env: Option<Arc<crate::host_env::HostEnv>>,
326    /// Optional FIFO retention caps on the session's in-memory stores
327    /// (run records, run events, trace events, terminal subagent
328    /// tasks). `None` selects [`SessionRetentionLimits::default`](crate::retention::SessionRetentionLimits::default),
329    /// which is finite. Pass [`SessionRetentionLimits::unbounded`](crate::retention::SessionRetentionLimits::unbounded)
330    /// explicitly to retain everything.
331    pub retention_limits: Option<crate::retention::SessionRetentionLimits>,
332    /// Optional structured JSONL trajectory config.
333    ///
334    /// When set, a3s-code records user prompts, LLM turns, tool calls,
335    /// tool observations, token usage, and execution end status for RL
336    /// training or service data collection. If unset, the same config can
337    /// be enabled by `A3S_CODE_TRAJECTORY_PATH`.
338    pub rl_trajectory: Option<crate::rl_trajectory::RlTrajectoryConfig>,
339    /// Request token-level log probabilities from compatible LLM providers.
340    ///
341    /// This is off by default because many public providers reject logprob
342    /// requests with tool calls. Training/evaluation harnesses using compatible
343    /// OpenAI-style backends can enable it explicitly.
344    pub llm_logprobs: Option<bool>,
345    /// Number of alternative token logprobs to request per generated token.
346    pub llm_top_logprobs: Option<usize>,
347    /// Auto-save after each completed `send()` or default-history `stream()` call.
348    pub auto_save: bool,
349    /// Optional artifact retention limits for large tool/program outputs.
350    pub artifact_store_limits: Option<crate::tools::ArtifactStoreLimits>,
351    /// Host-authorized immutable content adapter for original Tool content.
352    ///
353    /// The adapter is session-scoped and Rust-only. Its secret-free binding is
354    /// persisted; a resumed session must re-inject the exact same binding.
355    pub immutable_content_adapter: Option<crate::tools::ImmutableContentAdapterSession>,
356    /// Host-pinned deterministic policy for projecting large tool results.
357    ///
358    /// `None` selects the conservative compatibility profile. The resolved
359    /// policy is persisted and cannot drift when the session is resumed.
360    pub tool_result_transform_policy: Option<crate::tools::ToolResultTransformPolicyV1>,
361    /// Typed model-facing Tool presentation policy.
362    ///
363    /// The resolved value is frozen per Run and persisted for exact resume.
364    /// It never changes the Session's governed executor or A3S Use generation.
365    pub tool_presentation_profile: Option<crate::tools::ToolPresentationProfileV1>,
366    /// Max consecutive parse errors before aborting (overrides default of 2).
367    /// `None` uses the `AgentConfig` default.
368    pub max_parse_retries: Option<u32>,
369    /// Per-tool execution timeout in milliseconds.
370    /// `None` = no timeout (default).
371    pub tool_timeout_ms: Option<u64>,
372    /// Per-model API HTTP timeout in milliseconds.
373    /// `None` = no timeout (default).
374    pub llm_api_timeout_ms: Option<u64>,
375    /// Circuit-breaker threshold: max consecutive LLM API failures before
376    /// aborting in non-streaming mode (overrides default of 3).
377    /// `None` uses the `AgentConfig` default.
378    pub circuit_breaker_threshold: Option<u32>,
379    /// Max consecutive identical tool signatures before the guard turns the
380    /// duplicate call into a failed tool observation.
381    ///
382    /// `None` uses the `AgentConfig` default. Hosts can raise this for long
383    /// research sessions where repeating a read/search with the same arguments
384    /// is wasteful but should not make the whole run brittle.
385    pub duplicate_tool_call_threshold: Option<u32>,
386    /// Optional custom sandbox implementation.
387    ///
388    /// Local sessions install the A3S native sandbox by default. When set, this
389    /// handle replaces that default for `bash` tool commands. The host
390    /// application constructs and owns the custom implementation (for example,
391    /// an A3S Box-backed handle).
392    pub sandbox_handle: Option<Arc<dyn crate::sandbox::BashSandbox>>,
393    /// When the native sandbox cannot initialize, allow installing the
394    /// process-host Bash runner instead of an error-only handle.
395    ///
396    /// Default is `false` (fail-closed). Harbor / Terminal-Bench containers
397    /// that already isolate the job set this (or
398    /// `A3S_CODE_ALLOW_PROCESS_HOST_SANDBOX=1`) so default `bash` does not
399    /// require a `require_escalated` trial round.
400    pub allow_process_host_sandbox: bool,
401    /// Optional host-provided workspace backend.
402    ///
403    /// When set, built-in tools such as `read`, `write`, `ls`, and `bash`
404    /// execute against these workspace capabilities instead of assuming the
405    /// server-local filesystem. This is the primary extension point for DFS,
406    /// browser, container, and remote workspace deployments.
407    pub workspace_services: Option<Arc<crate::workspace::WorkspaceServices>>,
408    /// Optional session-owned semantic workspace retrieval configuration.
409    ///
410    /// Construction is asynchronous and ephemeral. Source chunks stay in the
411    /// workspace catalog, vectors stay in memory, and no vector state is
412    /// serialized with the session snapshot.
413    pub workspace_retrieval: Option<crate::workspace::WorkspaceRetrievalOptions>,
414    /// Enable auto-compaction when context usage exceeds threshold.
415    pub auto_compact: bool,
416    /// Context usage percentage threshold for auto-compaction (0.0 - 1.0).
417    /// Default: 0.80 (80%).
418    pub auto_compact_threshold: Option<f32>,
419    /// Model context window used for automatic compaction accounting.
420    ///
421    /// When omitted, the configured model's declared context limit is used,
422    /// falling back to the agent default when the model has no declaration.
423    pub max_context_tokens: Option<usize>,
424    /// Inject a continuation message when the LLM stops without completing the task.
425    /// `None` uses the `AgentConfig` default (true).
426    pub continuation_enabled: Option<bool>,
427    /// Maximum continuation injections per execution.
428    /// `None` uses the `AgentConfig` default (3).
429    pub max_continuation_turns: Option<u32>,
430    /// Maximum execution time in milliseconds.
431    /// `None` = no timeout (default).
432    /// When set, the execution loop, active LLM attempts, and retry backoff
433    /// will abort if they exceed this duration.
434    pub max_execution_time_ms: Option<u64>,
435    /// Optional MCP manager for connecting to external MCP servers.
436    ///
437    /// When set, all tools from connected MCP servers are registered and
438    /// available during agent execution with names like `mcp__server__tool`.
439    pub mcp_manager: Option<Arc<crate::mcp::manager::McpManager>>,
440    /// Sampling temperature (0.0–1.0). Overrides the provider default.
441    pub temperature: Option<f32>,
442    /// Extended thinking budget in tokens (Anthropic only).
443    pub thinking_budget: Option<usize>,
444    /// Per-session tool round limit override.
445    ///
446    /// When set, overrides the agent-level `max_tool_rounds` for this session only.
447    /// Maps directly from [`AgentDefinition::max_steps`](crate::subagent::AgentDefinition::max_steps)
448    /// when creating sessions
449    /// via [`Agent::session_for_agent`].
450    pub max_tool_rounds: Option<usize>,
451    /// Per-session parallel fan-out limit override.
452    ///
453    /// Applies to delegated `task` fan-out, plan wave execution, and safe
454    /// parallel write batches.
455    pub max_parallel_tasks: Option<usize>,
456    /// Per-session automatic subagent delegation override.
457    pub auto_delegation: Option<crate::config::AutoDelegationConfig>,
458    /// Per-session switch for model-visible manual child-agent tools.
459    ///
460    /// This overlays the effective automatic delegation config instead of
461    /// replacing it, so callers can hide `task` while preserving other
462    /// delegation settings.
463    pub manual_delegation_enabled: Option<bool>,
464    /// Per-session kill switch for automatic parallel child-agent fan-out.
465    ///
466    /// This overlays the effective automatic delegation config instead of
467    /// replacing it, so callers can disable auto fan-out without disabling
468    /// automatic delegation itself.
469    pub auto_parallel_delegation: Option<bool>,
470    /// Slot-based system prompt customization.
471    ///
472    /// When set, overrides the agent-level prompt slots for this session.
473    /// Users can customize role, guidelines, response style, and extra instructions
474    /// without losing the core agentic capabilities.
475    pub prompt_slots: Option<SystemPromptSlots>,
476    /// Optional external hook executor.
477    ///
478    /// When set, it replaces the built-in `HookEngine` for this session.
479    pub hook_executor: Option<Arc<dyn crate::hooks::HookExecutor>>,
480    /// When true, writes bind a git worktree and never fall back to the source tree.
481    pub effect_isolation: bool,
482    /// Host-confirmed completion waivers bound to an effect digest.
483    pub completion_waivers: Vec<crate::harness_loop::CompletionWaiverV1>,
484    /// Admitted plan digest for an implementation run. Empty means ordinary.
485    pub plan_run: crate::harness_loop::PlanRunAdmission,
486    /// Path-scoped instruction fragments. The AGENTS.md prefix stays stable.
487    pub path_rules: Vec<crate::path_instructions::PathRule>,
488    /// Opt-in read-only verifier. Default is off so local-code spends no extra model call.
489    pub verifier_enabled: bool,
490    /// Host-only completion attestor (#160). Trait hook; omitted from serializable SDKs.
491    pub completion_attestor:
492        Option<std::sync::Arc<dyn crate::completion_attestor::CompletionAttestor>>,
493    /// Optional Meta Harness compose recipe (`components: [...]` style).
494    ///
495    /// When unset, the stock `coding_actor` tree is admitted. Kernel policy
496    /// (permissions + completion gate) stays Core-owned either way.
497    pub harness: Option<crate::meta_harness::HarnessComposeOptions>,
498    /// Registry for `host:<id>` mounts referenced from [`Self::harness`].
499    pub host_harness_registry: Option<std::sync::Arc<dyn crate::meta_harness::HostHarnessRegistry>>,
500    /// Full custom graph assembler. When set, replaces stock/compose admission.
501    pub host_harness_assembler:
502        Option<std::sync::Arc<dyn crate::meta_harness::HostHarnessAssembler>>,
503    /// Typed external observations bound into the run.
504    pub external_observations: Vec<crate::external_observation::ExternalObservationV1>,
505    /// Outcome ledger written by the promoting host.
506    pub outcome_ledger: crate::outcome_memory::OutcomeLedger,
507    /// A session that cannot write does not get an isolated worktree.
508    pub read_only_session: bool,
509}
510
511// ============================================================================
512// AgentSession
513// ============================================================================
514
515/// Workspace-bound session. All LLM and tool operations happen here.
516///
517/// History is automatically accumulated after each `send()` call and after
518/// `stream()` completes when no custom history is supplied.
519/// Use `history()` to retrieve the current conversation log.
520///
521/// Conversation operations are single-flight, including slash commands and
522/// checkpoint resume. An overlapping call returns
523/// [`CodeError::SessionBusy`](crate::error::CodeError::SessionBusy) immediately.
524/// A streaming operation remains active until its returned handle completes.
525pub struct AgentSession {
526    llm_client: Arc<dyn LlmClient>,
527    /// Provider-reported generation capacity shared by every loop and
528    /// host-direct tool call created for this session.
529    model_generation_admission: crate::llm::ModelGenerationAdmission,
530    /// Secret-free middleware stage counters shared by every loop rebuilt for
531    /// this session (`OPT-OBS1`).
532    middleware_obs: Arc<crate::agent::ModelMiddlewareObs>,
533    /// Agent-wide execution admission shared across sibling sessions.
534    task_scheduler: Arc<crate::task_scheduler::TaskScheduler>,
535    /// Base priority for this session's top-level operations.
536    task_priority: crate::task_scheduler::TaskPriority,
537    tool_executor: Arc<ToolExecutor>,
538    /// Session-owned atomic host capability catalog. A Run pins one immutable
539    /// generation before its model definitions and executor are assembled.
540    capability_catalog: Arc<crate::capability::CapabilityCatalog>,
541    tool_context: ToolContext,
542    config: AgentConfig,
543    tool_result_transform_policy: crate::tools::ToolResultTransformPolicyV1,
544    /// Host provider plus the durable exact-generation package binding.
545    cognitive_context: Option<crate::cognitive_context::CognitiveContextSession>,
546    /// General-purpose providers supplied by the embedding host before Code
547    /// adds its own workspace-instruction and Skill-catalog providers.
548    /// Projected Knowledge must not silently compose with this ambient RAG
549    /// surface.
550    host_context_provider_names: std::collections::BTreeSet<String>,
551    workspace: PathBuf,
552    /// Unique session identifier.
553    session_id: String,
554    /// Internal conversation history, auto-updated after each `send()` and default-history `stream()`.
555    history: Arc<RwLock<Vec<Message>>>,
556    /// Fail-fast single-flight admission for transcript-affecting operations.
557    run_admission: Arc<run_admission::RunAdmission>,
558    /// Optional lane queue for priority-based tool execution.
559    command_queue: Option<Arc<crate::session_lane_queue::SessionLaneQueue>>,
560    /// Long-term memory handle.
561    ///
562    /// Built sessions resolve a default memory store. This remains optional for
563    /// compatibility with lower-level/manual construction paths.
564    memory: Option<Arc<crate::memory::AgentMemory>>,
565    /// Optional session store for persistence.
566    session_store: Option<Arc<dyn crate::store::SessionStore>>,
567    /// Host-owned destination for exact live tool-boundary checkpoints.
568    ///
569    /// This extension does not replace the Session store. Code captures one
570    /// canonical `SessionSnapshotV1` and its matching logical resume boundary,
571    /// then hands the immutable export to this sink. Host storage policy and
572    /// cross-process fencing remain outside Code. SDKs may install the sink
573    /// after construction through
574    /// [`AgentSession::set_session_checkpoint_export_sink`].
575    runtime_session_checkpoint_export_sink:
576        std::sync::Mutex<Option<Arc<dyn crate::session_checkpoint::SessionCheckpointExportSink>>>,
577    /// Runtime-owned fields used to build lossless persistence generations.
578    persistence_state: Arc<RwLock<session_persistence::SessionPersistenceState>>,
579    /// Auto-save after each completed `send()` or default-history `stream()`.
580    auto_save: bool,
581    /// Hook engine for lifecycle event interception.
582    hook_engine: Arc<crate::hooks::HookEngine>,
583    /// Optional external hook executor. When set, replaces `hook_engine` as the
584    /// executor passed to each `AgentLoop`.
585    hook_executor: Option<Arc<dyn crate::hooks::HookExecutor>>,
586    /// Deferred init warning: emitted as PersistenceFailed on first send() if set.
587    init_warning: Option<String>,
588    /// Slash command registry for `/command` dispatch.
589    /// Uses interior mutability so commands can be registered on a shared `Arc<AgentSession>`.
590    command_registry: std::sync::Mutex<CommandRegistry>,
591    /// Model identifier for display (e.g., "anthropic/claude-sonnet-4-20250514").
592    model_name: String,
593    /// Session-private MCP manager used by live add/remove operations.
594    mcp_manager: Arc<crate::mcp::manager::McpManager>,
595    /// Read-only MCP sources inherited from the parent agent and session options.
596    inherited_mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
597    /// Ordered MCP capability sources inherited by delegated child runs.
598    mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
599    /// Shared agent registry — populated at session creation; extended via register_agent_dir().
600    agent_registry: Arc<crate::subagent::AgentRegistry>,
601    /// Cancellation token for the current operation (send/stream).
602    /// Stored so that cancel() can abort ongoing LLM calls.
603    cancel_token: Arc<tokio::sync::Mutex<Option<tokio_util::sync::CancellationToken>>>,
604    /// ID of the run currently attached to the active cancellation token.
605    current_run_id: Arc<tokio::sync::Mutex<Option<String>>>,
606    /// Per-run cooperative control inbox. A session is single-flight, so one
607    /// slot is sufficient; child runs own their own session slot.
608    active_run_control: Arc<tokio::sync::Mutex<Option<Arc<crate::run_control::RunControlInbox>>>>,
609    /// In-memory run snapshots and event replay buffer for this session.
610    run_store: Arc<crate::run::InMemoryRunStore>,
611    /// Materialized view of delegated subagent task lifecycle, populated from runtime events.
612    subagent_tasks: Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>,
613    /// Currently executing tools observed from runtime events.
614    active_tools: Arc<tokio::sync::RwLock<HashMap<String, ActiveToolState>>>,
615    /// Compact execution traces for this session.
616    trace_sink: crate::trace::InMemoryTraceSink,
617    /// Structured completion evidence collected from agent and explicit verification runs.
618    verification_reports: Arc<RwLock<Vec<crate::verification::VerificationReport>>>,
619    /// Set once `close()` has been called. Subsequent send/stream calls
620    /// fast-fail with [`crate::error::CodeError::SessionClosed`].
621    closed: Arc<std::sync::atomic::AtomicBool>,
622    /// Session-level parent cancellation token.
623    ///
624    /// Every in-flight run (blocking send, stream, delegated subagent task)
625    /// derives its per-operation token from this one via `child_token()`,
626    /// so `session_cancel.cancel()` cascades to all of them. `close()` fires
627    /// this token first, after which any new `child_token()` returns an
628    /// already-cancelled token (defending against close/spawn races).
629    pub(crate) session_cancel: tokio_util::sync::CancellationToken,
630    /// Optional asynchronous semantic workspace index owned by this session.
631    workspace_retrieval: Option<Arc<crate::workspace::WorkspaceRetrievalRuntime>>,
632    /// Shared `Arc`-handle used by both [`AgentSession::close`] and the
633    /// parent [`Agent`]'s registry. The handle bundles every field needed
634    /// to perform the close sequence so the two entry points cannot drift.
635    close_handle: Arc<SessionCloseHandle>,
636    /// Runtime-mutable override for the budget guard. When set, takes
637    /// precedence over `config.budget_guard` on the next agent-loop
638    /// build. Lets SDK callers (Node especially) install a host-side
639    /// guard after `session()` has returned without ever putting a
640    /// JS callable into `SessionOptions`.
641    runtime_budget_guard: std::sync::Mutex<Option<Arc<dyn crate::budget::BudgetGuard>>>,
642    /// Promoting-host outcome ledger. When set, the next agent-loop build
643    /// uses it instead of the ledger copied at session construction.
644    runtime_outcome_ledger: std::sync::Mutex<Option<crate::outcome_memory::OutcomeLedger>>,
645    /// Runtime override for specialty [`crate::prompts::AgentStyle`]. `None`
646    /// means "no override" (use session-built `prompt_slots.style`). `Some(style)`
647    /// replaces the style for the next agent-loop build so hosts can hot-switch
648    /// Plan/GeneralPurpose without rebuilding the session.
649    runtime_agent_style: std::sync::Mutex<Option<Option<crate::prompts::AgentStyle>>>,
650    /// Runtime override for [`PlanningMode`]. Outer `None` means unset (use the
651    /// session-built `config.planning_mode`). `Some(mode)` replaces planning for
652    /// the next agent-loop build so hosts can disable forced planning on a
653    /// verifier turn without rebuilding the session.
654    runtime_planning_mode: std::sync::Mutex<Option<PlanningMode>>,
655    /// Runtime override for user-facing reply language. Outer `None` means unset
656    /// (use `prompt_slots.output_language`). `Some(None)` clears an earlier pin.
657    /// `Some(Some(tag))` pins replies for the next agent-loop build.
658    runtime_output_language: std::sync::Mutex<Option<Option<String>>>,
659    /// Multi-tenant label. Framework only carries the string; semantics
660    /// belong to the host.
661    pub(crate) tenant_id: Option<String>,
662    /// Principal that triggered the session (user / service / etc.).
663    pub(crate) principal: Option<String>,
664    /// Logical identifier of the agent template the session was
665    /// instantiated from.
666    pub(crate) agent_template_id: Option<String>,
667    /// Distributed-trace correlation id propagated to hooks / traces.
668    pub(crate) correlation_id: Option<String>,
669    /// Durable sticky reply / multi-scenario review findings for this session.
670    pub(crate) session_review: Arc<RwLock<crate::session_review::SessionReviewStoreV1>>,
671    /// Process-local review scenario registry (hosts may register more).
672    pub(crate) review_scenarios: Arc<crate::session_review::ReviewScenarioRegistry>,
673}
674
675// ============================================================================
676// Tests
677// ============================================================================
678
679#[cfg(test)]
680mod capability_runtime_tests;
681#[cfg(test)]
682mod replacement_tests;
683#[cfg(test)]
684mod retrieval_qa_tests;
685#[cfg(test)]
686mod retrieval_tests;
687#[cfg(test)]
688mod tests;