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