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