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