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_supervision_sink,
162    register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
163    resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
164    PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
165    PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
166    PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
167    PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
168    PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
169    PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
170    PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
171    PersonaValueSinkRegistration, PERSONA_RUNTIME_TOPIC,
172};
173pub use provenance::{
174    build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
175    ReceiptBuildOptions, ReceiptVerificationReport,
176};
177pub use receipts::{
178    Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
179    RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
180};
181pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
182pub use schema::json_to_vm_value;
183pub use sessions::{
184    CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
185    TouchSession, SESSIONS_TOPIC,
186};
187pub use stdlib::hitl::{
188    append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
189    HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
190};
191pub use stdlib::host::{clear_host_call_bridge, set_host_call_bridge, HostCallBridge};
192pub use stdlib::io::{set_stdout_passthrough, take_stderr_buffer};
193pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
194pub use stdlib::secret_scan::{
195    append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
196    SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
197};
198pub use stdlib::template::{
199    lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
200    PromptSourceSpan, PromptSpanKind,
201};
202pub use stdlib::waitpoint::{
203    process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
204};
205pub use stdlib::workflow_messages::{
206    workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
207    workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
208    workflow_update_for_base, WorkflowMailboxState,
209};
210pub use stdlib::{
211    register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
212};
213pub use store::register_store_builtins;
214pub use tenant::{
215    tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
216    TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
217    TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
218    TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
219    TENANT_SECRET_NAMESPACE_PREFIX,
220};
221pub use triggers::{
222    append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
223    binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
224    clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
225    dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
226    install_manifest_triggers, install_orchestrator_budget, install_provider_catalog,
227    micros_to_usd, note_autonomous_decision, note_orchestrator_budget_cost,
228    orchestrator_budget_would_exceed, parse_flow_control_duration, pause, pin_trigger_binding,
229    provider_metadata, record_predicate_cost_sample, redact_headers, register_provider_schema,
230    registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
231    reset_provider_catalog, reset_provider_catalog_with, resolve_live_or_as_of,
232    resolve_live_trigger_binding, resolve_trigger_binding_as_of, resume,
233    run_trigger_harness_fixture, scheduler_in_flight_by_key, scheduler_ready_stats_by_key,
234    snapshot_dispatcher_stats, snapshot_orchestrator_budget, snapshot_trigger_bindings,
235    unpin_trigger_binding, usd_to_micros, worker_claims_topic_name, worker_job_topic_name,
236    worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest, DispatchError,
237    DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport, DispatcherStatsSnapshot,
238    FairnessKey, HeaderRedactionPolicy, InboxIndex, NotionPolledChangeEvent,
239    OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog, ProviderCatalogError,
240    ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload, ProviderRuntimeMetadata,
241    ProviderSchema, ProviderSecretRequirement, ReadyKeyStats, RecordedTriggerBinding, RetryPolicy,
242    SchedulableJob, SchedulerKeyStat, SchedulerPolicy, SchedulerSnapshot, SchedulerState,
243    SchedulerStrategy, SignatureStatus, SignatureVerificationMetadata, StreamEventPayload,
244    TenantId, TraceId, TriggerBatchConfig, TriggerBindingSnapshot, TriggerBindingSource,
245    TriggerBindingSpec, TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig,
246    TriggerDebounceConfig, TriggerDispatchOutcome, TriggerEvent, TriggerEventId,
247    TriggerExpressionSpec, TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult,
248    TriggerId, TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
249    TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
250    TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
251    WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
252    WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
253    DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
254    TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
255    TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
256    TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC, TRIGGER_TEST_FIXTURES,
257    WORKER_QUEUE_CATALOG_TOPIC,
258};
259pub use trust_graph::{
260    append_active_trust_record, append_trust_record, export_trust_chain,
261    group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
262    query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
263    summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
264    TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
265    TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord, TrustScore,
266    TrustTraceGroup, OPENTRUSTGRAPH_CHAIN_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0,
267    TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_TOPIC_PREFIX,
268    TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
269};
270pub use value::*;
271pub use vm::*;
272
273#[cfg(feature = "vm-bench-internals")]
274#[doc(hidden)]
275pub mod bench_internals;
276
277/// Lex, parse, type-check, and compile source to bytecode in one call.
278/// Bails on the first type error. For callers that need diagnostics
279/// rather than early exit, use `harn_parser::check_source` directly
280/// and then call `Compiler::new().compile(&program)`.
281pub fn compile_source(source: &str) -> Result<Chunk, String> {
282    let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
283    Compiler::new().compile(&program).map_err(|e| e.to_string())
284}
285
286/// Same as [`compile_source`] but compiles a specific named pipeline as
287/// the program entry point instead of the default-pipeline-or-first
288/// selection rule. Returns a runtime error when no pipeline with
289/// `pipeline_name` exists in the source.
290pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
291    let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
292    let has_pipeline = program.iter().any(|sn| {
293        let (_, inner) = harn_parser::peel_attributes(sn);
294        matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
295    });
296    if !has_pipeline {
297        return Err(format!("no pipeline named `{pipeline_name}` in source"));
298    }
299    Compiler::new()
300        .compile_named(&program, pipeline_name)
301        .map_err(|e| e.to_string())
302}
303
304pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
305    let schema = compiler::Compiler::type_expr_to_schema_value(type_expr)?;
306    let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
307    Some(llm::vm_value_to_json(&json_schema))
308}
309
310pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
311    let mut properties = serde_json::Map::new();
312    let mut required = Vec::new();
313
314    for param in params {
315        let param_schema = param
316            .type_expr
317            .as_ref()
318            .and_then(json_schema_for_type_expr)
319            .unwrap_or_else(|| serde_json::json!({}));
320        if param.default_value.is_none() {
321            required.push(serde_json::Value::String(param.name.clone()));
322        }
323        properties.insert(param.name.clone(), param_schema);
324    }
325
326    let mut schema = serde_json::Map::new();
327    schema.insert(
328        "type".to_string(),
329        serde_json::Value::String("object".to_string()),
330    );
331    schema.insert(
332        "properties".to_string(),
333        serde_json::Value::Object(properties),
334    );
335    if !required.is_empty() {
336        schema.insert("required".to_string(), serde_json::Value::Array(required));
337    }
338    serde_json::Value::Object(schema)
339}
340
341/// Reset all thread-local state that can leak between test runs.
342pub fn reset_thread_local_state() {
343    llm::reset_llm_state();
344    llm_config::clear_user_overrides();
345    http::reset_http_state();
346    event_log::reset_active_event_log();
347    stdlib::reset_stdlib_state();
348    connectors::clear_active_connector_clients();
349    orchestration::clear_runtime_hooks();
350    orchestration::clear_execution_policy_stacks();
351    orchestration::clear_command_policies();
352    redact::clear_policy_stack();
353    triggers::clear_dispatcher_state();
354    triggers::clear_trigger_registry();
355    events::reset_event_sinks();
356    agent_events::reset_all_sinks();
357    agent_sessions::reset_session_store();
358    mcp_registry::reset();
359    clock_mock::leak_audit::reset();
360}