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_prompts, take_mcp_serve_registry, take_mcp_serve_resource_templates,
244 take_mcp_serve_resources, tool_registry_to_mcp_tools, McpServer,
245};
246pub use metadata::register_metadata_builtins;
247pub use observability::audit::{audit_events as audit_obs_events, AuditFinding, AuditFindingKind};
248pub use observability::request_id::{current_request_id, enter_request_id, RequestIdScopeGuard};
249pub use orchestration::{
250 benchmark_adapted_replay_pair, benchmark_replay_trace, build_replay_benchmark_report,
251 OpenCodeJsonlAdapter, ReplayBenchmarkCloudIngest, ReplayBenchmarkError,
252 ReplayBenchmarkFixtureReceipt, ReplayBenchmarkFixtureReport, ReplayBenchmarkMetrics,
253 ReplayBenchmarkReport, ReplayBenchmarkSuiteIdentity, ReplayBenchmarkSummary,
254 ReplayCategoryMetric, ReplayDebuggingProxyMetrics, ReplayRuntimeCostMetrics,
255 ReplayTraceAdapter, OPENCODE_JSONL_ADAPTER_ID, OPENCODE_JSONL_ADAPTER_SCHEMA_VERSION,
256 REPLAY_BENCHMARK_CLOUD_INGEST_KIND, REPLAY_BENCHMARK_REPORT_SCHEMA_VERSION,
257};
258pub use orchestration::{
259 canonicalize_run, first_divergence, run_replay_oracle_trace, ReplayAllowlistRule,
260 ReplayDivergence, ReplayExpectation, ReplayOracleError, ReplayOracleReport, ReplayOracleTrace,
261 ReplayTraceRun, ReplayTraceRunCounts, REPLAY_TRACE_SCHEMA_VERSION,
262};
263pub use orchestration::{
264 install_handoff_routes, snapshot_handoff_routes, HandoffRouteConfig,
265 HandoffRouteDecisionRecord, HandoffRouteTargetConfig,
266};
267pub use personas::{
268 disable_persona, fire_schedule as fire_persona_schedule, fire_trigger as fire_persona_trigger,
269 format_ms as format_persona_ms, now_ms as persona_now_ms, parse_rfc3339_ms as parse_persona_ms,
270 pause_persona, persona_status, record_persona_spend, register_persona_supervision_sink,
271 register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
272 resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
273 PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
274 PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
275 PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
276 PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
277 PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
278 PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
279 PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
280 PersonaValueSinkRegistration, StageDecl, StageExit, PERSONA_RUNTIME_TOPIC,
281};
282pub use provenance::{
283 build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
284 ReceiptBuildOptions, ReceiptVerificationReport,
285};
286pub use receipts::{
287 Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
288 RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
289};
290pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
291pub use runtime_limits::{
292 RuntimeLimitDescription, RuntimeLimitEntry, RuntimeLimits, RuntimeLimitsReport,
293 RUNTIME_LIMIT_DESCRIPTIONS,
294};
295pub use schema::json_to_vm_value;
296pub use sessions::{
297 CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
298 TouchSession, SESSIONS_TOPIC,
299};
300pub use stdlib::hitl::{
301 append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
302 HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
303};
304pub use stdlib::host::{clear_host_call_bridge, set_host_call_bridge, HostCallBridge};
305pub use stdlib::http_response::{
306 parse_envelope as parse_http_envelope, HttpEnvelope, HttpHeaderValue, WsUpgradeSpec,
307 HTTP_RESPONSE_TAG_KEY, HTTP_RESPONSE_TAG_VERSION,
308};
309#[cfg(feature = "postgres")]
310pub use stdlib::install_shared_pool_registry;
311pub use stdlib::io::{set_stdout_passthrough, take_stderr_buffer};
312pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
313pub use stdlib::observability::install_default_backend as install_obs_default_backend;
314pub use stdlib::secret_scan::{
315 append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
316 SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
317};
318pub use stdlib::template::{
319 lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
320 PromptSourceSpan, PromptSpanKind,
321};
322pub use stdlib::waitpoint::{
323 process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
324};
325pub use stdlib::workflow_messages::{
326 workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
327 workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
328 workflow_update_for_base, WorkflowMailboxState,
329};
330pub use stdlib::{
331 register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
332};
333pub use store::register_store_builtins;
334pub use tenant::{
335 tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
336 TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
337 TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
338 TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
339 TENANT_SECRET_NAMESPACE_PREFIX,
340};
341pub use triggers::{
342 append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
343 binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
344 clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
345 dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
346 install_manifest_triggers, install_orchestrator_budget, install_provider_catalog,
347 micros_to_usd, note_autonomous_decision, note_orchestrator_budget_cost,
348 orchestrator_budget_would_exceed, parse_flow_control_duration, pause, pin_trigger_binding,
349 provider_metadata, record_predicate_cost_sample, redact_headers, register_provider_schema,
350 registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
351 reset_provider_catalog, reset_provider_catalog_with, resolve_live_or_as_of,
352 resolve_live_trigger_binding, resolve_trigger_binding_as_of, resume,
353 run_trigger_harness_fixture, scheduler_in_flight_by_key, scheduler_ready_stats_by_key,
354 snapshot_dispatcher_stats, snapshot_orchestrator_budget, snapshot_trigger_bindings,
355 unpin_trigger_binding, usd_to_micros, worker_claims_topic_name, worker_job_topic_name,
356 worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest, DispatchError,
357 DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport, DispatcherStatsSnapshot,
358 FairnessKey, HeaderRedactionPolicy, InboxIndex, NotionPolledChangeEvent,
359 OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog, ProviderCatalogError,
360 ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload, ProviderRuntimeMetadata,
361 ProviderSchema, ProviderSecretRequirement, ReadyKeyStats, RecordedTriggerBinding, RetryPolicy,
362 SchedulableJob, SchedulerKeyStat, SchedulerPolicy, SchedulerSnapshot, SchedulerState,
363 SchedulerStrategy, SignatureStatus, SignatureVerificationMetadata, StreamEventPayload,
364 TenantId, TraceId, TriggerBatchConfig, TriggerBindingSnapshot, TriggerBindingSource,
365 TriggerBindingSpec, TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig,
366 TriggerDebounceConfig, TriggerDispatchOutcome, TriggerEvent, TriggerEventId,
367 TriggerExpressionSpec, TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult,
368 TriggerId, TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
369 TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
370 TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
371 WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
372 WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
373 DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
374 TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
375 TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
376 TRIGGER_INBOX_OBSERVABILITY_TOPIC, TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC,
377 TRIGGER_TEST_FIXTURES, WORKER_QUEUE_CATALOG_TOPIC,
378};
379pub use trust_graph::{
380 append_active_scope_attenuation_alert, append_active_trust_record,
381 append_scope_attenuation_alert, append_trust_record, export_trust_chain,
382 group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
383 query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
384 summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
385 TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
386 TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord,
387 TrustRecordActionKind, TrustScore, TrustTraceGroup, METADATA_KEY_ACTOR_CHAIN,
388 METADATA_KEY_ACTOR_CHAIN_ALERT, METADATA_KEY_EFFECTS_GRANT, METADATA_KEY_EFFECTS_USED,
389 METADATA_KEY_PARENT_RECORD_ID, OPENTRUSTGRAPH_ACCEPTED_SCHEMAS, OPENTRUSTGRAPH_CHAIN_SCHEMA_V0,
390 OPENTRUSTGRAPH_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0_1, TRUST_ACTION_RELEASE,
391 TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_TOPIC_PREFIX,
392 TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
393};
394pub use value::*;
395pub use vm::*;
396
397#[cfg(feature = "vm-bench-internals")]
398#[doc(hidden)]
399pub mod bench_internals;
400
401pub fn compile_source(source: &str) -> Result<Chunk, String> {
406 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
407 Compiler::new().compile(&program).map_err(|e| e.to_string())
408}
409
410pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
415 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
416 let has_pipeline = program.iter().any(|sn| {
417 let (_, inner) = harn_parser::peel_attributes(sn);
418 matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
419 });
420 if !has_pipeline {
421 return Err(format!("no pipeline named `{pipeline_name}` in source"));
422 }
423 Compiler::new()
424 .compile_named(&program, pipeline_name)
425 .map_err(|e| e.to_string())
426}
427
428pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
429 let schema = compiler::Compiler::type_expr_to_schema_value(type_expr)?;
430 let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
431 Some(llm::vm_value_to_json(&json_schema))
432}
433
434pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
435 let mut properties = serde_json::Map::new();
436 let mut required = Vec::new();
437
438 for param in params {
439 let param_schema = param
440 .type_expr
441 .as_ref()
442 .and_then(json_schema_for_type_expr)
443 .unwrap_or_else(|| serde_json::json!({}));
444 if param.default_value.is_none() {
445 required.push(serde_json::Value::String(param.name.clone()));
446 }
447 properties.insert(param.name.clone(), param_schema);
448 }
449
450 let mut schema = serde_json::Map::new();
451 schema.insert(
452 "type".to_string(),
453 serde_json::Value::String("object".to_string()),
454 );
455 schema.insert(
456 "properties".to_string(),
457 serde_json::Value::Object(properties),
458 );
459 if !required.is_empty() {
460 schema.insert("required".to_string(), serde_json::Value::Array(required));
461 }
462 serde_json::Value::Object(schema)
463}
464
465fn reset_llm_state_for_thread_reset() {
466 llm::reset_llm_state();
467 #[cfg(test)]
468 reset_thread_local_state_test_hooks::before_llm_global_reset();
469 llm::reset_rate_limit_registry();
472 llm_config::clear_user_overrides();
473}
474
475#[cfg(test)]
476mod reset_thread_local_state_test_hooks {
477 use std::sync::{Arc, Mutex, OnceLock};
478
479 type Hook = Arc<dyn Fn() + Send + Sync + 'static>;
480
481 static BEFORE_LLM_GLOBAL_RESET: OnceLock<Mutex<Option<Hook>>> = OnceLock::new();
482
483 fn before_llm_global_reset_hook() -> &'static Mutex<Option<Hook>> {
484 BEFORE_LLM_GLOBAL_RESET.get_or_init(|| Mutex::new(None))
485 }
486
487 pub(crate) struct HookGuard;
488
489 impl Drop for HookGuard {
490 fn drop(&mut self) {
491 let mut hook = before_llm_global_reset_hook()
492 .lock()
493 .unwrap_or_else(std::sync::PoisonError::into_inner);
494 *hook = None;
495 }
496 }
497
498 pub(crate) fn install_before_llm_global_reset(hook: Hook) -> HookGuard {
499 let mut slot = before_llm_global_reset_hook()
500 .lock()
501 .unwrap_or_else(std::sync::PoisonError::into_inner);
502 *slot = Some(hook);
503 HookGuard
504 }
505
506 pub(crate) fn before_llm_global_reset() {
507 let hook = before_llm_global_reset_hook()
508 .lock()
509 .unwrap_or_else(std::sync::PoisonError::into_inner)
510 .clone();
511 if let Some(hook) = hook {
512 hook();
513 }
514 }
515}
516
517pub fn reset_thread_local_state() {
519 #[cfg(test)]
520 {
521 let _guard = llm::env_guard();
526 reset_llm_state_for_thread_reset();
527 }
528 #[cfg(not(test))]
529 reset_llm_state_for_thread_reset();
530
531 http::reset_http_state();
532 channels::reset_channel_state();
533 event_log::reset_active_event_log();
534 egress::clear_explicit_egress_policy_requirement_for_host();
535 egress::clear_ssrf_guard_requirement_for_host();
536 stdlib::reset_stdlib_state();
537 connectors::clear_active_connector_clients();
538 orchestration::clear_runtime_hooks();
539 orchestration::clear_execution_policy_stacks();
540 orchestration::clear_command_policies();
541 orchestration::clear_pipeline_on_finish();
542 orchestration::reset_lifecycle_receipt_registry();
543 orchestration::agent_inbox::reset();
544 tool_call_cancellations::reset_registry();
545 redact::clear_policy_stack();
546 security::reset_thread_state();
547 triggers::clear_dispatcher_state();
548 triggers::clear_trigger_registry();
549 events::reset_event_sinks();
550 tracing::set_tracing_enabled(false);
551 tracing::reset_tracing();
552 agent_events::reset_all_sinks();
553 agent_sessions::reset_session_store();
554 mcp_registry::reset();
555 mcp_host::reset_for_tests();
556 call_budget::reset_call_budget_state();
557 clock_mock::leak_audit::reset();
558}
559
560#[cfg(test)]
561mod reset_leak_tests {
562 use super::*;
568 use crate::value::VmValue;
569
570 #[test]
571 fn reset_drains_agent_inbox() {
572 orchestration::agent_inbox::reset();
573 orchestration::agent_inbox::push("sess-2660", "note", "leak", "test");
574 assert!(orchestration::agent_inbox::session_count() > 0);
575 reset_thread_local_state();
576 assert_eq!(
577 orchestration::agent_inbox::session_count(),
578 0,
579 "agent_inbox must be empty after reset"
580 );
581 }
582
583 #[test]
584 fn reset_drains_tool_call_cancellation_registry() {
585 tool_call_cancellations::reset_registry();
586 let registered = tool_call_cancellations::register("sess-2660", "call-1", "tool");
589 if let Some((_handle, guard)) = registered {
590 std::mem::forget(guard);
591 }
592 assert!(tool_call_cancellations::registry_len() > 0);
593 reset_thread_local_state();
594 assert_eq!(
595 tool_call_cancellations::registry_len(),
596 0,
597 "tool-call cancellation registry must be empty after reset"
598 );
599 }
600
601 #[test]
602 fn reset_drains_routing_policy_registry() {
603 llm::routing::clear_policy_registry();
604 let mut config: crate::value::DictMap = crate::value::DictMap::new();
605 config.insert(
606 crate::value::intern_key("chain"),
607 VmValue::List(std::sync::Arc::new(vec![VmValue::String(
608 arcstr::ArcStr::from("mock:mock"),
609 )])),
610 );
611 llm::routing::build_routing_policy(&config).expect("intern a routing policy");
612 assert!(llm::routing::policy_registry_len() > 0);
613 reset_thread_local_state();
614 assert_eq!(
615 llm::routing::policy_registry_len(),
616 0,
617 "routing policy registry must be empty after reset"
618 );
619 }
620
621 #[test]
622 fn reset_holds_llm_env_guard_while_wiping_llm_globals() {
623 let observed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
624 let observed_hook = std::sync::Arc::clone(&observed);
625 let _hook = reset_thread_local_state_test_hooks::install_before_llm_global_reset(
626 std::sync::Arc::new(move || {
627 assert!(
628 matches!(
629 llm::env_lock().try_lock(),
630 Err(std::sync::TryLockError::WouldBlock)
631 ),
632 "reset_thread_local_state must hold env_guard before wiping LLM globals"
633 );
634 observed_hook.store(true, std::sync::atomic::Ordering::SeqCst);
635 }),
636 );
637
638 reset_thread_local_state();
639 assert!(
640 observed.load(std::sync::atomic::Ordering::SeqCst),
641 "LLM global reset hook should have run"
642 );
643 }
644}