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