Skip to main content

harn_vm/
lib.rs

1#![recursion_limit = "256"]
2#![allow(clippy::result_large_err, clippy::cloned_ref_to_slice_refs)]
3
4/// Re-export of the unified clock substrate so downstream crates (CLI,
5/// orchestrator, harn-cloud) can depend on a single canonical `Clock`
6/// trait without each adding `harn-clock` as a direct dependency.
7pub use harn_clock as clock;
8
9pub mod a2a;
10pub mod agent_events;
11pub mod agent_sessions;
12pub mod atomic_io;
13pub mod autonomy;
14pub(crate) mod aws_sigv4;
15pub mod bridge;
16mod builtin_id;
17pub mod bytecode_cache;
18pub mod channel_guardrails;
19pub mod channels;
20pub mod checkpoint;
21mod chunk;
22mod compiler;
23pub mod composition;
24pub mod config;
25pub mod connectors;
26pub mod corrections;
27pub mod egress;
28pub mod event_log;
29pub mod events;
30pub mod flow;
31pub mod harness;
32pub(crate) mod harness_crypto;
33pub mod harness_net;
34pub mod harness_system;
35mod http;
36pub mod jsonrpc;
37pub mod llm;
38pub mod llm_config;
39pub mod mcp;
40pub mod mcp_auth;
41pub mod mcp_card;
42pub mod mcp_elicit;
43pub mod mcp_file_upload;
44pub mod mcp_progress;
45pub mod mcp_protocol;
46pub mod mcp_registry;
47pub mod mcp_sampling;
48pub mod mcp_server;
49pub mod metadata;
50pub mod module_artifact;
51pub mod observability;
52pub mod orchestration;
53pub mod personas;
54pub mod process_sandbox;
55pub mod profile;
56pub mod provenance;
57pub mod provider_catalog;
58pub mod receipts;
59pub mod record_filter;
60pub mod redact;
61pub mod run_events;
62pub mod runtime_context;
63pub(crate) mod runtime_guards;
64pub mod runtime_limits;
65pub mod runtime_paths;
66pub mod schema;
67pub(crate) mod secret_patterns;
68pub mod secrets;
69pub mod session_bundle;
70pub mod sessions;
71pub(crate) mod shared_state;
72pub mod shells;
73pub mod skills;
74pub mod stdlib;
75pub mod stdlib_modules;
76pub mod step_runtime;
77pub mod store;
78pub(crate) mod synchronization;
79pub mod tenant;
80pub(crate) mod term;
81pub mod testbench;
82pub mod tool_annotations;
83pub mod tool_call_cancellations;
84pub mod tool_surface;
85pub mod tracing;
86pub mod triggers;
87pub mod trust_graph;
88pub(crate) mod url_encoding;
89
90/// Crate-wide deterministic clock mock used by stdlib time builtins, the
91/// trigger dispatcher, the cron scheduler, and Rust-side tests. Re-exports
92/// the long-lived implementation under `triggers::test_util::clock` so all
93/// callers go through one source of truth.
94pub mod clock_mock {
95    pub use crate::triggers::test_util::clock::{
96        active_mock_clock, advance, clear_overrides, install_override, instant_now, is_mocked,
97        now_ms, now_utc, sleep, ClockInstant, ClockOverrideGuard, MockClock,
98    };
99
100    /// Runtime audit for capabilities that observe real wall-clock or
101    /// monotonic time while a testbench mock is installed. See the module
102    /// docs for the full design.
103    pub mod leak_audit {
104        #[cfg(test)]
105        pub use crate::triggers::test_util::clock_leak::TEST_LOCK;
106        pub use crate::triggers::test_util::clock_leak::{
107            drain, instant_now, reset, snapshot, wall_now, ClockLeak,
108        };
109    }
110}
111
112pub mod typecheck;
113pub mod value;
114pub mod visible_text;
115mod vm;
116pub mod waitpoints;
117pub mod workspace_anchor;
118pub mod workspace_path;
119
120pub use builtin_id::BuiltinId;
121pub use checkpoint::register_checkpoint_builtins;
122pub use chunk::*;
123pub use compiler::*;
124pub use connectors::{
125    active_connector_client, active_metrics_registry, clear_active_connector_clients,
126    clear_active_metrics_registry, connector_export_denied_builtin_reason,
127    connector_export_effect_class,
128    cron::{CatchupMode, CronConnector},
129    default_connector_export_policy,
130    harn_module::{
131        load_contract as load_harn_connector_contract, HarnConnector, HarnConnectorContract,
132    },
133    hmac::{verify_hmac_signed, SIGNATURE_VERIFY_AUDIT_TOPIC},
134    install_active_connector_clients, install_active_metrics_registry,
135    postprocess_normalized_event, ActivationHandle, ClientError, Connector, ConnectorClient,
136    ConnectorCtx, ConnectorError, ConnectorExportEffectClass, ConnectorHttpResponse,
137    ConnectorMetricsSnapshot, ConnectorNormalizeResult, ConnectorRegistry, GenericWebhookConnector,
138    HarnConnectorEffectPolicies, MetricsRegistry, PostNormalizeOutcome, ProviderPayloadSchema,
139    RateLimitConfig, RateLimiterFactory, RawInbound, StreamConnector, TriggerBinding, TriggerKind,
140    TriggerRegistry, WebhookSignatureVariant,
141};
142pub use corrections::{
143    append_correction_record, apply_corrections_to_policy, correction_query_filters_from_json,
144    correction_record_from_json, policy_with_corrections, query_correction_records,
145    CorrectionQueryFilters, CorrectionRecord, CorrectionScope, CORRECTIONS_TOPIC,
146    CORRECTION_EVENT_KIND, CORRECTION_SCHEMA_V0,
147};
148pub use harness::{
149    DenyEvent, Harness, HarnessCall, HarnessClock, HarnessCrypto, HarnessEnv, HarnessFs,
150    HarnessKind, HarnessLlm, HarnessNet, HarnessProcess, HarnessRandom, HarnessStdio,
151    HarnessSystem, HarnessTerm, MockAwareClock, MockHarnessBuilder, VmHarness,
152};
153pub use harness_net::{
154    bypass_enabled as net_policy_bypass_enabled, NetMatcher, NetPolicy, NetPolicyAudit,
155    NetPolicyDecision, NetPolicyDefault, NetPolicyRule, OnViolation, HARN_NET_POLICY_BYPASS_ENV,
156    NET_POLICY_AUDIT_TOPIC,
157};
158pub use http::{register_http_builtins, reset_http_state};
159pub use llm::register_llm_builtins;
160pub use llm::trigger_predicate::TriggerPredicateBudget;
161pub use llm::{current_agent_session_id, register_session_end_hook};
162pub use mcp::{connect_mcp_server_from_json, connect_mcp_server_from_spec, register_mcp_builtins};
163pub use mcp_card::{fetch_server_card, load_server_card_from_path, CardError};
164pub use mcp_registry::{
165    active_handle as mcp_active_handle, ensure_active as mcp_ensure_active,
166    get_registration as mcp_get_registration, install_active as mcp_install_active,
167    is_registered as mcp_is_registered, register_servers as mcp_register_servers,
168    release as mcp_release, reset as mcp_reset_registry, snapshot_status as mcp_snapshot_status,
169    sweep_expired as mcp_sweep_expired, RegisteredMcpServer, RegistryStatus,
170};
171pub use mcp_server::{
172    take_mcp_serve_prompts, take_mcp_serve_registry, take_mcp_serve_resource_templates,
173    take_mcp_serve_resources, tool_registry_to_mcp_tools, McpServer,
174};
175pub use metadata::{register_metadata_builtins, register_scan_builtins};
176pub use orchestration::{
177    benchmark_adapted_replay_pair, benchmark_replay_trace, build_replay_benchmark_report,
178    OpenCodeJsonlAdapter, ReplayBenchmarkCloudIngest, ReplayBenchmarkError,
179    ReplayBenchmarkFixtureReceipt, ReplayBenchmarkFixtureReport, ReplayBenchmarkMetrics,
180    ReplayBenchmarkReport, ReplayBenchmarkSuiteIdentity, ReplayBenchmarkSummary,
181    ReplayCategoryMetric, ReplayDebuggingProxyMetrics, ReplayRuntimeCostMetrics,
182    ReplayTraceAdapter, OPENCODE_JSONL_ADAPTER_ID, OPENCODE_JSONL_ADAPTER_SCHEMA_VERSION,
183    REPLAY_BENCHMARK_CLOUD_INGEST_KIND, REPLAY_BENCHMARK_REPORT_SCHEMA_VERSION,
184};
185pub use orchestration::{
186    canonicalize_run, first_divergence, run_replay_oracle_trace, ReplayAllowlistRule,
187    ReplayDivergence, ReplayExpectation, ReplayOracleError, ReplayOracleReport, ReplayOracleTrace,
188    ReplayTraceRun, ReplayTraceRunCounts, REPLAY_TRACE_SCHEMA_VERSION,
189};
190pub use orchestration::{
191    install_handoff_routes, snapshot_handoff_routes, HandoffRouteConfig,
192    HandoffRouteDecisionRecord, HandoffRouteTargetConfig,
193};
194pub use personas::{
195    disable_persona, fire_schedule as fire_persona_schedule, fire_trigger as fire_persona_trigger,
196    format_ms as format_persona_ms, now_ms as persona_now_ms, parse_rfc3339_ms as parse_persona_ms,
197    pause_persona, persona_status, record_persona_spend, register_persona_supervision_sink,
198    register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
199    resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
200    PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
201    PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
202    PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
203    PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
204    PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
205    PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
206    PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
207    PersonaValueSinkRegistration, StageDecl, StageExit, PERSONA_RUNTIME_TOPIC,
208};
209pub use provenance::{
210    build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
211    ReceiptBuildOptions, ReceiptVerificationReport,
212};
213pub use receipts::{
214    Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
215    RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
216};
217pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
218pub use runtime_limits::{
219    RuntimeLimitDescription, RuntimeLimitEntry, RuntimeLimits, RuntimeLimitsReport,
220    RUNTIME_LIMIT_DESCRIPTIONS,
221};
222pub use schema::json_to_vm_value;
223pub use sessions::{
224    CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
225    TouchSession, SESSIONS_TOPIC,
226};
227pub use stdlib::hitl::{
228    append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
229    HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
230};
231pub use stdlib::host::{clear_host_call_bridge, set_host_call_bridge, HostCallBridge};
232pub use stdlib::io::{set_stdout_passthrough, take_stderr_buffer};
233pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
234pub use stdlib::secret_scan::{
235    append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
236    SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
237};
238pub use stdlib::template::{
239    lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
240    PromptSourceSpan, PromptSpanKind,
241};
242pub use stdlib::waitpoint::{
243    process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
244};
245pub use stdlib::workflow_messages::{
246    workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
247    workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
248    workflow_update_for_base, WorkflowMailboxState,
249};
250pub use stdlib::{
251    register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
252    register_vm_stdlib_with_deferred_llm,
253};
254pub use store::register_store_builtins;
255pub use tenant::{
256    tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
257    TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
258    TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
259    TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
260    TENANT_SECRET_NAMESPACE_PREFIX,
261};
262pub use triggers::{
263    append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
264    binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
265    clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
266    dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
267    install_manifest_triggers, install_orchestrator_budget, install_provider_catalog,
268    micros_to_usd, note_autonomous_decision, note_orchestrator_budget_cost,
269    orchestrator_budget_would_exceed, parse_flow_control_duration, pause, pin_trigger_binding,
270    provider_metadata, record_predicate_cost_sample, redact_headers, register_provider_schema,
271    registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
272    reset_provider_catalog, reset_provider_catalog_with, resolve_live_or_as_of,
273    resolve_live_trigger_binding, resolve_trigger_binding_as_of, resume,
274    run_trigger_harness_fixture, scheduler_in_flight_by_key, scheduler_ready_stats_by_key,
275    snapshot_dispatcher_stats, snapshot_orchestrator_budget, snapshot_trigger_bindings,
276    unpin_trigger_binding, usd_to_micros, worker_claims_topic_name, worker_job_topic_name,
277    worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest, DispatchError,
278    DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport, DispatcherStatsSnapshot,
279    FairnessKey, HeaderRedactionPolicy, InboxIndex, NotionPolledChangeEvent,
280    OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog, ProviderCatalogError,
281    ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload, ProviderRuntimeMetadata,
282    ProviderSchema, ProviderSecretRequirement, ReadyKeyStats, RecordedTriggerBinding, RetryPolicy,
283    SchedulableJob, SchedulerKeyStat, SchedulerPolicy, SchedulerSnapshot, SchedulerState,
284    SchedulerStrategy, SignatureStatus, SignatureVerificationMetadata, StreamEventPayload,
285    TenantId, TraceId, TriggerBatchConfig, TriggerBindingSnapshot, TriggerBindingSource,
286    TriggerBindingSpec, TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig,
287    TriggerDebounceConfig, TriggerDispatchOutcome, TriggerEvent, TriggerEventId,
288    TriggerExpressionSpec, TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult,
289    TriggerId, TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
290    TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
291    TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
292    WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
293    WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
294    DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
295    TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
296    TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
297    TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC, TRIGGER_TEST_FIXTURES,
298    WORKER_QUEUE_CATALOG_TOPIC,
299};
300pub use trust_graph::{
301    append_active_trust_record, append_trust_record, export_trust_chain,
302    group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
303    query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
304    summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
305    TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
306    TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord,
307    TrustRecordActionKind, TrustScore, TrustTraceGroup, METADATA_KEY_EFFECTS_GRANT,
308    METADATA_KEY_EFFECTS_USED, METADATA_KEY_PARENT_RECORD_ID, OPENTRUSTGRAPH_ACCEPTED_SCHEMAS,
309    OPENTRUSTGRAPH_CHAIN_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0_1,
310    TRUST_ACTION_RELEASE, TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC,
311    TRUST_GRAPH_LEGACY_TOPIC_PREFIX, TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
312};
313pub use value::*;
314pub use vm::*;
315
316#[cfg(feature = "vm-bench-internals")]
317#[doc(hidden)]
318pub mod bench_internals;
319
320/// Lex, parse, type-check, and compile source to bytecode in one call.
321/// Bails on the first type error. For callers that need diagnostics
322/// rather than early exit, use `harn_parser::check_source` directly
323/// and then call `Compiler::new().compile(&program)`.
324pub fn compile_source(source: &str) -> Result<Chunk, String> {
325    let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
326    Compiler::new().compile(&program).map_err(|e| e.to_string())
327}
328
329/// Same as [`compile_source`] but compiles a specific named pipeline as
330/// the program entry point instead of the default-pipeline-or-first
331/// selection rule. Returns a runtime error when no pipeline with
332/// `pipeline_name` exists in the source.
333pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
334    let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
335    let has_pipeline = program.iter().any(|sn| {
336        let (_, inner) = harn_parser::peel_attributes(sn);
337        matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
338    });
339    if !has_pipeline {
340        return Err(format!("no pipeline named `{pipeline_name}` in source"));
341    }
342    Compiler::new()
343        .compile_named(&program, pipeline_name)
344        .map_err(|e| e.to_string())
345}
346
347pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
348    let schema = compiler::Compiler::type_expr_to_schema_value(type_expr)?;
349    let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
350    Some(llm::vm_value_to_json(&json_schema))
351}
352
353pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
354    let mut properties = serde_json::Map::new();
355    let mut required = Vec::new();
356
357    for param in params {
358        let param_schema = param
359            .type_expr
360            .as_ref()
361            .and_then(json_schema_for_type_expr)
362            .unwrap_or_else(|| serde_json::json!({}));
363        if param.default_value.is_none() {
364            required.push(serde_json::Value::String(param.name.clone()));
365        }
366        properties.insert(param.name.clone(), param_schema);
367    }
368
369    let mut schema = serde_json::Map::new();
370    schema.insert(
371        "type".to_string(),
372        serde_json::Value::String("object".to_string()),
373    );
374    schema.insert(
375        "properties".to_string(),
376        serde_json::Value::Object(properties),
377    );
378    if !required.is_empty() {
379        schema.insert("required".to_string(), serde_json::Value::Array(required));
380    }
381    serde_json::Value::Object(schema)
382}
383
384/// Reset all thread-local state that can leak between test runs.
385pub fn reset_thread_local_state() {
386    llm::reset_llm_state();
387    llm_config::clear_user_overrides();
388    http::reset_http_state();
389    channels::reset_channel_state();
390    event_log::reset_active_event_log();
391    egress::clear_explicit_egress_policy_requirement_for_host();
392    stdlib::reset_stdlib_state();
393    connectors::clear_active_connector_clients();
394    orchestration::clear_runtime_hooks();
395    orchestration::clear_execution_policy_stacks();
396    orchestration::clear_command_policies();
397    orchestration::clear_pipeline_on_finish();
398    orchestration::reset_lifecycle_receipt_registry();
399    redact::clear_policy_stack();
400    triggers::clear_dispatcher_state();
401    triggers::clear_trigger_registry();
402    events::reset_event_sinks();
403    tracing::set_tracing_enabled(false);
404    tracing::reset_tracing();
405    agent_events::reset_all_sinks();
406    agent_sessions::reset_session_store();
407    mcp_registry::reset();
408    clock_mock::leak_audit::reset();
409}