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