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