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