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