Skip to main content

harn_vm/
lib.rs

1#![recursion_limit = "256"]
2#![allow(clippy::result_large_err, clippy::cloned_ref_to_slice_refs)]
3
4/// Re-export of the unified clock substrate so downstream crates (CLI,
5/// orchestrator, harn-cloud) can depend on a single canonical `Clock`
6/// trait without each adding `harn-clock` as a direct dependency.
7pub use harn_clock as clock;
8
9pub mod a2a;
10pub mod agent_events;
11pub mod agent_sessions;
12pub mod atomic_io;
13pub mod autonomy;
14pub(crate) mod aws_sigv4;
15pub mod bridge;
16mod builtin_id;
17pub mod bytecode_cache;
18pub mod call_budget;
19pub mod channel_guardrails;
20pub mod channels;
21pub mod checkpoint;
22mod chunk;
23mod compiler;
24pub mod composition;
25pub mod config;
26pub mod connectors;
27pub mod corrections;
28pub(crate) mod durable_rate_limit;
29pub mod egress;
30pub mod event_log;
31pub mod events;
32pub mod external_agent;
33pub mod flow;
34pub mod harness;
35pub(crate) mod harness_crypto;
36pub mod harness_net;
37pub mod harness_system;
38pub mod harness_tenant;
39mod http;
40pub mod jsonrpc;
41pub(crate) mod limits;
42pub mod llm;
43pub mod llm_config;
44pub mod mcp;
45pub mod mcp_allowlist;
46pub mod mcp_auth;
47pub mod mcp_card;
48pub mod mcp_elicit;
49pub mod mcp_file_upload;
50pub mod mcp_host;
51pub mod mcp_oauth;
52pub mod mcp_presets;
53pub mod mcp_progress;
54pub mod mcp_protocol;
55pub mod mcp_registry;
56pub mod mcp_sampling;
57pub mod mcp_server;
58pub mod metadata;
59pub mod module_artifact;
60pub mod observability;
61pub mod orchestration;
62pub mod personas;
63pub mod process_sandbox;
64pub mod profile;
65pub mod provenance;
66pub mod provider_catalog;
67pub mod receipts;
68pub mod record_filter;
69pub mod redact;
70pub mod run_events;
71pub mod runtime_context;
72pub(crate) mod runtime_guards;
73pub mod runtime_limits;
74pub mod runtime_paths;
75pub mod schema;
76pub(crate) mod secret_patterns;
77pub mod secrets;
78pub mod security;
79pub mod session_bundle;
80pub mod session_timeline;
81pub mod sessions;
82pub(crate) mod shared_state;
83pub mod shells;
84pub mod skills;
85pub mod stdlib;
86pub mod stdlib_modules;
87pub mod step_runtime;
88pub mod store;
89pub(crate) mod synchronization;
90pub mod tenant;
91pub(crate) mod term;
92pub mod testbench;
93pub mod tool_annotations;
94pub mod tool_call_cancellations;
95pub mod tool_surface;
96pub mod tracing;
97pub mod triggers;
98pub mod trust_graph;
99pub(crate) mod url_encoding;
100pub mod user_dirs;
101
102/// Crate-wide deterministic clock mock used by stdlib time builtins, the
103/// trigger dispatcher, the cron scheduler, and Rust-side tests. Re-exports
104/// the long-lived implementation under `triggers::test_util::clock` so all
105/// callers go through one source of truth.
106pub mod clock_mock {
107    pub use crate::triggers::test_util::clock::{
108        active_mock_clock, advance, clear_overrides, install_override, instant_now, is_mocked,
109        now_ms, now_utc, sleep, ClockInstant, ClockOverrideGuard, MockClock,
110    };
111
112    /// Runtime audit for capabilities that observe real wall-clock or
113    /// monotonic time while a testbench mock is installed. See the module
114    /// docs for the full design.
115    pub mod leak_audit {
116        #[cfg(test)]
117        pub use crate::triggers::test_util::clock_leak::TEST_LOCK;
118        pub use crate::triggers::test_util::clock_leak::{
119            drain, instant_now, reset, snapshot, wall_now, ClockLeak,
120        };
121    }
122}
123
124pub mod typecheck;
125pub mod value;
126pub mod visible_text;
127mod vm;
128pub(crate) mod wait_for_graph;
129pub mod waitpoints;
130pub mod workspace_anchor;
131pub mod workspace_path;
132
133pub use builtin_id::BuiltinId;
134pub use call_budget::{
135    charge_mcp_call, charge_pg_query, install_mcp_call_budget, install_pg_query_budget,
136    McpCallBudgetGuard, PgQueryBudgetGuard,
137};
138pub use checkpoint::register_checkpoint_builtins;
139pub use chunk::*;
140pub use compiler::*;
141pub use connectors::{
142    active_connector_client, active_metrics_registry, clear_active_connector_clients,
143    clear_active_metrics_registry, connector_export_denied_builtin_reason,
144    connector_export_effect_class,
145    cron::{CatchupMode, CronConnector},
146    default_connector_export_policy,
147    harn_module::{
148        load_contract as load_harn_connector_contract, HarnConnector, HarnConnectorContract,
149    },
150    hmac::{verify_hmac_signed, SIGNATURE_VERIFY_AUDIT_TOPIC},
151    install_active_connector_clients, install_active_metrics_registry,
152    postprocess_normalized_event, ActivationHandle, ClientError, Connector, ConnectorClient,
153    ConnectorCtx, ConnectorError, ConnectorExportEffectClass, ConnectorHttpResponse,
154    ConnectorMetricsSnapshot, ConnectorNormalizeResult, ConnectorRegistry, GenericWebhookConnector,
155    HarnConnectorEffectPolicies, MetricsRegistry, PostNormalizeOutcome, ProviderPayloadSchema,
156    RateLimitConfig, RateLimiterFactory, RawInbound, StreamConnector, TriggerBinding, TriggerKind,
157    TriggerRegistry, WebhookSignatureVariant,
158};
159pub use corrections::{
160    append_correction_record, apply_corrections_to_policy, correction_query_filters_from_json,
161    correction_record_from_json, policy_with_corrections, query_correction_records,
162    CorrectionQueryFilters, CorrectionRecord, CorrectionScope, CORRECTIONS_TOPIC,
163    CORRECTION_EVENT_KIND, CORRECTION_SCHEMA_V0,
164};
165pub use harness::{
166    DenyEvent, Harness, HarnessCall, HarnessClock, HarnessCrypto, HarnessEnv, HarnessFs,
167    HarnessKind, HarnessLlm, HarnessNet, HarnessObs, HarnessProcess, HarnessRandom, HarnessStdio,
168    HarnessSystem, HarnessTenant, HarnessTerm, MockAwareClock, MockHarnessBuilder, VmHarness,
169};
170pub use harness_net::{
171    bypass_enabled as net_policy_bypass_enabled, NetMatcher, NetPolicy, NetPolicyAudit,
172    NetPolicyDecision, NetPolicyDefault, NetPolicyRule, OnViolation, HARN_NET_POLICY_BYPASS_ENV,
173    NET_POLICY_AUDIT_TOPIC,
174};
175pub use harness_tenant::{
176    current_tenant_id, enter_tenant, TenantScopeGuard, MISSING_TENANT_MESSAGE,
177};
178pub use http::{register_http_builtins, reset_http_state};
179pub use llm::register_llm_builtins;
180pub use llm::trigger_predicate::TriggerPredicateBudget;
181pub use llm::{
182    current_agent_session_id, install_llm_cost_budget, install_llm_token_budget,
183    peek_llm_cost_budget, peek_llm_token_budget, register_session_end_hook, set_llm_cost_budget,
184    set_llm_token_budget, LlmBudgetGuard, LlmTokenBudgetGuard,
185};
186pub use mcp::{connect_mcp_server_from_json, connect_mcp_server_from_spec, register_mcp_builtins};
187pub use mcp_allowlist::{
188    build_catalog as build_mcp_catalog, catalog_for_request as mcp_catalog_for_request,
189    AdvertisedItem as McpAdvertisedItem, CatalogRequest as McpCatalogRequest, McpAllowlist,
190    McpAllowlistItem, McpCatalog, McpCatalogItem, McpCatalogServer, McpItemKind,
191    MCP_ALLOWLIST_SCHEMA_VERSION,
192};
193pub use mcp_card::{fetch_server_card, load_server_card_from_path, CardError};
194pub use mcp_host::{
195    cache_stats as mcp_host_cache_stats, set_allowlist as set_mcp_host_allowlist,
196    AllowlistDecision as McpHostAllowlistDecision, AllowlistGuard as McpHostAllowlistGuard,
197    BreakerState as McpHostBreakerState, CacheStats as McpHostCacheStats, McpHostStatus,
198    SpawnOptions as McpHostSpawnOptions, SupervisionPolicy as McpHostSupervisionPolicy,
199};
200pub use mcp_registry::{
201    active_handle as mcp_active_handle, ensure_active as mcp_ensure_active,
202    get_registration as mcp_get_registration, install_active as mcp_install_active,
203    is_registered as mcp_is_registered, register_servers as mcp_register_servers,
204    release as mcp_release, reset as mcp_reset_registry, snapshot_status as mcp_snapshot_status,
205    sweep_expired as mcp_sweep_expired, RegisteredMcpServer, RegistryStatus,
206};
207pub use mcp_server::{
208    take_mcp_serve_prompts, take_mcp_serve_registry, take_mcp_serve_resource_templates,
209    take_mcp_serve_resources, tool_registry_to_mcp_tools, McpServer,
210};
211pub use metadata::register_metadata_builtins;
212pub use observability::audit::{audit_events as audit_obs_events, AuditFinding, AuditFindingKind};
213pub use observability::request_id::{current_request_id, enter_request_id, RequestIdScopeGuard};
214pub use orchestration::{
215    benchmark_adapted_replay_pair, benchmark_replay_trace, build_replay_benchmark_report,
216    OpenCodeJsonlAdapter, ReplayBenchmarkCloudIngest, ReplayBenchmarkError,
217    ReplayBenchmarkFixtureReceipt, ReplayBenchmarkFixtureReport, ReplayBenchmarkMetrics,
218    ReplayBenchmarkReport, ReplayBenchmarkSuiteIdentity, ReplayBenchmarkSummary,
219    ReplayCategoryMetric, ReplayDebuggingProxyMetrics, ReplayRuntimeCostMetrics,
220    ReplayTraceAdapter, OPENCODE_JSONL_ADAPTER_ID, OPENCODE_JSONL_ADAPTER_SCHEMA_VERSION,
221    REPLAY_BENCHMARK_CLOUD_INGEST_KIND, REPLAY_BENCHMARK_REPORT_SCHEMA_VERSION,
222};
223pub use orchestration::{
224    canonicalize_run, first_divergence, run_replay_oracle_trace, ReplayAllowlistRule,
225    ReplayDivergence, ReplayExpectation, ReplayOracleError, ReplayOracleReport, ReplayOracleTrace,
226    ReplayTraceRun, ReplayTraceRunCounts, REPLAY_TRACE_SCHEMA_VERSION,
227};
228pub use orchestration::{
229    install_handoff_routes, snapshot_handoff_routes, HandoffRouteConfig,
230    HandoffRouteDecisionRecord, HandoffRouteTargetConfig,
231};
232pub use personas::{
233    disable_persona, fire_schedule as fire_persona_schedule, fire_trigger as fire_persona_trigger,
234    format_ms as format_persona_ms, now_ms as persona_now_ms, parse_rfc3339_ms as parse_persona_ms,
235    pause_persona, persona_status, record_persona_spend, register_persona_supervision_sink,
236    register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
237    resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
238    PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
239    PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
240    PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
241    PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
242    PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
243    PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
244    PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
245    PersonaValueSinkRegistration, StageDecl, StageExit, PERSONA_RUNTIME_TOPIC,
246};
247pub use provenance::{
248    build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
249    ReceiptBuildOptions, ReceiptVerificationReport,
250};
251pub use receipts::{
252    Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
253    RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
254};
255pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
256pub use runtime_limits::{
257    RuntimeLimitDescription, RuntimeLimitEntry, RuntimeLimits, RuntimeLimitsReport,
258    RUNTIME_LIMIT_DESCRIPTIONS,
259};
260pub use schema::json_to_vm_value;
261pub use sessions::{
262    CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
263    TouchSession, SESSIONS_TOPIC,
264};
265pub use stdlib::hitl::{
266    append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
267    HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
268};
269pub use stdlib::host::{clear_host_call_bridge, set_host_call_bridge, HostCallBridge};
270pub use stdlib::http_response::{
271    parse_envelope as parse_http_envelope, HttpEnvelope, HttpHeaderValue, WsUpgradeSpec,
272    HTTP_RESPONSE_TAG_KEY, HTTP_RESPONSE_TAG_VERSION,
273};
274pub use stdlib::io::{set_stdout_passthrough, take_stderr_buffer};
275pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
276pub use stdlib::observability::install_default_backend as install_obs_default_backend;
277pub use stdlib::secret_scan::{
278    append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
279    SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
280};
281pub use stdlib::template::{
282    lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
283    PromptSourceSpan, PromptSpanKind,
284};
285pub use stdlib::waitpoint::{
286    process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
287};
288pub use stdlib::workflow_messages::{
289    workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
290    workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
291    workflow_update_for_base, WorkflowMailboxState,
292};
293pub use stdlib::{
294    register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
295};
296pub use store::register_store_builtins;
297pub use tenant::{
298    tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
299    TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
300    TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
301    TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
302    TENANT_SECRET_NAMESPACE_PREFIX,
303};
304pub use triggers::{
305    append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
306    binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
307    clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
308    dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
309    install_manifest_triggers, install_orchestrator_budget, install_provider_catalog,
310    micros_to_usd, note_autonomous_decision, note_orchestrator_budget_cost,
311    orchestrator_budget_would_exceed, parse_flow_control_duration, pause, pin_trigger_binding,
312    provider_metadata, record_predicate_cost_sample, redact_headers, register_provider_schema,
313    registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
314    reset_provider_catalog, reset_provider_catalog_with, resolve_live_or_as_of,
315    resolve_live_trigger_binding, resolve_trigger_binding_as_of, resume,
316    run_trigger_harness_fixture, scheduler_in_flight_by_key, scheduler_ready_stats_by_key,
317    snapshot_dispatcher_stats, snapshot_orchestrator_budget, snapshot_trigger_bindings,
318    unpin_trigger_binding, usd_to_micros, worker_claims_topic_name, worker_job_topic_name,
319    worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest, DispatchError,
320    DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport, DispatcherStatsSnapshot,
321    FairnessKey, HeaderRedactionPolicy, InboxIndex, NotionPolledChangeEvent,
322    OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog, ProviderCatalogError,
323    ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload, ProviderRuntimeMetadata,
324    ProviderSchema, ProviderSecretRequirement, ReadyKeyStats, RecordedTriggerBinding, RetryPolicy,
325    SchedulableJob, SchedulerKeyStat, SchedulerPolicy, SchedulerSnapshot, SchedulerState,
326    SchedulerStrategy, SignatureStatus, SignatureVerificationMetadata, StreamEventPayload,
327    TenantId, TraceId, TriggerBatchConfig, TriggerBindingSnapshot, TriggerBindingSource,
328    TriggerBindingSpec, TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig,
329    TriggerDebounceConfig, TriggerDispatchOutcome, TriggerEvent, TriggerEventId,
330    TriggerExpressionSpec, TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult,
331    TriggerId, TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
332    TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
333    TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
334    WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
335    WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
336    DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
337    TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
338    TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
339    TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC, TRIGGER_TEST_FIXTURES,
340    WORKER_QUEUE_CATALOG_TOPIC,
341};
342pub use trust_graph::{
343    append_active_trust_record, append_trust_record, export_trust_chain,
344    group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
345    query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
346    summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
347    TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
348    TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord,
349    TrustRecordActionKind, TrustScore, TrustTraceGroup, METADATA_KEY_EFFECTS_GRANT,
350    METADATA_KEY_EFFECTS_USED, METADATA_KEY_PARENT_RECORD_ID, OPENTRUSTGRAPH_ACCEPTED_SCHEMAS,
351    OPENTRUSTGRAPH_CHAIN_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0_1,
352    TRUST_ACTION_RELEASE, TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC,
353    TRUST_GRAPH_LEGACY_TOPIC_PREFIX, TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
354};
355pub use value::*;
356pub use vm::*;
357
358#[cfg(feature = "vm-bench-internals")]
359#[doc(hidden)]
360pub mod bench_internals;
361
362/// Lex, parse, type-check, and compile source to bytecode in one call.
363/// Bails on the first type error. For callers that need diagnostics
364/// rather than early exit, use `harn_parser::check_source` directly
365/// and then call `Compiler::new().compile(&program)`.
366pub fn compile_source(source: &str) -> Result<Chunk, String> {
367    let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
368    Compiler::new().compile(&program).map_err(|e| e.to_string())
369}
370
371/// Same as [`compile_source`] but compiles a specific named pipeline as
372/// the program entry point instead of the default-pipeline-or-first
373/// selection rule. Returns a runtime error when no pipeline with
374/// `pipeline_name` exists in the source.
375pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
376    let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
377    let has_pipeline = program.iter().any(|sn| {
378        let (_, inner) = harn_parser::peel_attributes(sn);
379        matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
380    });
381    if !has_pipeline {
382        return Err(format!("no pipeline named `{pipeline_name}` in source"));
383    }
384    Compiler::new()
385        .compile_named(&program, pipeline_name)
386        .map_err(|e| e.to_string())
387}
388
389pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
390    let schema = compiler::Compiler::type_expr_to_schema_value(type_expr)?;
391    let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
392    Some(llm::vm_value_to_json(&json_schema))
393}
394
395pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
396    let mut properties = serde_json::Map::new();
397    let mut required = Vec::new();
398
399    for param in params {
400        let param_schema = param
401            .type_expr
402            .as_ref()
403            .and_then(json_schema_for_type_expr)
404            .unwrap_or_else(|| serde_json::json!({}));
405        if param.default_value.is_none() {
406            required.push(serde_json::Value::String(param.name.clone()));
407        }
408        properties.insert(param.name.clone(), param_schema);
409    }
410
411    let mut schema = serde_json::Map::new();
412    schema.insert(
413        "type".to_string(),
414        serde_json::Value::String("object".to_string()),
415    );
416    schema.insert(
417        "properties".to_string(),
418        serde_json::Value::Object(properties),
419    );
420    if !required.is_empty() {
421        schema.insert("required".to_string(), serde_json::Value::Array(required));
422    }
423    serde_json::Value::Object(schema)
424}
425
426/// Reset all thread-local state that can leak between test runs.
427pub fn reset_thread_local_state() {
428    llm::reset_llm_state();
429    llm_config::clear_user_overrides();
430    http::reset_http_state();
431    channels::reset_channel_state();
432    event_log::reset_active_event_log();
433    egress::clear_explicit_egress_policy_requirement_for_host();
434    stdlib::reset_stdlib_state();
435    connectors::clear_active_connector_clients();
436    orchestration::clear_runtime_hooks();
437    orchestration::clear_execution_policy_stacks();
438    orchestration::clear_command_policies();
439    orchestration::clear_pipeline_on_finish();
440    orchestration::reset_lifecycle_receipt_registry();
441    orchestration::agent_inbox::reset();
442    tool_call_cancellations::reset_registry();
443    redact::clear_policy_stack();
444    security::reset_thread_state();
445    triggers::clear_dispatcher_state();
446    triggers::clear_trigger_registry();
447    events::reset_event_sinks();
448    tracing::set_tracing_enabled(false);
449    tracing::reset_tracing();
450    agent_events::reset_all_sinks();
451    agent_sessions::reset_session_store();
452    mcp_registry::reset();
453    mcp_host::reset_for_tests();
454    call_budget::reset_call_budget_state();
455    clock_mock::leak_audit::reset();
456}
457
458#[cfg(test)]
459mod reset_leak_tests {
460    //! Regression coverage for harn#2660: process-/thread-global
461    //! registries that accumulated one entry per test because they were
462    //! never drained by `reset_thread_local_state`. Each case populates a
463    //! registry through its real entry point, runs the reset, and asserts
464    //! the registry is empty again.
465    use super::*;
466    use crate::value::VmValue;
467    use std::collections::BTreeMap;
468
469    #[test]
470    fn reset_drains_agent_inbox() {
471        orchestration::agent_inbox::reset();
472        orchestration::agent_inbox::push("sess-2660", "note", "leak", "test");
473        assert!(orchestration::agent_inbox::session_count() > 0);
474        reset_thread_local_state();
475        assert_eq!(
476            orchestration::agent_inbox::session_count(),
477            0,
478            "agent_inbox must be empty after reset"
479        );
480    }
481
482    #[test]
483    fn reset_drains_tool_call_cancellation_registry() {
484        tool_call_cancellations::reset_registry();
485        // Leak the guard so the entry survives until the reset runs —
486        // this mirrors a dispatch abandoned mid-flight.
487        let registered = tool_call_cancellations::register("sess-2660", "call-1", "tool");
488        if let Some((_handle, guard)) = registered {
489            std::mem::forget(guard);
490        }
491        assert!(tool_call_cancellations::registry_len() > 0);
492        reset_thread_local_state();
493        assert_eq!(
494            tool_call_cancellations::registry_len(),
495            0,
496            "tool-call cancellation registry must be empty after reset"
497        );
498    }
499
500    #[test]
501    fn reset_drains_routing_policy_registry() {
502        llm::routing::clear_policy_registry();
503        let mut config: BTreeMap<String, VmValue> = BTreeMap::new();
504        config.insert(
505            "chain".to_string(),
506            VmValue::List(std::sync::Arc::new(vec![VmValue::String(
507                std::sync::Arc::from("mock:mock"),
508            )])),
509        );
510        llm::routing::build_routing_policy(&config).expect("intern a routing policy");
511        assert!(llm::routing::policy_registry_len() > 0);
512        reset_thread_local_state();
513        assert_eq!(
514            llm::routing::policy_registry_len(),
515            0,
516            "routing policy registry must be empty after reset"
517        );
518    }
519}