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