1#![recursion_limit = "256"]
2#![allow(clippy::result_large_err, clippy::cloned_ref_to_slice_refs)]
3pub use harn_clock as clock;
23
24pub mod a2a;
25pub mod actor_chain;
26pub mod agent_events;
27pub mod agent_sessions;
28pub mod agent_transcript_budget;
29pub mod atomic_io;
30pub mod autonomy;
31pub(crate) mod aws_sigv4;
32pub mod bridge;
33mod builtin_id;
34pub mod bytecode_cache;
35pub mod call_budget;
36pub mod channel_guardrails;
37pub mod channels;
38pub mod checkpoint;
39mod chunk;
40mod compiler;
41pub mod composition;
42pub mod config;
43pub mod connectors;
44pub mod corrections;
45pub mod coverage;
46pub(crate) mod durable_rate_limit;
47pub(crate) mod duration_parse;
48pub mod egress;
49pub mod event_log;
50pub mod events;
51pub mod external_agent;
52pub mod flow;
53pub mod harness;
54pub mod harness_auth;
55pub(crate) mod harness_crypto;
56pub mod harness_net;
57pub mod harness_system;
58pub mod harness_tenant;
59mod http;
60pub mod jsonrpc;
61pub(crate) mod limits;
62pub mod llm;
63pub mod llm_config;
64pub mod mcp;
65pub mod mcp_allowlist;
66pub mod mcp_auth;
67pub mod mcp_bulk_auth;
68pub mod mcp_card;
69pub mod mcp_elicit;
70pub mod mcp_file_upload;
71pub mod mcp_host;
72pub mod mcp_identity;
73pub mod mcp_json_discovery;
74pub mod mcp_oauth;
75pub mod mcp_presets;
76pub mod mcp_progress;
77pub mod mcp_protocol;
78pub mod mcp_registry;
79pub mod mcp_sampling;
80pub mod mcp_server;
81pub mod metadata;
82pub mod module_artifact;
83pub mod observability;
84pub mod op_interrupt;
85pub mod orchestration;
86pub mod personas;
87pub mod process_sandbox;
88pub mod profile;
89pub mod provenance;
90pub mod provider_catalog;
91pub mod receipts;
92pub mod record_filter;
93pub mod redact;
94pub mod run_events;
95pub mod runtime_context;
96pub(crate) mod runtime_guards;
97pub mod runtime_limits;
98pub mod runtime_paths;
99pub(crate) mod runtime_sqlite;
100pub mod schema;
101pub(crate) mod secret_patterns;
102pub mod secrets;
103pub mod security;
104pub mod session_bundle;
105pub mod session_timeline;
106pub mod sessions;
107pub(crate) mod shared_state;
108pub mod shells;
109pub mod skills;
110pub mod stdlib;
111pub mod stdlib_modules;
112pub mod step_runtime;
113pub mod store;
114pub(crate) mod synchronization;
115pub mod tenant;
116pub(crate) mod term;
117pub mod testbench;
118pub mod tool_annotations;
119pub mod tool_call_cancellations;
120pub mod tool_surface;
121pub mod tracing;
122pub mod triggers;
123pub mod trust_graph;
124pub(crate) mod url_encoding;
125pub mod user_dirs;
126
127pub mod clock_mock {
132 pub use crate::triggers::test_util::clock::{
133 active_mock_clock, advance, clear_overrides, install_override, instant_now, is_mocked,
134 now_ms, now_utc, sleep, ClockInstant, ClockOverrideGuard, MockClock,
135 };
136
137 pub mod leak_audit {
141 #[cfg(test)]
142 pub use crate::triggers::test_util::clock_leak::TEST_LOCK;
143 pub use crate::triggers::test_util::clock_leak::{
144 drain, instant_now, reset, snapshot, wall_now, ClockLeak,
145 };
146 }
147}
148
149pub mod typecheck;
150pub mod value;
151pub mod verification;
152pub mod visible_text;
153mod vm;
154pub(crate) mod wait_for_graph;
155pub mod waitpoints;
156pub mod workspace_anchor;
157pub mod workspace_path;
158
159pub use actor_chain::{
160 ActorChain, ActorChainEntry, ActorChainError, Principal, ScopeAttenuationMode,
161 ScopeAttenuationPolicy, ScopeAttenuationViolation,
162};
163pub use builtin_id::BuiltinId;
164pub use call_budget::{
165 charge_mcp_call, charge_pg_query, install_mcp_call_budget, install_pg_query_budget,
166 McpCallBudgetGuard, PgQueryBudgetGuard,
167};
168pub use checkpoint::register_checkpoint_builtins;
169pub use chunk::*;
170pub use compiler::*;
171pub use connectors::{
172 active_connector_client, active_metrics_registry, clear_active_connector_clients,
173 clear_active_metrics_registry, connector_export_denied_builtin_reason,
174 connector_export_effect_class,
175 cron::{CatchupMode, CronConnector},
176 default_connector_export_policy,
177 harn_module::{
178 load_contract as load_harn_connector_contract, HarnConnector, HarnConnectorContract,
179 },
180 hmac::{verify_hmac_signed, SIGNATURE_VERIFY_AUDIT_TOPIC},
181 install_active_connector_clients, install_active_metrics_registry,
182 postprocess_normalized_event, ActivationHandle, ClientError, Connector, ConnectorClient,
183 ConnectorCtx, ConnectorError, ConnectorExportEffectClass, ConnectorHttpResponse,
184 ConnectorMetricsSnapshot, ConnectorNormalizeResult, ConnectorRegistry, GenericWebhookConnector,
185 HarnConnectorEffectPolicies, MetricsRegistry, PostNormalizeOutcome, ProviderPayloadSchema,
186 RateLimitConfig, RateLimiterFactory, RawInbound, StreamConnector, TriggerBinding, TriggerKind,
187 TriggerRegistry, WebhookSignatureVariant,
188};
189pub use corrections::{
190 append_correction_record, apply_corrections_to_policy, correction_query_filters_from_json,
191 correction_record_from_json, policy_with_corrections, query_correction_records,
192 CorrectionQueryFilters, CorrectionRecord, CorrectionScope, CORRECTIONS_TOPIC,
193 CORRECTION_EVENT_KIND, CORRECTION_SCHEMA_V0,
194};
195pub use harness::{
196 DenyEvent, Harness, HarnessCall, HarnessClock, HarnessCrypto, HarnessEnv, HarnessFs,
197 HarnessKind, HarnessLlm, HarnessNet, HarnessObs, HarnessProcess, HarnessRandom, HarnessSecrets,
198 HarnessStdio, HarnessSystem, HarnessTenant, HarnessTerm, MockAwareClock, MockHarnessBuilder,
199 VmHarness,
200};
201pub use harness_auth::{
202 current_auth_principal, enter_auth_principal, AuthPrincipal, AuthPrincipalScopeGuard,
203 MISSING_PRINCIPAL_MESSAGE,
204};
205pub use harness_net::{
206 bypass_enabled as net_policy_bypass_enabled, NetMatcher, NetPolicy, NetPolicyAudit,
207 NetPolicyDecision, NetPolicyDefault, NetPolicyRule, OnViolation, HARN_NET_POLICY_BYPASS_ENV,
208 NET_POLICY_AUDIT_TOPIC,
209};
210pub use harness_tenant::{
211 current_tenant_id, enter_tenant, TenantScopeGuard, MISSING_TENANT_MESSAGE,
212};
213pub use http::{register_http_builtins, reset_http_state};
214pub use llm::register_llm_builtins;
215pub use llm::trigger_predicate::TriggerPredicateBudget;
216pub use llm::{
217 current_agent_session_id, install_llm_cost_budget, install_llm_token_budget,
218 peek_llm_cost_budget, peek_llm_token_budget, register_session_end_hook, set_llm_cost_budget,
219 set_llm_token_budget, LlmBudgetGuard, LlmTokenBudgetGuard,
220};
221pub use mcp::{connect_mcp_server_from_json, connect_mcp_server_from_spec, register_mcp_builtins};
222pub use mcp_allowlist::{
223 build_catalog as build_mcp_catalog, catalog_for_request as mcp_catalog_for_request,
224 AdvertisedItem as McpAdvertisedItem, CatalogRequest as McpCatalogRequest, McpAllowlist,
225 McpAllowlistItem, McpCatalog, McpCatalogItem, McpCatalogServer, McpItemKind,
226 MCP_ALLOWLIST_SCHEMA_VERSION,
227};
228pub use mcp_card::{fetch_server_card, load_server_card_from_path, CardError};
229pub use mcp_host::{
230 cache_stats as mcp_host_cache_stats, set_allowlist as set_mcp_host_allowlist,
231 AllowlistDecision as McpHostAllowlistDecision, AllowlistGuard as McpHostAllowlistGuard,
232 BreakerState as McpHostBreakerState, CacheStats as McpHostCacheStats, McpHostStatus,
233 SpawnOptions as McpHostSpawnOptions, SupervisionPolicy as McpHostSupervisionPolicy,
234};
235pub use mcp_registry::{
236 active_handle as mcp_active_handle, ensure_active as mcp_ensure_active,
237 get_registration as mcp_get_registration, install_active as mcp_install_active,
238 is_registered as mcp_is_registered, register_servers as mcp_register_servers,
239 release as mcp_release, reset as mcp_reset_registry, snapshot_status as mcp_snapshot_status,
240 sweep_expired as mcp_sweep_expired, RegisteredMcpServer, RegistryStatus,
241};
242pub use mcp_server::{
243 take_mcp_serve_metadata, take_mcp_serve_prompts, take_mcp_serve_registry,
244 take_mcp_serve_resource_templates, take_mcp_serve_resources, tool_registry_to_mcp_tools,
245 McpServer, McpServerMetadata,
246};
247pub use metadata::register_metadata_builtins;
248pub use observability::audit::{audit_events as audit_obs_events, AuditFinding, AuditFindingKind};
249pub use observability::request_id::{current_request_id, enter_request_id, RequestIdScopeGuard};
250pub use orchestration::{
251 benchmark_adapted_replay_pair, benchmark_replay_trace, build_replay_benchmark_report,
252 OpenCodeJsonlAdapter, ReplayBenchmarkCloudIngest, ReplayBenchmarkError,
253 ReplayBenchmarkFixtureReceipt, ReplayBenchmarkFixtureReport, ReplayBenchmarkMetrics,
254 ReplayBenchmarkReport, ReplayBenchmarkSuiteIdentity, ReplayBenchmarkSummary,
255 ReplayCategoryMetric, ReplayDebuggingProxyMetrics, ReplayRuntimeCostMetrics,
256 ReplayTraceAdapter, OPENCODE_JSONL_ADAPTER_ID, OPENCODE_JSONL_ADAPTER_SCHEMA_VERSION,
257 REPLAY_BENCHMARK_CLOUD_INGEST_KIND, REPLAY_BENCHMARK_REPORT_SCHEMA_VERSION,
258};
259pub use orchestration::{
260 canonicalize_run, first_divergence, run_replay_oracle_trace, ReplayAllowlistRule,
261 ReplayDivergence, ReplayExpectation, ReplayOracleError, ReplayOracleReport, ReplayOracleTrace,
262 ReplayTraceRun, ReplayTraceRunCounts, REPLAY_TRACE_SCHEMA_VERSION,
263};
264pub use orchestration::{
265 install_handoff_routes, snapshot_handoff_routes, HandoffRouteConfig,
266 HandoffRouteDecisionRecord, HandoffRouteTargetConfig,
267};
268pub use personas::{
269 disable_persona, fire_schedule as fire_persona_schedule, fire_trigger as fire_persona_trigger,
270 format_ms as format_persona_ms, now_ms as persona_now_ms, parse_rfc3339_ms as parse_persona_ms,
271 pause_persona, persona_status, record_persona_spend, register_persona_supervision_sink,
272 register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
273 resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
274 PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
275 PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
276 PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
277 PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
278 PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
279 PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
280 PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
281 PersonaValueSinkRegistration, StageDecl, StageExit, PERSONA_RUNTIME_TOPIC,
282};
283pub use provenance::{
284 build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
285 ReceiptBuildOptions, ReceiptVerificationReport,
286};
287pub use receipts::{
288 Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
289 RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
290};
291pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
292pub use runtime_limits::{
293 RuntimeLimitDescription, RuntimeLimitEntry, RuntimeLimits, RuntimeLimitsReport,
294 RUNTIME_LIMIT_DESCRIPTIONS,
295};
296pub use schema::json_to_vm_value;
297pub use sessions::{
298 CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
299 TouchSession, SESSIONS_TOPIC,
300};
301pub use stdlib::hitl::{
302 append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
303 HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
304};
305pub use stdlib::host::{clear_host_call_bridge, set_host_call_bridge, HostCallBridge};
306pub use stdlib::http_response::{
307 parse_envelope as parse_http_envelope, HttpEnvelope, HttpHeaderValue, WsUpgradeSpec,
308 HTTP_RESPONSE_TAG_KEY, HTTP_RESPONSE_TAG_VERSION,
309};
310#[cfg(feature = "postgres")]
311pub use stdlib::install_shared_pool_registry;
312pub use stdlib::io::{set_stdout_passthrough, take_stderr_buffer};
313pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
314pub use stdlib::observability::install_default_backend as install_obs_default_backend;
315pub use stdlib::secret_scan::{
316 append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
317 SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
318};
319pub use stdlib::template::{
320 lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
321 PromptSourceSpan, PromptSpanKind,
322};
323pub use stdlib::waitpoint::{
324 process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
325};
326pub use stdlib::workflow_messages::{
327 workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
328 workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
329 workflow_update_for_base, WorkflowMailboxState,
330};
331pub use stdlib::{
332 register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
333};
334pub use store::register_store_builtins;
335pub use tenant::{
336 tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
337 TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
338 TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
339 TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
340 TENANT_SECRET_NAMESPACE_PREFIX,
341};
342pub use triggers::{
343 append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
344 binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
345 clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
346 dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
347 install_manifest_triggers, install_orchestrator_budget, install_provider_catalog,
348 micros_to_usd, note_autonomous_decision, note_orchestrator_budget_cost,
349 orchestrator_budget_would_exceed, parse_flow_control_duration, pause, pin_trigger_binding,
350 provider_metadata, record_predicate_cost_sample, redact_headers, register_provider_schema,
351 registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
352 reset_provider_catalog, reset_provider_catalog_with, resolve_live_or_as_of,
353 resolve_live_trigger_binding, resolve_trigger_binding_as_of, resume,
354 run_trigger_harness_fixture, scheduler_in_flight_by_key, scheduler_ready_stats_by_key,
355 snapshot_dispatcher_stats, snapshot_orchestrator_budget, snapshot_trigger_bindings,
356 unpin_trigger_binding, usd_to_micros, worker_claims_topic_name, worker_job_topic_name,
357 worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest, DispatchError,
358 DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport, DispatcherStatsSnapshot,
359 FairnessKey, HeaderRedactionPolicy, InboxIndex, NotionPolledChangeEvent,
360 OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog, ProviderCatalogError,
361 ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload, ProviderRuntimeMetadata,
362 ProviderSchema, ProviderSecretRequirement, ReadyKeyStats, RecordedTriggerBinding, RetryPolicy,
363 SchedulableJob, SchedulerKeyStat, SchedulerPolicy, SchedulerSnapshot, SchedulerState,
364 SchedulerStrategy, SignatureStatus, SignatureVerificationMetadata, StreamEventPayload,
365 TenantId, TraceId, TriggerBatchConfig, TriggerBindingSnapshot, TriggerBindingSource,
366 TriggerBindingSpec, TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig,
367 TriggerDebounceConfig, TriggerDispatchOutcome, TriggerEvent, TriggerEventId,
368 TriggerExpressionSpec, TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult,
369 TriggerId, TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
370 TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
371 TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
372 WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
373 WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
374 DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
375 TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
376 TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
377 TRIGGER_INBOX_OBSERVABILITY_TOPIC, TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC,
378 TRIGGER_TEST_FIXTURES, WORKER_QUEUE_CATALOG_TOPIC,
379};
380pub use trust_graph::{
381 append_active_scope_attenuation_alert, append_active_trust_record,
382 append_scope_attenuation_alert, append_trust_record, export_trust_chain,
383 group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
384 query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
385 summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
386 TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
387 TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord,
388 TrustRecordActionKind, TrustScore, TrustTraceGroup, METADATA_KEY_ACTOR_CHAIN,
389 METADATA_KEY_ACTOR_CHAIN_ALERT, METADATA_KEY_EFFECTS_GRANT, METADATA_KEY_EFFECTS_USED,
390 METADATA_KEY_PARENT_RECORD_ID, OPENTRUSTGRAPH_ACCEPTED_SCHEMAS, OPENTRUSTGRAPH_CHAIN_SCHEMA_V0,
391 OPENTRUSTGRAPH_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0_1, TRUST_ACTION_RELEASE,
392 TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_TOPIC_PREFIX,
393 TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
394};
395pub use value::*;
396pub use vm::*;
397
398#[cfg(feature = "vm-bench-internals")]
399#[doc(hidden)]
400pub mod bench_internals;
401
402pub fn compile_source(source: &str) -> Result<Chunk, String> {
407 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
408 Compiler::new().compile(&program).map_err(|e| e.to_string())
409}
410
411pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
416 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
417 let has_pipeline = program.iter().any(|sn| {
418 let (_, inner) = harn_parser::peel_attributes(sn);
419 matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
420 });
421 if !has_pipeline {
422 return Err(format!("no pipeline named `{pipeline_name}` in source"));
423 }
424 Compiler::new()
425 .compile_named(&program, pipeline_name)
426 .map_err(|e| e.to_string())
427}
428
429pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
430 let schema = compiler::Compiler::type_expr_to_schema_value(type_expr)?;
431 let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
432 Some(llm::vm_value_to_json(&json_schema))
433}
434
435pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
436 let mut properties = serde_json::Map::new();
437 let mut required = Vec::new();
438
439 for param in params {
440 let param_schema = param
441 .type_expr
442 .as_ref()
443 .and_then(json_schema_for_type_expr)
444 .unwrap_or_else(|| serde_json::json!({}));
445 if param.default_value.is_none() {
446 required.push(serde_json::Value::String(param.name.clone()));
447 }
448 properties.insert(param.name.clone(), param_schema);
449 }
450
451 let mut schema = serde_json::Map::new();
452 schema.insert(
453 "type".to_string(),
454 serde_json::Value::String("object".to_string()),
455 );
456 schema.insert(
457 "properties".to_string(),
458 serde_json::Value::Object(properties),
459 );
460 if !required.is_empty() {
461 schema.insert("required".to_string(), serde_json::Value::Array(required));
462 }
463 serde_json::Value::Object(schema)
464}
465
466fn reset_llm_state_for_thread_reset() {
467 llm::reset_llm_state();
468 #[cfg(test)]
469 reset_thread_local_state_test_hooks::before_llm_global_reset();
470 llm::reset_rate_limit_registry();
473 llm_config::clear_user_overrides();
474}
475
476#[cfg(test)]
477mod reset_thread_local_state_test_hooks {
478 use std::sync::{Arc, Mutex, OnceLock};
479
480 type Hook = Arc<dyn Fn() + Send + Sync + 'static>;
481
482 static BEFORE_LLM_GLOBAL_RESET: OnceLock<Mutex<Option<Hook>>> = OnceLock::new();
483
484 fn before_llm_global_reset_hook() -> &'static Mutex<Option<Hook>> {
485 BEFORE_LLM_GLOBAL_RESET.get_or_init(|| Mutex::new(None))
486 }
487
488 pub(crate) struct HookGuard;
489
490 impl Drop for HookGuard {
491 fn drop(&mut self) {
492 let mut hook = before_llm_global_reset_hook()
493 .lock()
494 .unwrap_or_else(std::sync::PoisonError::into_inner);
495 *hook = None;
496 }
497 }
498
499 pub(crate) fn install_before_llm_global_reset(hook: Hook) -> HookGuard {
500 let mut slot = before_llm_global_reset_hook()
501 .lock()
502 .unwrap_or_else(std::sync::PoisonError::into_inner);
503 *slot = Some(hook);
504 HookGuard
505 }
506
507 pub(crate) fn before_llm_global_reset() {
508 let hook = before_llm_global_reset_hook()
509 .lock()
510 .unwrap_or_else(std::sync::PoisonError::into_inner)
511 .clone();
512 if let Some(hook) = hook {
513 hook();
514 }
515 }
516}
517
518pub fn reset_thread_local_state() {
520 #[cfg(test)]
521 {
522 let _guard = llm::env_guard();
527 reset_llm_state_for_thread_reset();
528 }
529 #[cfg(not(test))]
530 reset_llm_state_for_thread_reset();
531
532 http::reset_http_state();
533 channels::reset_channel_state();
534 event_log::reset_active_event_log();
535 egress::clear_explicit_egress_policy_requirement_for_host();
536 egress::clear_ssrf_guard_requirement_for_host();
537 stdlib::reset_stdlib_state();
538 connectors::clear_active_connector_clients();
539 orchestration::clear_runtime_hooks();
540 orchestration::clear_execution_policy_stacks();
541 orchestration::clear_command_policies();
542 orchestration::clear_pipeline_on_finish();
543 orchestration::reset_lifecycle_receipt_registry();
544 orchestration::agent_inbox::reset();
545 tool_call_cancellations::reset_registry();
546 redact::clear_policy_stack();
547 security::reset_thread_state();
548 triggers::clear_dispatcher_state();
549 triggers::clear_trigger_registry();
550 events::reset_event_sinks();
551 tracing::set_tracing_enabled(false);
552 tracing::reset_tracing();
553 agent_events::reset_all_sinks();
554 agent_sessions::reset_session_store();
555 mcp_registry::reset();
556 mcp_host::reset_for_tests();
557 call_budget::reset_call_budget_state();
558 clock_mock::leak_audit::reset();
559}
560
561#[cfg(test)]
562mod reset_leak_tests {
563 use super::*;
569 use crate::value::VmValue;
570
571 #[test]
572 fn reset_drains_agent_inbox() {
573 orchestration::agent_inbox::reset();
574 orchestration::agent_inbox::push("sess-2660", "note", "leak", "test");
575 assert!(orchestration::agent_inbox::session_count() > 0);
576 reset_thread_local_state();
577 assert_eq!(
578 orchestration::agent_inbox::session_count(),
579 0,
580 "agent_inbox must be empty after reset"
581 );
582 }
583
584 #[test]
585 fn reset_drains_tool_call_cancellation_registry() {
586 tool_call_cancellations::reset_registry();
587 let registered = tool_call_cancellations::register("sess-2660", "call-1", "tool");
590 if let Some((_handle, guard)) = registered {
591 std::mem::forget(guard);
592 }
593 assert!(tool_call_cancellations::registry_len() > 0);
594 reset_thread_local_state();
595 assert_eq!(
596 tool_call_cancellations::registry_len(),
597 0,
598 "tool-call cancellation registry must be empty after reset"
599 );
600 }
601
602 #[test]
603 fn reset_drains_routing_policy_registry() {
604 llm::routing::clear_policy_registry();
605 let mut config: crate::value::DictMap = crate::value::DictMap::new();
606 config.insert(
607 crate::value::intern_key("chain"),
608 VmValue::List(std::sync::Arc::new(vec![VmValue::String(
609 arcstr::ArcStr::from("mock:mock"),
610 )])),
611 );
612 llm::routing::build_routing_policy(&config).expect("intern a routing policy");
613 assert!(llm::routing::policy_registry_len() > 0);
614 reset_thread_local_state();
615 assert_eq!(
616 llm::routing::policy_registry_len(),
617 0,
618 "routing policy registry must be empty after reset"
619 );
620 }
621
622 #[test]
623 fn reset_holds_llm_env_guard_while_wiping_llm_globals() {
624 let observed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
625 let observed_hook = std::sync::Arc::clone(&observed);
626 let _hook = reset_thread_local_state_test_hooks::install_before_llm_global_reset(
627 std::sync::Arc::new(move || {
628 assert!(
629 matches!(
630 llm::env_lock().try_lock(),
631 Err(std::sync::TryLockError::WouldBlock)
632 ),
633 "reset_thread_local_state must hold env_guard before wiping LLM globals"
634 );
635 observed_hook.store(true, std::sync::atomic::Ordering::SeqCst);
636 }),
637 );
638
639 reset_thread_local_state();
640 assert!(
641 observed.load(std::sync::atomic::Ordering::SeqCst),
642 "LLM global reset hook should have run"
643 );
644 }
645}