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