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(crate) mod agent_session_journal;
28pub mod agent_sessions;
29pub mod agent_transcript_budget;
30pub mod atomic_io;
31pub mod autonomy;
32pub(crate) mod aws_sigv4;
33pub mod boundary;
34pub mod bridge;
35mod builtin_id;
36pub mod builtin_profile;
37pub mod bytecode_cache;
38pub mod call_budget;
39pub mod canonical_json;
40pub mod channel_guardrails;
41pub mod channels;
42pub mod checkpoint;
43mod chunk;
44mod compiler;
45pub mod composition;
46pub mod conditional_replace;
47pub mod config;
48pub mod connectors;
49pub mod context_manifest;
50pub mod corrections;
51pub mod coverage;
52pub(crate) mod durable_rate_limit;
53pub mod duration_parse;
54pub mod egress;
55pub mod environment_registry;
56pub mod event_log;
57pub mod events;
58pub mod external_agent;
59pub mod flow;
60pub mod harness;
61pub mod harness_auth;
62pub(crate) mod harness_crypto;
63pub mod harness_net;
64pub mod harness_system;
65pub mod harness_tenant;
66pub mod host_attachments;
67mod http;
68pub mod jsonrpc;
69pub(crate) mod limits;
70pub mod llm;
71pub mod llm_config;
72pub mod mcp;
73pub mod mcp_allowlist;
74pub mod mcp_auth;
75pub mod mcp_bulk_auth;
76pub mod mcp_card;
77pub mod mcp_client_request;
78pub mod mcp_client_roots;
79pub mod mcp_elicit;
80pub mod mcp_file_upload;
81pub mod mcp_host;
82pub mod mcp_identity;
83pub mod mcp_json_discovery;
84pub mod mcp_oauth;
85pub mod mcp_presets;
86pub mod mcp_progress;
87pub mod mcp_protocol;
88pub mod mcp_registry;
89pub mod mcp_sampling;
90pub mod mcp_server;
91pub mod metadata;
92pub mod module_artifact;
93pub mod module_source;
94pub mod observability;
95pub mod op_interrupt;
96pub mod orchestration;
97mod persistent_state;
98pub mod personas;
99mod prepared_module;
100pub mod process_sandbox;
101pub mod profile;
102pub mod provenance;
103pub mod provider_catalog;
104pub mod receipts;
105pub mod record_filter;
106pub mod redact;
107pub mod run_events;
108pub mod runtime_context;
109pub(crate) mod runtime_guards;
110pub mod runtime_limits;
111pub mod runtime_paths;
112pub(crate) mod runtime_sqlite;
113pub mod schema;
114pub(crate) mod secret_patterns;
115pub mod secrets;
116pub mod security;
117pub mod session_bundle;
118pub mod session_timeline;
119pub mod sessions;
120pub(crate) mod shared_state;
121pub mod shells;
122pub mod skills;
123pub mod stdlib;
124pub mod stdlib_modules;
125pub mod step_runtime;
126pub mod store;
127pub(crate) mod synchronization;
128pub mod tenant;
129pub(crate) mod term;
130pub(crate) mod test_env;
131pub mod testbench;
132pub mod text;
133pub mod text_diff;
134pub mod tool_annotations;
135pub mod tool_call_cancellations;
136pub mod tool_surface;
137pub mod tracing;
138pub mod triggers;
139pub mod trust_graph;
140pub(crate) mod url_encoding;
141pub mod user_dirs;
142
143pub fn initialize_runtime_assets() {
151 secret_patterns::initialize_default_secret_patterns();
152}
153
154pub fn initialize_runtime() -> Result<(), environment_registry::EnvironmentValidationError> {
157 environment_registry::validate_startup_environment()?;
158 initialize_runtime_assets();
159 Ok(())
160}
161
162pub mod clock_mock {
167 pub use crate::triggers::test_util::clock::{
168 active_mock_clock, advance, clear_overrides, install_override, instant_now, is_mocked,
169 now_ms, now_utc, sleep, ClockInstant, ClockOverrideGuard, MockClock,
170 };
171
172 pub mod leak_audit {
176 pub use crate::triggers::test_util::clock_leak::{
177 drain, enter_scope, install_scope, instant_now, reset, snapshot, wall_now, ClockLeak,
178 ClockLeakScope, ClockLeakScopeGuard,
179 };
180 }
181}
182
183pub(crate) mod text_index;
184pub mod typecheck;
185pub mod value;
186pub mod verification;
187pub mod visible_text;
188mod vm;
189pub(crate) mod wait_for_graph;
190pub mod waitpoints;
191pub mod windows_path;
192pub mod workspace_anchor;
193pub mod workspace_path;
194
195pub use persistent_state::{register_persistent_state_builtins_at_root, PersistentStateRoot};
196pub use prepared_module::{PreparedModuleCache, PreparedModuleCacheStats};
197
198pub use actor_chain::{
199 ActorChain, ActorChainEntry, ActorChainError, Principal, ScopeAttenuationMode,
200 ScopeAttenuationPolicy, ScopeAttenuationViolation,
201};
202pub use builtin_id::BuiltinId;
203pub use call_budget::{
204 charge_mcp_call, charge_pg_query, install_mcp_call_budget, install_pg_query_budget,
205 McpCallBudgetGuard, PgQueryBudgetGuard,
206};
207pub use checkpoint::register_checkpoint_builtins;
208pub use chunk::*;
209pub use compiler::*;
210pub use connectors::{
211 active_connector_client, active_metrics_registry, clear_active_connector_clients,
212 clear_active_metrics_registry, connector_export_denied_builtin_reason,
213 connector_export_effect_class,
214 cron::{CatchupMode, CronConnector},
215 default_connector_export_policy,
216 harn_module::{
217 load_contract as load_harn_connector_contract, HarnConnector, HarnConnectorContract,
218 },
219 hmac::{verify_hmac_signed, SIGNATURE_VERIFY_AUDIT_TOPIC},
220 install_active_connector_clients, install_active_metrics_registry,
221 postprocess_normalized_event, ActivationHandle, ClientError, Connector, ConnectorClient,
222 ConnectorCtx, ConnectorError, ConnectorExportEffectClass, ConnectorHttpResponse,
223 ConnectorMetricsSnapshot, ConnectorNormalizeResult, ConnectorRegistry, GenericWebhookConnector,
224 HarnConnectorEffectPolicies, MetricsRegistry, PostNormalizeOutcome, ProviderPayloadSchema,
225 RateLimitConfig, RateLimiterFactory, RawInbound, StreamConnector, TriggerBinding, TriggerKind,
226 TriggerRegistry, WebhookSignatureVariant,
227};
228pub use corrections::{
229 append_correction_record, apply_corrections_to_policy, correction_query_filters_from_json,
230 correction_record_from_json, policy_with_corrections, query_correction_records,
231 CorrectionQueryFilters, CorrectionRecord, CorrectionScope, CORRECTIONS_TOPIC,
232 CORRECTION_EVENT_KIND, CORRECTION_SCHEMA_V0,
233};
234pub use harness::{
235 DenyEvent, Harness, HarnessCall, HarnessClock, HarnessCrypto, HarnessEnv, HarnessFs,
236 HarnessKind, HarnessLlm, HarnessNet, HarnessObs, HarnessProcess, HarnessRandom, HarnessSecrets,
237 HarnessStdio, HarnessSystem, HarnessTenant, HarnessTerm, MockAwareClock, MockHarnessBuilder,
238 VmHarness,
239};
240pub use harness_auth::{
241 current_auth_principal, enter_auth_principal, AuthPrincipal, AuthPrincipalScopeGuard,
242 MISSING_PRINCIPAL_MESSAGE,
243};
244pub use harness_net::{
245 bypass_enabled as net_policy_bypass_enabled, NetMatcher, NetPolicy, NetPolicyAudit,
246 NetPolicyDecision, NetPolicyDefault, NetPolicyRule, OnViolation, HARN_NET_POLICY_BYPASS_ENV,
247 NET_POLICY_AUDIT_TOPIC,
248};
249pub use harness_tenant::{
250 current_tenant_id, enter_tenant, TenantScopeGuard, MISSING_TENANT_MESSAGE,
251};
252pub use http::{register_http_builtins, reset_http_state};
253pub use llm::register_llm_builtins;
254pub use llm::trigger_predicate::TriggerPredicateBudget;
255pub use llm::{
256 current_agent_session_id, install_llm_cost_budget, install_llm_token_budget,
257 peek_llm_cost_budget, peek_llm_token_budget, register_session_end_hook, set_llm_cost_budget,
258 set_llm_token_budget, LlmBudgetGuard, LlmTokenBudgetGuard, SessionEndHookRegistration,
259};
260pub use mcp::{connect_mcp_server_from_json, connect_mcp_server_from_spec, register_mcp_builtins};
261pub use mcp_allowlist::{
262 build_catalog as build_mcp_catalog, catalog_for_request as mcp_catalog_for_request,
263 AdvertisedItem as McpAdvertisedItem, CatalogRequest as McpCatalogRequest, McpAllowlist,
264 McpAllowlistItem, McpCatalog, McpCatalogItem, McpCatalogServer, McpItemKind,
265 MCP_ALLOWLIST_SCHEMA_VERSION,
266};
267pub use mcp_card::{fetch_server_card, load_server_card_from_path, CardError};
268pub use mcp_host::{
269 cache_stats as mcp_host_cache_stats, set_allowlist as set_mcp_host_allowlist,
270 AllowlistDecision as McpHostAllowlistDecision, AllowlistGuard as McpHostAllowlistGuard,
271 BreakerState as McpHostBreakerState, CacheStats as McpHostCacheStats, McpHostStatus,
272 SpawnOptions as McpHostSpawnOptions, SupervisionPolicy as McpHostSupervisionPolicy,
273};
274pub use mcp_registry::{
275 active_handle as mcp_active_handle, ensure_active as mcp_ensure_active,
276 get_registration as mcp_get_registration, install_active as mcp_install_active,
277 is_registered as mcp_is_registered, register_servers as mcp_register_servers,
278 release as mcp_release, reset as mcp_reset_registry, snapshot_status as mcp_snapshot_status,
279 sweep_expired as mcp_sweep_expired, RegisteredMcpServer, RegistryStatus,
280};
281pub use mcp_server::{
282 take_mcp_serve_metadata, take_mcp_serve_prompts, take_mcp_serve_registry,
283 take_mcp_serve_resource_templates, take_mcp_serve_resources, tool_registry_to_mcp_tools,
284 McpServer, McpServerMetadata,
285};
286pub use metadata::register_metadata_builtins;
287pub use observability::audit::{audit_events as audit_obs_events, AuditFinding, AuditFindingKind};
288pub use observability::execution_scope::{
289 current_execution_scope, enter_execution_scope, mint_execution_scope, ExecutionScopeGuard,
290};
291pub use observability::request_id::{current_request_id, enter_request_id, RequestIdScopeGuard};
292pub use orchestration::{
293 benchmark_adapted_replay_pair, benchmark_replay_trace, build_replay_benchmark_report,
294 OpenCodeJsonlAdapter, ReplayBenchmarkCloudIngest, ReplayBenchmarkError,
295 ReplayBenchmarkFixtureReceipt, ReplayBenchmarkFixtureReport, ReplayBenchmarkMetrics,
296 ReplayBenchmarkReport, ReplayBenchmarkSuiteIdentity, ReplayBenchmarkSummary,
297 ReplayCategoryMetric, ReplayDebuggingProxyMetrics, ReplayRuntimeCostMetrics,
298 ReplayTraceAdapter, OPENCODE_JSONL_ADAPTER_ID, OPENCODE_JSONL_ADAPTER_SCHEMA_VERSION,
299 REPLAY_BENCHMARK_CLOUD_INGEST_KIND, REPLAY_BENCHMARK_REPORT_SCHEMA_VERSION,
300};
301pub use orchestration::{
302 canonicalize_run, first_divergence, run_replay_oracle_trace, ReplayAllowlistRule,
303 ReplayDivergence, ReplayExpectation, ReplayOracleError, ReplayOracleReport, ReplayOracleTrace,
304 ReplayTraceRun, ReplayTraceRunCounts, REPLAY_TRACE_SCHEMA_VERSION,
305};
306pub use orchestration::{
307 install_handoff_routes, snapshot_handoff_routes, HandoffRouteConfig,
308 HandoffRouteDecisionRecord, HandoffRouteTargetConfig,
309};
310pub use personas::{
311 disable_persona, fire_schedule as fire_persona_schedule, fire_trigger as fire_persona_trigger,
312 format_ms as format_persona_ms, now_ms as persona_now_ms, parse_rfc3339_ms as parse_persona_ms,
313 pause_persona, persona_status, record_persona_spend, register_persona_supervision_sink,
314 register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
315 resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
316 PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
317 PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
318 PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
319 PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
320 PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
321 PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
322 PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
323 PersonaValueSinkRegistration, StageDecl, StageExit, PERSONA_RUNTIME_TOPIC,
324};
325pub use provenance::{
326 build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
327 ReceiptBuildOptions, ReceiptVerificationReport,
328};
329pub use receipts::{
330 Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
331 RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
332};
333pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
334pub use runtime_limits::{
335 RuntimeLimitDescription, RuntimeLimitEntry, RuntimeLimits, RuntimeLimitsReport,
336 RUNTIME_LIMIT_DESCRIPTIONS,
337};
338pub use schema::json_to_vm_value;
339pub use sessions::{
340 CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
341 TouchSession, SESSIONS_TOPIC,
342};
343pub use stdlib::fs::ignore_policy;
349pub use stdlib::hitl::{
350 append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
351 HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
352};
353pub use stdlib::host::turn_cache as host_turn_cache;
355pub use stdlib::host::{
356 clear_host_call_bridge, dispatch_host_operation, host_call_ready, set_host_call_bridge,
357 HostCallBridge, HostCallDispatchFuture,
358};
359pub use stdlib::http_response::{
360 parse_envelope as parse_http_envelope, HttpEnvelope, HttpHeaderValue, WsUpgradeSpec,
361 HTTP_RESPONSE_TAG_KEY, HTTP_RESPONSE_TAG_VERSION,
362};
363#[cfg(feature = "postgres")]
364pub use stdlib::install_shared_pool_registry;
365pub use stdlib::io::{
366 reserve_stdio_for_current_thread, set_stdout_passthrough, take_stderr_buffer,
367 StdioReservationGuard,
368};
369pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
370pub use stdlib::observability::install_default_backend as install_obs_default_backend;
371pub use stdlib::secret_scan::{
372 append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
373 SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
374};
375pub use stdlib::template::{
376 lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
377 PromptSourceSpan, PromptSpanKind,
378};
379pub use stdlib::waitpoint::{
380 process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
381};
382pub use stdlib::workflow_messages::{
383 workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
384 workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
385 workflow_update_for_base, WorkflowMailboxState,
386};
387pub use stdlib::{
388 register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
389};
390pub use store::register_store_builtins;
391pub use tenant::{
392 tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
393 TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
394 TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
395 TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
396 TENANT_SECRET_NAMESPACE_PREFIX,
397};
398pub use triggers::{
399 append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
400 binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
401 clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
402 dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
403 install_manifest_triggers, install_orchestrator_budget, micros_to_usd,
404 note_autonomous_decision, note_orchestrator_budget_cost, orchestrator_budget_would_exceed,
405 parse_flow_control_duration, pause, pin_trigger_binding, provider_metadata,
406 record_predicate_cost_sample, redact_headers, register_provider_schemas,
407 registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
408 reset_provider_catalog, resolve_live_or_as_of, resolve_live_trigger_binding,
409 resolve_trigger_binding_as_of, resume, run_trigger_harness_fixture, scheduler_in_flight_by_key,
410 scheduler_ready_stats_by_key, snapshot_dispatcher_stats, snapshot_orchestrator_budget,
411 snapshot_trigger_bindings, unpin_trigger_binding, usd_to_micros, worker_claims_topic_name,
412 worker_job_topic_name, worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest,
413 DispatchError, DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport,
414 DispatcherStatsSnapshot, ExtensionProviderPayload, FairnessKey, HeaderRedactionPolicy,
415 InboxIndex, OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog,
416 ProviderCatalogError, ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload,
417 ProviderRuntimeMetadata, ProviderSchema, ProviderSecretRequirement, ReadyKeyStats,
418 RecordedTriggerBinding, RetryPolicy, SchedulableJob, SchedulerKeyStat, SchedulerPolicy,
419 SchedulerSnapshot, SchedulerState, SchedulerStrategy, SignatureStatus,
420 SignatureVerificationMetadata, StreamEventPayload, TenantId, TraceId, TriggerBatchConfig,
421 TriggerBindingSnapshot, TriggerBindingSource, TriggerBindingSpec,
422 TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig, TriggerDebounceConfig,
423 TriggerDispatchOutcome, TriggerEvent, TriggerEventId, TriggerExpressionSpec,
424 TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult, TriggerId,
425 TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
426 TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
427 TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
428 WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
429 WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
430 DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
431 TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
432 TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
433 TRIGGER_INBOX_OBSERVABILITY_TOPIC, TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC,
434 TRIGGER_TEST_FIXTURES, WORKER_QUEUE_CATALOG_TOPIC,
435};
436pub use trust_graph::{
437 append_active_scope_attenuation_alert, append_active_trust_record,
438 append_scope_attenuation_alert, append_trust_record, export_trust_chain,
439 group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
440 query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
441 summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
442 TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
443 TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord,
444 TrustRecordActionKind, TrustScore, TrustTraceGroup, METADATA_KEY_ACTOR_CHAIN,
445 METADATA_KEY_ACTOR_CHAIN_ALERT, METADATA_KEY_EFFECTS_GRANT, METADATA_KEY_EFFECTS_USED,
446 METADATA_KEY_PARENT_RECORD_ID, OPENTRUSTGRAPH_ACCEPTED_SCHEMAS, OPENTRUSTGRAPH_CHAIN_SCHEMA_V0,
447 OPENTRUSTGRAPH_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0_1, TRUST_ACTION_RELEASE,
448 TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_TOPIC_PREFIX,
449 TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
450};
451pub use value::*;
452pub use vm::*;
453
454#[cfg(feature = "vm-bench-internals")]
455#[doc(hidden)]
456pub mod bench_internals;
457
458pub fn compile_source(source: &str) -> Result<Chunk, String> {
463 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
464 Compiler::new().compile(&program).map_err(|e| e.to_string())
465}
466
467pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
472 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
473 let has_pipeline = program.iter().any(|sn| {
474 let (_, inner) = harn_parser::peel_attributes(sn);
475 matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
476 });
477 if !has_pipeline {
478 return Err(format!("no pipeline named `{pipeline_name}` in source"));
479 }
480 Compiler::new()
481 .compile_named(&program, pipeline_name)
482 .map_err(|e| e.to_string())
483}
484
485pub struct SchemaAliasResolver {
493 compiler: compiler::Compiler,
494}
495
496impl SchemaAliasResolver {
497 pub fn empty() -> Self {
500 Self {
501 compiler: compiler::Compiler::new(),
502 }
503 }
504
505 pub fn from_program(program: &[harn_parser::SNode]) -> Self {
508 let mut compiler = compiler::Compiler::new();
509 compiler.collect_type_aliases(program);
510 Self { compiler }
511 }
512
513 pub fn json_schema_for_type_expr(
516 &self,
517 type_expr: &harn_parser::TypeExpr,
518 ) -> Option<serde_json::Value> {
519 let expanded = self.compiler.expand_alias(type_expr);
520 let schema = compiler::Compiler::type_expr_to_schema_value(&expanded)?;
521 let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
522 Some(llm::vm_value_to_json(&json_schema))
523 }
524
525 pub fn json_schema_for_typed_params(
528 &self,
529 params: &[harn_parser::TypedParam],
530 ) -> serde_json::Value {
531 let mut properties = serde_json::Map::new();
532 let mut required = Vec::new();
533
534 for param in params {
535 let param_schema = param
536 .type_expr
537 .as_ref()
538 .and_then(|type_expr| self.json_schema_for_type_expr(type_expr))
539 .unwrap_or_else(|| serde_json::json!({}));
540 if param.default_value.is_none() {
541 required.push(serde_json::Value::String(param.name.clone()));
542 }
543 properties.insert(param.name.clone(), param_schema);
544 }
545
546 let mut schema = serde_json::Map::new();
547 schema.insert(
548 "type".to_string(),
549 serde_json::Value::String("object".to_string()),
550 );
551 schema.insert(
552 "properties".to_string(),
553 serde_json::Value::Object(properties),
554 );
555 if !required.is_empty() {
556 schema.insert("required".to_string(), serde_json::Value::Array(required));
557 }
558 serde_json::Value::Object(schema)
559 }
560}
561
562pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
566 SchemaAliasResolver::empty().json_schema_for_type_expr(type_expr)
567}
568
569pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
570 SchemaAliasResolver::empty().json_schema_for_typed_params(params)
571}
572
573#[cfg(test)]
574mod schema_alias_resolver_tests {
575 use super::*;
576
577 fn fn_params_schema(src: &str) -> serde_json::Value {
578 let program = harn_parser::parse_source(src).expect("parse test source");
579 let resolver = SchemaAliasResolver::from_program(&program);
580 for node in &program {
581 let (_, inner) = harn_parser::peel_attributes(node);
582 if let harn_parser::Node::FnDecl { params, .. } = &inner.node {
583 return resolver.json_schema_for_typed_params(params);
584 }
585 }
586 panic!("no fn decl in test source");
587 }
588
589 #[test]
590 fn named_shape_alias_projects_like_inline_shape() {
591 let inline = fn_params_schema("pub fn f(p: {kind: string, path: string}) {}");
592 let aliased =
593 fn_params_schema("type Src = {kind: string, path: string}\npub fn f(p: Src) {}");
594 assert_eq!(
595 aliased, inline,
596 "a named shape alias must project the same inputSchema as its inline shape",
597 );
598 assert_ne!(
599 aliased["properties"]["p"],
600 serde_json::json!({}),
601 "the alias parameter must not erase to an empty schema",
602 );
603 }
604
605 #[test]
606 fn literal_union_alias_projects_json_enum() {
607 let schema = fn_params_schema("type Kind = \"local\" | \"ssh\"\npub fn f(p: Kind) {}");
608 let p = &schema["properties"]["p"];
609 assert_eq!(p["type"], "string");
610 assert_eq!(p["enum"], serde_json::json!(["local", "ssh"]));
611 }
612
613 #[test]
614 fn unknown_named_type_still_erases_to_empty() {
615 let schema = fn_params_schema("pub fn f(p: Unknown) {}");
617 assert_eq!(schema["properties"]["p"], serde_json::json!({}));
618 }
619}
620
621fn reset_llm_state_for_thread_reset() {
622 llm::reset_llm_state();
623 #[cfg(test)]
624 reset_thread_local_state_test_hooks::before_llm_global_reset();
625 llm::reset_rate_limit_registry();
628 llm_config::clear_user_overrides();
629 llm_config::clear_runtime_provider_endpoint_overrides();
630}
631
632#[cfg(test)]
633mod reset_thread_local_state_test_hooks {
634 use std::sync::{Arc, Mutex, OnceLock};
635
636 type Hook = Arc<dyn Fn() + Send + Sync + 'static>;
637
638 static BEFORE_LLM_GLOBAL_RESET: OnceLock<Mutex<Option<Hook>>> = OnceLock::new();
639
640 fn before_llm_global_reset_hook() -> &'static Mutex<Option<Hook>> {
641 BEFORE_LLM_GLOBAL_RESET.get_or_init(|| Mutex::new(None))
642 }
643
644 pub(crate) struct HookGuard;
645
646 impl Drop for HookGuard {
647 fn drop(&mut self) {
648 let mut hook = before_llm_global_reset_hook()
649 .lock()
650 .unwrap_or_else(std::sync::PoisonError::into_inner);
651 *hook = None;
652 }
653 }
654
655 pub(crate) fn install_before_llm_global_reset(hook: Hook) -> HookGuard {
656 let mut slot = before_llm_global_reset_hook()
657 .lock()
658 .unwrap_or_else(std::sync::PoisonError::into_inner);
659 *slot = Some(hook);
660 HookGuard
661 }
662
663 pub(crate) fn before_llm_global_reset() {
664 let hook = before_llm_global_reset_hook()
665 .lock()
666 .unwrap_or_else(std::sync::PoisonError::into_inner)
667 .clone();
668 if let Some(hook) = hook {
669 hook();
670 }
671 }
672}
673
674pub fn reset_thread_local_state() {
676 #[cfg(test)]
677 {
678 let _guard = llm::env_guard();
683 reset_llm_state_for_thread_reset();
684 }
685 #[cfg(not(test))]
686 reset_llm_state_for_thread_reset();
687
688 http::reset_http_state();
689 channels::reset_channel_state();
690 event_log::reset_active_event_log();
691 egress::clear_explicit_egress_policy_requirement_for_host();
692 egress::clear_ssrf_guard_requirement_for_host();
693 stdlib::reset_stdlib_state();
694 connectors::clear_active_connector_clients();
695 orchestration::clear_runtime_hooks();
696 orchestration::clear_file_edit_queue();
697 orchestration::clear_execution_policy_stacks();
698 orchestration::clear_command_policies();
699 orchestration::clear_pipeline_on_finish();
700 orchestration::reset_lifecycle_receipt_registry();
701 orchestration::agent_inbox::reset();
702 tool_call_cancellations::reset_registry();
703 redact::clear_policy_stack();
704 security::reset_thread_state();
705 triggers::clear_dispatcher_state();
706 triggers::clear_trigger_registry();
707 events::reset_event_sinks();
708 tracing::set_tracing_enabled(false);
709 tracing::reset_tracing();
710 agent_events::reset_all_sinks();
722 agent_sessions::reset_session_store();
723 mcp_registry::reset();
724 mcp_host::reset_for_tests();
725 call_budget::reset_call_budget_state();
726 clock_mock::leak_audit::reset();
727}
728
729#[cfg(test)]
730mod reset_leak_tests {
731 use super::*;
737 use crate::value::VmValue;
738
739 #[test]
740 fn reset_drains_pending_file_edit_notifications() {
741 orchestration::queue_file_edited("stale.harn", serde_json::json!({"operation": "write"}));
742
743 reset_thread_local_state();
744
745 assert!(
746 orchestration::drain_file_edits().is_empty(),
747 "a later VM run must not receive file edits queued by the previous run"
748 );
749 }
750
751 #[test]
760 fn builtin_profile_recording_ends_with_its_run_not_with_a_global_reset() {
761 let _lock = builtin_profile::test_lock()
762 .lock()
763 .unwrap_or_else(std::sync::PoisonError::into_inner);
764 let recording = builtin_profile::enable();
765 builtin_profile::record("run_shell", std::time::Duration::from_millis(5));
766 assert!(builtin_profile::is_enabled());
767 assert!(!builtin_profile::snapshot().is_empty());
768
769 reset_thread_local_state();
770
771 assert!(
772 builtin_profile::is_enabled(),
773 "an unrelated global reset must not disarm an in-flight profiled run"
774 );
775 assert!(
776 !builtin_profile::snapshot().is_empty(),
777 "an unrelated global reset must not drop totals the run still owns"
778 );
779
780 drop(recording);
781
782 assert!(
783 !builtin_profile::is_enabled(),
784 "a profiled run must not leave the recorder on for the next one"
785 );
786 assert!(
787 builtin_profile::snapshot().is_empty(),
788 "builtin totals must be empty once the run's guard drops"
789 );
790 }
791
792 #[test]
797 fn reset_drains_session_changed_paths() {
798 let session = "sess-leak";
799 agent_sessions::open_or_create(Some(session.to_string()));
800 agent_sessions::record_session_changed_path(session, "/tmp/written-by-a-dead-run.txt");
801 assert!(!agent_sessions::session_changed_paths(session).is_empty());
802 reset_thread_local_state();
803 assert!(
804 agent_sessions::session_changed_paths(session).is_empty(),
805 "a receipt must not inherit an abandoned session's writes"
806 );
807 }
808
809 #[test]
810 fn reset_drains_agent_inbox() {
811 orchestration::agent_inbox::reset();
812 orchestration::agent_inbox::push("sess-2660", "note", "leak", "test");
813 assert!(orchestration::agent_inbox::session_count() > 0);
814 reset_thread_local_state();
815 assert_eq!(
816 orchestration::agent_inbox::session_count(),
817 0,
818 "agent_inbox must be empty after reset"
819 );
820 }
821
822 #[test]
823 fn reset_drains_tool_call_cancellation_registry() {
824 tool_call_cancellations::reset_registry();
825 let registered = tool_call_cancellations::register("sess-2660", "call-1", "tool");
828 if let Some((_handle, guard)) = registered {
829 std::mem::forget(guard);
830 }
831 assert!(tool_call_cancellations::registry_len() > 0);
832 reset_thread_local_state();
833 assert_eq!(
834 tool_call_cancellations::registry_len(),
835 0,
836 "tool-call cancellation registry must be empty after reset"
837 );
838 }
839
840 #[test]
841 fn reset_drains_routing_policy_registry() {
842 llm::routing::clear_policy_registry();
843 let mut config: crate::value::DictMap = crate::value::DictMap::new();
844 config.insert(
845 crate::value::intern_key("chain"),
846 VmValue::List(std::sync::Arc::new(vec![VmValue::String(
847 arcstr::ArcStr::from("mock:mock"),
848 )])),
849 );
850 llm::routing::build_routing_policy(&config).expect("intern a routing policy");
851 assert!(llm::routing::policy_registry_len() > 0);
852 reset_thread_local_state();
853 assert_eq!(
854 llm::routing::policy_registry_len(),
855 0,
856 "routing policy registry must be empty after reset"
857 );
858 }
859
860 #[test]
861 fn reset_holds_llm_env_guard_while_wiping_llm_globals() {
862 let observed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
863 let observed_hook = std::sync::Arc::clone(&observed);
864 let _hook = reset_thread_local_state_test_hooks::install_before_llm_global_reset(
865 std::sync::Arc::new(move || {
866 assert!(
867 matches!(
868 llm::env_lock().try_lock(),
869 Err(std::sync::TryLockError::WouldBlock)
870 ),
871 "reset_thread_local_state must hold env_guard before wiping LLM globals"
872 );
873 observed_hook.store(true, std::sync::atomic::Ordering::SeqCst);
874 }),
875 );
876
877 reset_thread_local_state();
878 assert!(
879 observed.load(std::sync::atomic::Ordering::SeqCst),
880 "LLM global reset hook should have run"
881 );
882 }
883}