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