1#![recursion_limit = "256"]
2#![allow(clippy::result_large_err, clippy::cloned_ref_to_slice_refs)]
3pub use harn_clock as clock;
23
24mod runtime_stack;
25pub use runtime_stack::RUNTIME_STACK_SIZE;
26
27pub mod a2a;
28pub mod actor_chain;
29pub mod agent_events;
30pub(crate) mod agent_session_journal;
31pub mod agent_session_restore;
32pub mod agent_sessions;
33pub mod agent_transcript_budget;
34pub mod atomic_io;
35pub mod autonomy;
36#[cfg(feature = "cloud-aws")]
37pub(crate) mod aws_sigv4;
38#[cfg(not(feature = "cloud-aws"))]
39#[path = "aws_sigv4_disabled.rs"]
40pub(crate) mod aws_sigv4;
41pub mod boundary;
42pub mod bridge;
43pub use bridge::{
44 inject_leading_authorities, inject_leading_authority, leading_authority_param_count,
45};
46pub mod builtin_profile;
47pub mod bytecode_cache;
48pub mod call_budget;
49pub mod canonical_json;
50pub mod channel_guardrails;
51pub mod channels;
52pub mod checkpoint;
53mod chunk;
54mod compiler;
55pub mod composition;
56pub mod conditional_replace;
57pub mod config;
58pub mod connectors;
59pub mod context_manifest;
60pub mod corrections;
61pub mod coverage;
62pub(crate) mod durable_rate_limit;
63pub mod duration_parse;
64pub mod egress;
65pub mod environment_registry;
66pub mod event_log;
67pub mod events;
68pub mod external_agent;
69pub mod flow;
70pub mod harness;
71pub mod harness_auth;
72pub(crate) mod harness_crypto;
73pub mod harness_net;
74pub mod harness_system;
75pub mod harness_tenant;
76pub mod host_attachments;
77
78pub mod subtask {
84 pub use crate::vm::subtask::{
85 placement, scope_placement, SubtaskPlacement, SubtaskPlacementParseError, PLACEMENT_ENV,
86 PLACEMENT_VALUES,
87 };
88}
89mod http;
90pub mod jsonrpc;
91pub(crate) mod limits;
92pub mod linked_program;
93pub mod llm;
94pub mod llm_config;
95pub mod mcp;
96pub mod mcp_allowlist;
97pub mod mcp_auth;
98pub mod mcp_bulk_auth;
99pub mod mcp_card;
100pub mod mcp_client_roots;
101pub mod mcp_elicit;
102pub mod mcp_host;
103pub mod mcp_identity;
104pub mod mcp_input;
105pub mod mcp_json_discovery;
106pub mod mcp_oauth;
107pub mod mcp_presets;
108pub mod mcp_progress;
109pub mod mcp_protocol;
110pub mod mcp_registry;
111pub mod mcp_sampling;
112pub mod mcp_server;
113pub mod mcp_tasks;
114pub mod metadata;
115pub mod module_artifact;
116pub mod module_source;
117pub mod observability;
118pub mod op_interrupt;
119pub mod orchestration;
120mod persistent_state;
121pub mod personas;
122pub mod portable;
123mod prepared_module;
124pub mod prepared_run;
125pub mod process_sandbox;
126pub mod profile;
127pub mod provenance;
128pub mod provider_catalog;
129pub mod receipts;
130pub mod record_filter;
131pub mod redact;
132pub mod run_events;
133pub mod runtime_context;
134pub(crate) mod runtime_guards;
135pub mod runtime_limits;
136pub mod runtime_paths;
137pub(crate) mod runtime_sqlite;
138pub mod schema;
139pub(crate) mod secret_patterns;
140pub mod secrets;
141pub mod security;
142pub mod session_bundle;
143pub mod session_recap;
144pub mod session_timeline;
145pub mod sessions;
146pub(crate) mod shared_state;
147pub mod shells;
148pub mod skills;
149pub mod stdlib;
150pub use stdlib::session_change::{
152 subscribe as subscribe_session_changes, SessionChangeSubscription,
153};
154pub use stdlib::session_store::open_canonical_store;
155pub mod stdlib_modules;
156pub mod step_runtime;
157pub mod store;
158pub(crate) mod synchronization;
159pub mod tenant;
160pub(crate) mod term;
161pub(crate) mod test_env;
162pub mod testbench;
163pub mod text;
164pub mod text_diff;
165pub mod tool_annotations;
166pub mod tool_call_cancellations;
167pub mod tool_surface;
168pub mod tracing;
169pub mod triggers;
170pub mod trust_graph;
171pub(crate) mod url_encoding;
172pub mod user_dirs;
173
174pub fn initialize_runtime_assets() {
182 secret_patterns::initialize_default_secret_patterns();
183}
184
185#[derive(Debug)]
187pub enum RuntimeInitError {
188 Environment(environment_registry::EnvironmentValidationError),
190 CacheDir(bytecode_cache::CacheDirError),
192}
193
194impl std::fmt::Display for RuntimeInitError {
195 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 match self {
197 Self::Environment(error) => error.fmt(formatter),
198 Self::CacheDir(error) => error.fmt(formatter),
199 }
200 }
201}
202
203impl std::error::Error for RuntimeInitError {}
204
205pub fn initialize_runtime() -> Result<Option<&'static str>, RuntimeInitError> {
214 environment_registry::validate_startup_environment().map_err(RuntimeInitError::Environment)?;
215 let warning = bytecode_cache::check_cache_config().map_err(RuntimeInitError::CacheDir)?;
216 initialize_runtime_assets();
217 Ok(warning)
218}
219
220pub mod clock_mock {
225 pub(crate) use crate::triggers::test_util::clock::scope_capability_clock;
226 pub use crate::triggers::test_util::clock::{
227 active_clock, active_mock_clock, advance, clear_overrides, install_override, instant_now,
228 is_mocked, now_ms, now_utc, sleep, ClockInstant, ClockOverrideGuard, MockClock,
229 };
230
231 pub mod leak_audit {
235 pub use crate::triggers::test_util::clock_leak::{
236 drain, enter_scope, install_scope, instant_now, reset, snapshot, wall_now, ClockLeak,
237 ClockLeakScope, ClockLeakScopeGuard,
238 };
239 }
240}
241
242pub(crate) mod text_index;
243pub mod typecheck;
244pub mod value;
245pub mod verification;
246pub mod visible_text;
247mod vm;
248pub(crate) mod wait_for_graph;
249pub mod waitpoints;
250pub mod windows_path;
251pub mod workspace_anchor;
252pub mod workspace_path;
253
254pub use persistent_state::{
255 register_persistent_state_builtins_at_root, scope_persistent_state_root, PersistentStateRoot,
256 ScopedPersistentStateRoot,
257};
258pub use prepared_module::{PreparedModuleCache, PreparedModuleCacheStats};
259
260pub use actor_chain::{
261 ActorChain, ActorChainEntry, ActorChainError, Principal, ScopeAttenuationMode,
262 ScopeAttenuationPolicy, ScopeAttenuationViolation,
263};
264pub use call_budget::{
265 charge_mcp_call, charge_pg_query, install_mcp_call_budget, install_pg_query_budget,
266 McpCallBudgetGuard, PgQueryBudgetGuard,
267};
268pub use checkpoint::register_checkpoint_builtins;
269pub use chunk::*;
270pub use compiler::*;
271pub use connectors::{
272 active_connector_client, active_metrics_registry, clear_active_connector_clients,
273 clear_active_metrics_registry, connector_export_denied_builtin_reason,
274 connector_export_denied_harness_method_reason, connector_export_effect_class,
275 cron::{CatchupMode, CronConnector},
276 declared_secret_ids, default_connector_export_policy,
277 harn_module::{
278 load_contract as load_harn_connector_contract, HarnConnector, HarnConnectorContract,
279 },
280 hmac::{verify_hmac_signed, SIGNATURE_VERIFY_AUDIT_TOPIC},
281 install_active_connector_clients, install_active_metrics_registry,
282 postprocess_normalized_event, scope_active_connector_clients, ActivationHandle,
283 ActiveConnectorClientsGuard, ClientError, Connector, ConnectorClient, ConnectorClientResolver,
284 ConnectorCtx, ConnectorError, ConnectorExportEffectClass, ConnectorHttpResponse,
285 ConnectorMetricsSnapshot, ConnectorNormalizeResult, ConnectorRegistry, GenericWebhookConnector,
286 HarnConnectorEffectPolicies, MetricsRegistry, PostNormalizeOutcome, ProviderPayloadSchema,
287 RateLimitConfig, RateLimiterFactory, RawInbound, StreamConnector, TriggerBinding, TriggerKind,
288 TriggerRegistry, VmConnectorClients, WebhookSignatureVariant,
289};
290pub use corrections::{
291 append_correction_record, apply_corrections_to_policy, correction_query_filters_from_json,
292 correction_record_from_json, policy_with_corrections, query_correction_records,
293 CorrectionQueryFilters, CorrectionRecord, CorrectionScope, CORRECTIONS_TOPIC,
294 CORRECTION_EVENT_KIND, CORRECTION_SCHEMA_V0,
295};
296pub use harn_kernel::BuiltinId;
297pub use harness::{
298 DenyEvent, Harness, HarnessAgent, HarnessCall, HarnessChannels, HarnessClock, HarnessEnv,
299 HarnessFs, HarnessKind, HarnessLlm, HarnessMemory, HarnessNet, HarnessObs, HarnessPostgres,
300 HarnessProcess, HarnessRandom, HarnessSecrets, HarnessSqlite, HarnessStdio, HarnessSystem,
301 HarnessTenant, HarnessTerm, HarnessTesting, MockHarnessBuilder, VmHarness,
302};
303pub use harness_auth::{
304 current_auth_principal, enter_auth_principal, AuthPrincipal, AuthPrincipalScopeGuard,
305 MISSING_PRINCIPAL_MESSAGE,
306};
307pub use harness_net::{
308 bypass_enabled as net_policy_bypass_enabled, NetMatcher, NetPolicy, NetPolicyAudit,
309 NetPolicyDecision, NetPolicyDefault, NetPolicyRule, OnViolation, HARN_NET_POLICY_BYPASS_ENV,
310 NET_POLICY_AUDIT_TOPIC,
311};
312pub use harness_tenant::{
313 current_tenant_id, enter_tenant, TenantScopeGuard, MISSING_TENANT_MESSAGE,
314};
315pub use http::{register_http_builtins, reset_http_state};
316pub use llm::register_llm_builtins;
317pub use llm::trigger_predicate::TriggerPredicateBudget;
318pub use llm::{
319 current_agent_session_id, install_llm_cost_budget, install_llm_token_budget,
320 peek_llm_cost_budget, peek_llm_token_budget, register_session_end_hook, set_llm_cost_budget,
321 set_llm_token_budget, LlmBudgetGuard, LlmTokenBudgetGuard, SessionEndHookRegistration,
322};
323pub use mcp::{connect_mcp_server_from_json, connect_mcp_server_from_spec, register_mcp_builtins};
324pub use mcp_allowlist::{
325 build_catalog as build_mcp_catalog, catalog_for_request as mcp_catalog_for_request,
326 AdvertisedItem as McpAdvertisedItem, CatalogRequest as McpCatalogRequest, McpAllowlist,
327 McpAllowlistItem, McpCatalog, McpCatalogItem, McpCatalogServer, McpItemKind,
328 MCP_ALLOWLIST_SCHEMA_VERSION,
329};
330pub use mcp_card::{fetch_server_card, load_server_card_from_path, CardError};
331pub use mcp_host::{
332 cache_stats as mcp_host_cache_stats, set_allowlist as set_mcp_host_allowlist,
333 AllowlistDecision as McpHostAllowlistDecision, AllowlistGuard as McpHostAllowlistGuard,
334 BreakerState as McpHostBreakerState, CacheStats as McpHostCacheStats, McpHostStatus,
335 SpawnOptions as McpHostSpawnOptions, SupervisionPolicy as McpHostSupervisionPolicy,
336};
337pub use mcp_registry::{
338 active_handle as mcp_active_handle, ensure_active as mcp_ensure_active,
339 get_registration as mcp_get_registration, install_active as mcp_install_active,
340 is_registered as mcp_is_registered, register_servers as mcp_register_servers,
341 release as mcp_release, reset as mcp_reset_registry, snapshot_status as mcp_snapshot_status,
342 sweep_expired as mcp_sweep_expired, RegisteredMcpServer, RegistryStatus,
343};
344pub use mcp_server::{
345 take_mcp_serve_metadata, take_mcp_serve_prompts, take_mcp_serve_registry,
346 take_mcp_serve_resource_templates, take_mcp_serve_resources, tool_registry_to_mcp_tools,
347 McpServer, McpServerMetadata,
348};
349pub use metadata::register_metadata_builtins;
350pub use observability::audit::{audit_events as audit_obs_events, AuditFinding, AuditFindingKind};
351pub use observability::execution_scope::{
352 current_execution_scope, enter_execution_scope, mint_execution_scope, ExecutionScopeGuard,
353};
354pub use observability::request_id::{current_request_id, enter_request_id, RequestIdScopeGuard};
355pub use orchestration::{
356 benchmark_adapted_replay_pair, benchmark_replay_trace, build_replay_benchmark_report,
357 OpenCodeJsonlAdapter, ReplayBenchmarkCloudIngest, ReplayBenchmarkError,
358 ReplayBenchmarkFixtureReceipt, ReplayBenchmarkFixtureReport, ReplayBenchmarkMetrics,
359 ReplayBenchmarkReport, ReplayBenchmarkSuiteIdentity, ReplayBenchmarkSummary,
360 ReplayCategoryMetric, ReplayDebuggingProxyMetrics, ReplayRuntimeCostMetrics,
361 ReplayTraceAdapter, OPENCODE_JSONL_ADAPTER_ID, OPENCODE_JSONL_ADAPTER_SCHEMA_VERSION,
362 REPLAY_BENCHMARK_CLOUD_INGEST_KIND, REPLAY_BENCHMARK_REPORT_SCHEMA_VERSION,
363};
364pub use orchestration::{
365 canonicalize_run, first_divergence, run_replay_oracle_trace, ReplayAllowlistRule,
366 ReplayDivergence, ReplayExpectation, ReplayOracleError, ReplayOracleReport, ReplayOracleTrace,
367 ReplayTraceRun, ReplayTraceRunCounts, REPLAY_TRACE_SCHEMA_VERSION,
368};
369pub use orchestration::{
370 install_handoff_routes, snapshot_handoff_routes, HandoffRouteConfig,
371 HandoffRouteDecisionRecord, HandoffRouteTargetConfig,
372};
373pub use personas::{
374 disable_persona, fire_schedule as fire_persona_schedule, fire_trigger as fire_persona_trigger,
375 format_ms as format_persona_ms, now_ms as persona_now_ms, parse_rfc3339_ms as parse_persona_ms,
376 pause_persona, persona_status, record_persona_spend, register_persona_supervision_sink,
377 register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
378 resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
379 PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
380 PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
381 PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
382 PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
383 PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
384 PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
385 PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
386 PersonaValueSinkRegistration, StageDecl, StageExit, PERSONA_RUNTIME_TOPIC,
387};
388pub use provenance::{
389 build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
390 ReceiptBuildOptions, ReceiptVerificationReport,
391};
392pub use receipts::{
393 Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
394 RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
395};
396pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
397pub use runtime_limits::{
398 RuntimeLimitDescription, RuntimeLimitEntry, RuntimeLimits, RuntimeLimitsReport,
399 RUNTIME_LIMIT_DESCRIPTIONS,
400};
401pub use schema::json_to_vm_value;
402pub use sessions::{
403 CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
404 TouchSession, SESSIONS_TOPIC,
405};
406pub use stdlib::fs::ignore_policy;
412#[doc(hidden)]
413pub use stdlib::fs::invalidate_cached_file_text;
414pub use stdlib::hitl::{
415 append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
416 HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
417};
418pub use stdlib::host::turn_cache as host_turn_cache;
420pub use stdlib::host::{
421 clear_host_call_bridge, dispatch_host_operation, host_call_ready, install_host_call_bridge,
422 set_host_call_bridge, HostCallBridge, HostCallBridgeGuard, HostCallDispatchFuture,
423};
424pub use stdlib::http_response::{
425 parse_envelope as parse_http_envelope, HttpEnvelope, HttpHeaderValue, WsUpgradeSpec,
426 HTTP_RESPONSE_TAG_KEY, HTTP_RESPONSE_TAG_VERSION,
427};
428#[cfg(feature = "postgres")]
429pub use stdlib::install_shared_pool_registry;
430pub use stdlib::io::{
431 reserve_stdio_for_current_thread, set_stdout_passthrough, take_stderr_buffer,
432 StdioReservationGuard,
433};
434pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
435pub use stdlib::observability::install_default_backend as install_obs_default_backend;
436pub use stdlib::secret_scan::{
437 append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
438 SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
439};
440pub use stdlib::template::{
441 lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
442 PromptSourceSpan, PromptSpanKind,
443};
444pub use stdlib::waitpoint::{
445 process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
446};
447pub use stdlib::workflow_messages::{
448 workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
449 workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
450 workflow_update_for_base, WorkflowMailboxState,
451};
452pub use stdlib::{
453 register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
454};
455pub use store::register_store_builtins;
456pub use tenant::{
457 tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
458 TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
459 TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
460 TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
461 TENANT_SECRET_NAMESPACE_PREFIX,
462};
463pub use triggers::{
464 append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
465 binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
466 clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
467 dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
468 install_manifest_triggers, install_orchestrator_budget, micros_to_usd,
469 note_autonomous_decision, note_orchestrator_budget_cost, orchestrator_budget_would_exceed,
470 parse_flow_control_duration, pause, pin_trigger_binding, provider_metadata,
471 record_predicate_cost_sample, redact_headers, register_provider_schemas,
472 registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
473 reset_provider_catalog, resolve_live_or_as_of, resolve_live_trigger_binding,
474 resolve_trigger_binding_as_of, resume, run_trigger_harness_fixture, scheduler_in_flight_by_key,
475 scheduler_ready_stats_by_key, snapshot_dispatcher_stats, snapshot_orchestrator_budget,
476 snapshot_trigger_bindings, unpin_trigger_binding, usd_to_micros, worker_claims_topic_name,
477 worker_job_topic_name, worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest,
478 DispatchError, DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport,
479 DispatcherStatsSnapshot, ExtensionProviderPayload, FairnessKey, HeaderRedactionPolicy,
480 InboxIndex, OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog,
481 ProviderCatalogError, ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload,
482 ProviderRuntimeMetadata, ProviderSchema, ProviderSecretRequirement, ReadyKeyStats,
483 RecordedTriggerBinding, RetryPolicy, SchedulableJob, SchedulerKeyStat, SchedulerPolicy,
484 SchedulerSnapshot, SchedulerState, SchedulerStrategy, SignatureStatus,
485 SignatureVerificationMetadata, StreamEventPayload, TenantId, TraceId, TriggerBatchConfig,
486 TriggerBindingSnapshot, TriggerBindingSource, TriggerBindingSpec,
487 TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig, TriggerDebounceConfig,
488 TriggerDispatchOutcome, TriggerEvent, TriggerEventId, TriggerExpressionSpec,
489 TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult, TriggerId,
490 TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
491 TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
492 TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
493 WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
494 WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
495 DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
496 TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
497 TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
498 TRIGGER_INBOX_OBSERVABILITY_TOPIC, TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC,
499 TRIGGER_TEST_FIXTURES, WORKER_QUEUE_CATALOG_TOPIC,
500};
501pub use trust_graph::{
502 append_active_scope_attenuation_alert, append_active_trust_record,
503 append_scope_attenuation_alert, append_trust_record, export_trust_chain,
504 group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
505 query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
506 summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
507 TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
508 TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord,
509 TrustRecordActionKind, TrustScore, TrustTraceGroup, METADATA_KEY_ACTOR_CHAIN,
510 METADATA_KEY_ACTOR_CHAIN_ALERT, METADATA_KEY_EFFECTS_GRANT, METADATA_KEY_EFFECTS_USED,
511 METADATA_KEY_PARENT_RECORD_ID, OPENTRUSTGRAPH_ACCEPTED_SCHEMAS, OPENTRUSTGRAPH_CHAIN_SCHEMA_V0,
512 OPENTRUSTGRAPH_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0_1, TRUST_ACTION_RELEASE,
513 TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_TOPIC_PREFIX,
514 TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
515};
516pub use value::*;
517pub use vm::*;
518
519#[cfg(feature = "vm-bench-internals")]
520#[doc(hidden)]
521pub mod bench_internals;
522
523pub fn compile_source(source: &str) -> Result<Chunk, String> {
528 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
529 Compiler::new().compile(&program).map_err(|e| e.to_string())
530}
531
532pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
537 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
538 let has_pipeline = program.iter().any(|sn| {
539 let (_, inner) = harn_parser::peel_attributes(sn);
540 matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
541 });
542 if !has_pipeline {
543 return Err(format!("no pipeline named `{pipeline_name}` in source"));
544 }
545 Compiler::new()
546 .compile_named(&program, pipeline_name)
547 .map_err(|e| e.to_string())
548}
549
550pub struct TypeSchemaResolver {
556 compiler: compiler::Compiler,
557 nominal_types: std::collections::BTreeMap<String, SchemaNominalType>,
558}
559
560#[derive(Clone)]
561enum SchemaNominalType {
562 Struct {
563 type_params: Vec<harn_parser::TypeParam>,
564 fields: Vec<harn_parser::StructField>,
565 },
566 Enum {
567 type_params: Vec<harn_parser::TypeParam>,
568 variants: Vec<harn_parser::EnumVariant>,
569 },
570}
571
572impl TypeSchemaResolver {
573 pub fn empty() -> Self {
575 Self {
576 compiler: compiler::Compiler::new(),
577 nominal_types: std::collections::BTreeMap::new(),
578 }
579 }
580
581 pub fn from_program(program: &[harn_parser::SNode]) -> Self {
583 let mut compiler = compiler::Compiler::new();
584 compiler.collect_type_aliases(program);
585 let mut nominal_types = std::collections::BTreeMap::new();
586 for node in program {
587 let (_, declaration) = harn_parser::peel_attributes(node);
588 match &declaration.node {
589 harn_parser::Node::StructDecl {
590 name,
591 type_params,
592 fields,
593 ..
594 } => {
595 nominal_types.insert(
596 name.clone(),
597 SchemaNominalType::Struct {
598 type_params: type_params.clone(),
599 fields: fields.clone(),
600 },
601 );
602 }
603 harn_parser::Node::EnumDecl {
604 name,
605 type_params,
606 variants,
607 ..
608 } => {
609 nominal_types.insert(
610 name.clone(),
611 SchemaNominalType::Enum {
612 type_params: type_params.clone(),
613 variants: variants.clone(),
614 },
615 );
616 }
617 _ => {}
618 }
619 }
620 Self {
621 compiler,
622 nominal_types,
623 }
624 }
625
626 pub fn json_schema_for_type_expr(
629 &self,
630 type_expr: &harn_parser::TypeExpr,
631 ) -> Option<serde_json::Value> {
632 self.json_schema_for_type_expr_inner(type_expr, &mut Vec::new())
633 }
634
635 pub fn json_schema_for_input_type_expr(
640 &self,
641 type_expr: &harn_parser::TypeExpr,
642 ) -> Option<serde_json::Value> {
643 let expanded = self.compiler.expand_alias(type_expr);
644 let schema = compiler::Compiler::type_expr_to_schema_value(&expanded)?;
645 let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
646 Some(llm::vm_value_to_json(&json_schema))
647 }
648
649 fn json_schema_for_type_expr_inner(
650 &self,
651 type_expr: &harn_parser::TypeExpr,
652 resolving: &mut Vec<harn_parser::TypeExpr>,
653 ) -> Option<serde_json::Value> {
654 const MAX_SCHEMA_TYPE_NEST: usize = 128;
655 let expanded = self.compiler.expand_alias(type_expr);
656 if resolving.len() >= MAX_SCHEMA_TYPE_NEST || resolving.contains(&expanded) {
657 return Some(serde_json::json!({}));
658 }
659
660 if let Some((name, args)) = nominal_reference(&expanded) {
661 if let Some(declaration) = self.nominal_types.get(name).cloned() {
662 resolving.push(expanded.clone());
663 let schema = self.json_schema_for_nominal(name, &declaration, args, resolving);
664 resolving.pop();
665 return schema;
666 }
667 }
668
669 if !contains_nominal_reference(&expanded, &self.nominal_types) {
670 let schema = compiler::Compiler::type_expr_to_schema_value(&expanded)?;
671 let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
672 return Some(llm::vm_value_to_json(&json_schema));
673 }
674
675 use harn_parser::TypeExpr;
676 match expanded {
677 TypeExpr::Shape(fields) => {
678 let mut properties = serde_json::Map::new();
679 let mut required = Vec::new();
680 for field in fields {
681 let mut field_schema =
682 self.json_schema_for_type_expr_inner(&field.type_expr, resolving)?;
683 if field.optional {
684 field_schema = serde_json::json!({
685 "anyOf": [field_schema, {"type": "null"}],
686 });
687 } else {
688 required.push(serde_json::Value::String(field.name.clone()));
689 }
690 properties.insert(field.name, field_schema);
691 }
692 Some(serde_json::json!({
693 "type": "object",
694 "properties": properties,
695 "required": required,
696 }))
697 }
698 TypeExpr::List(inner) => Some(serde_json::json!({
699 "type": "array",
700 "items": self.json_schema_for_type_expr_inner(&inner, resolving)?,
701 })),
702 TypeExpr::Tuple(elements) => {
703 let prefix_items = elements
704 .iter()
705 .map(|element| self.json_schema_for_type_expr_inner(element, resolving))
706 .collect::<Option<Vec<_>>>()?;
707 Some(serde_json::json!({
708 "type": "array",
709 "prefixItems": prefix_items,
710 "items": false,
711 "minItems": elements.len(),
712 "maxItems": elements.len(),
713 }))
714 }
715 TypeExpr::DictType(key, value) if matches!(key.as_ref(), TypeExpr::Named(name) if name == "string") => {
716 Some(serde_json::json!({
717 "type": "object",
718 "additionalProperties": self
719 .json_schema_for_type_expr_inner(&value, resolving)?,
720 }))
721 }
722 TypeExpr::Union(members) => Some(serde_json::json!({
723 "anyOf": members
724 .iter()
725 .map(|member| self.json_schema_for_type_expr_inner(member, resolving))
726 .collect::<Option<Vec<_>>>()?,
727 })),
728 TypeExpr::Intersection(members) => Some(serde_json::json!({
729 "allOf": members
730 .iter()
731 .map(|member| self.json_schema_for_type_expr_inner(member, resolving))
732 .collect::<Option<Vec<_>>>()?,
733 })),
734 TypeExpr::Owned(inner) => self.json_schema_for_type_expr_inner(&inner, resolving),
735 _ => None,
736 }
737 }
738
739 fn json_schema_for_nominal(
740 &self,
741 name: &str,
742 declaration: &SchemaNominalType,
743 args: &[harn_parser::TypeExpr],
744 resolving: &mut Vec<harn_parser::TypeExpr>,
745 ) -> Option<serde_json::Value> {
746 let type_params = match declaration {
747 SchemaNominalType::Struct { type_params, .. }
748 | SchemaNominalType::Enum { type_params, .. } => type_params,
749 };
750 if type_params.len() != args.len() {
751 return None;
752 }
753 let bindings = type_params
754 .iter()
755 .zip(args.iter().cloned())
756 .map(|(param, arg)| (param.name.clone(), arg))
757 .collect::<std::collections::BTreeMap<_, _>>();
758
759 match declaration {
760 SchemaNominalType::Struct { fields, .. } => {
761 let fields = fields
762 .iter()
763 .map(|field| harn_parser::ShapeField {
764 name: field.name.clone(),
765 type_expr: field
766 .type_expr
767 .as_ref()
768 .map(|ty| harn_parser::substitute_type_expr(ty, &bindings))
769 .unwrap_or_else(|| harn_parser::TypeExpr::Named("unknown".into())),
770 optional: field.optional,
771 span: field.span,
772 })
773 .collect();
774 self.json_schema_for_type_expr_inner(
775 &harn_parser::TypeExpr::Shape(fields),
776 resolving,
777 )
778 }
779 SchemaNominalType::Enum { variants, .. } => {
780 let branches = variants
781 .iter()
782 .map(|variant| {
783 let prefix_items = variant
784 .fields
785 .iter()
786 .map(|field| {
787 let type_expr = field.type_expr.as_ref()?;
788 let instantiated =
789 harn_parser::substitute_type_expr(type_expr, &bindings);
790 let mut schema =
791 self.json_schema_for_type_expr_inner(&instantiated, resolving)?;
792 if let serde_json::Value::Object(object) = &mut schema {
793 object.insert(
794 "title".to_string(),
795 serde_json::Value::String(field.name.clone()),
796 );
797 }
798 Some(schema)
799 })
800 .collect::<Option<Vec<_>>>()?;
801 Some(serde_json::json!({
802 "type": "object",
803 "properties": {
804 "enum": {"const": name},
805 "variant": {"const": variant.name},
806 "fields": {
807 "type": "array",
808 "prefixItems": prefix_items,
809 "items": false,
810 "minItems": variant.fields.len(),
811 "maxItems": variant.fields.len(),
812 },
813 },
814 "required": ["enum", "variant", "fields"],
815 "additionalProperties": false,
816 }))
817 })
818 .collect::<Option<Vec<_>>>()?;
819 Some(serde_json::json!({"oneOf": branches}))
820 }
821 }
822 }
823
824 pub fn json_schema_for_typed_params(
827 &self,
828 params: &[harn_parser::TypedParam],
829 ) -> serde_json::Value {
830 let mut properties = serde_json::Map::new();
831 let mut required = Vec::new();
832
833 for param in params {
834 let param_schema = param
835 .type_expr
836 .as_ref()
837 .and_then(|type_expr| self.json_schema_for_input_type_expr(type_expr))
838 .unwrap_or_else(|| serde_json::json!({}));
839 if param.default_value.is_none() {
840 required.push(serde_json::Value::String(param.name.clone()));
841 }
842 properties.insert(param.name.clone(), param_schema);
843 }
844
845 let mut schema = serde_json::Map::new();
846 schema.insert(
847 "type".to_string(),
848 serde_json::Value::String("object".to_string()),
849 );
850 schema.insert(
851 "properties".to_string(),
852 serde_json::Value::Object(properties),
853 );
854 if !required.is_empty() {
855 schema.insert("required".to_string(), serde_json::Value::Array(required));
856 }
857 serde_json::Value::Object(schema)
858 }
859}
860
861fn nominal_reference(
862 type_expr: &harn_parser::TypeExpr,
863) -> Option<(&str, &[harn_parser::TypeExpr])> {
864 match type_expr {
865 harn_parser::TypeExpr::Named(name) => Some((name, &[])),
866 harn_parser::TypeExpr::Applied { name, args } => Some((name, args)),
867 _ => None,
868 }
869}
870
871fn contains_nominal_reference(
872 type_expr: &harn_parser::TypeExpr,
873 nominal_types: &std::collections::BTreeMap<String, SchemaNominalType>,
874) -> bool {
875 use harn_parser::TypeExpr;
876 match type_expr {
877 TypeExpr::Named(name) => nominal_types.contains_key(name),
878 TypeExpr::Applied { name, args } => {
879 nominal_types.contains_key(name)
880 || args
881 .iter()
882 .any(|arg| contains_nominal_reference(arg, nominal_types))
883 }
884 TypeExpr::Union(types) | TypeExpr::Intersection(types) | TypeExpr::Tuple(types) => types
885 .iter()
886 .any(|ty| contains_nominal_reference(ty, nominal_types)),
887 TypeExpr::Shape(fields) => fields
888 .iter()
889 .any(|field| contains_nominal_reference(&field.type_expr, nominal_types)),
890 TypeExpr::OpenShape { fields, rests } => {
891 fields
892 .iter()
893 .any(|field| contains_nominal_reference(&field.type_expr, nominal_types))
894 || rests
895 .iter()
896 .any(|rest| contains_nominal_reference(rest, nominal_types))
897 }
898 TypeExpr::List(inner)
899 | TypeExpr::Iter(inner)
900 | TypeExpr::Generator(inner)
901 | TypeExpr::Stream(inner)
902 | TypeExpr::Owned(inner) => contains_nominal_reference(inner, nominal_types),
903 TypeExpr::DictType(key, value) => {
904 contains_nominal_reference(key, nominal_types)
905 || contains_nominal_reference(value, nominal_types)
906 }
907 TypeExpr::FnType {
908 params,
909 return_type,
910 } => {
911 params
912 .iter()
913 .any(|param| contains_nominal_reference(param, nominal_types))
914 || contains_nominal_reference(return_type, nominal_types)
915 }
916 TypeExpr::Never | TypeExpr::LitString(_) | TypeExpr::LitInt(_) => false,
917 }
918}
919
920pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
924 TypeSchemaResolver::empty().json_schema_for_type_expr(type_expr)
925}
926
927pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
928 TypeSchemaResolver::empty().json_schema_for_typed_params(params)
929}
930
931#[cfg(test)]
932mod schema_alias_resolver_tests {
933 use super::*;
934
935 fn fn_params_schema(src: &str) -> serde_json::Value {
936 let program = harn_parser::parse_source(src).expect("parse test source");
937 let resolver = TypeSchemaResolver::from_program(&program);
938 for node in &program {
939 let (_, inner) = harn_parser::peel_attributes(node);
940 if let harn_parser::Node::FnDecl { params, .. } = &inner.node {
941 return resolver.json_schema_for_typed_params(params);
942 }
943 }
944 panic!("no fn decl in test source");
945 }
946
947 #[test]
948 fn named_shape_alias_projects_like_inline_shape() {
949 let inline = fn_params_schema("pub fn f(p: {kind: string, path: string}) {}");
950 let aliased =
951 fn_params_schema("type Src = {kind: string, path: string}\npub fn f(p: Src) {}");
952 assert_eq!(
953 aliased, inline,
954 "a named shape alias must project the same inputSchema as its inline shape",
955 );
956 assert_ne!(
957 aliased["properties"]["p"],
958 serde_json::json!({}),
959 "the alias parameter must not erase to an empty schema",
960 );
961 }
962
963 #[test]
964 fn literal_union_alias_projects_json_enum() {
965 let schema = fn_params_schema("type Kind = \"local\" | \"ssh\"\npub fn f(p: Kind) {}");
966 let p = &schema["properties"]["p"];
967 assert_eq!(p["type"], "string");
968 assert_eq!(p["enum"], serde_json::json!(["local", "ssh"]));
969 }
970
971 #[test]
972 fn unknown_named_type_still_erases_to_empty() {
973 let schema = fn_params_schema("pub fn f(p: Unknown) {}");
975 assert_eq!(schema["properties"]["p"], serde_json::json!({}));
976 }
977}
978
979fn reset_llm_state_for_thread_reset() {
980 llm::reset_llm_state();
981 #[cfg(test)]
982 reset_thread_local_state_test_hooks::before_llm_global_reset();
983 llm::reset_rate_limit_registry();
986 llm_config::clear_user_overrides();
987 llm_config::clear_runtime_provider_endpoint_overrides();
988}
989
990#[cfg(test)]
991mod reset_thread_local_state_test_hooks {
992 use std::sync::{Arc, Mutex, OnceLock};
993
994 type Hook = Arc<dyn Fn() + Send + Sync + 'static>;
995
996 static BEFORE_LLM_GLOBAL_RESET: OnceLock<Mutex<Option<Hook>>> = OnceLock::new();
997
998 fn before_llm_global_reset_hook() -> &'static Mutex<Option<Hook>> {
999 BEFORE_LLM_GLOBAL_RESET.get_or_init(|| Mutex::new(None))
1000 }
1001
1002 pub(crate) struct HookGuard;
1003
1004 impl Drop for HookGuard {
1005 fn drop(&mut self) {
1006 let mut hook = before_llm_global_reset_hook()
1007 .lock()
1008 .unwrap_or_else(std::sync::PoisonError::into_inner);
1009 *hook = None;
1010 }
1011 }
1012
1013 pub(crate) fn install_before_llm_global_reset(hook: Hook) -> HookGuard {
1014 let mut slot = before_llm_global_reset_hook()
1015 .lock()
1016 .unwrap_or_else(std::sync::PoisonError::into_inner);
1017 *slot = Some(hook);
1018 HookGuard
1019 }
1020
1021 pub(crate) fn before_llm_global_reset() {
1022 let hook = before_llm_global_reset_hook()
1023 .lock()
1024 .unwrap_or_else(std::sync::PoisonError::into_inner)
1025 .clone();
1026 if let Some(hook) = hook {
1027 hook();
1028 }
1029 }
1030}
1031
1032pub fn reset_thread_local_state() {
1034 #[cfg(test)]
1035 {
1036 let _guard = llm::env_guard();
1041 reset_llm_state_for_thread_reset();
1042 }
1043 #[cfg(not(test))]
1044 reset_llm_state_for_thread_reset();
1045
1046 http::reset_http_state();
1047 channels::reset_channel_state();
1048 event_log::reset_active_event_log();
1049 egress::clear_explicit_egress_policy_requirement_for_host();
1050 egress::clear_ssrf_guard_requirement_for_host();
1051 stdlib::reset_stdlib_state();
1052 connectors::clear_active_connector_clients();
1053 orchestration::clear_runtime_hooks();
1054 orchestration::clear_file_edit_queue();
1055 orchestration::clear_execution_policy_stacks();
1056 orchestration::clear_command_policies();
1057 orchestration::clear_pipeline_on_finish();
1058 orchestration::reset_lifecycle_receipt_registry();
1059 orchestration::agent_inbox::reset();
1060 tool_call_cancellations::reset_registry();
1061 redact::clear_policy_stack();
1062 security::reset_thread_state();
1063 triggers::clear_dispatcher_state();
1064 triggers::clear_trigger_registry();
1065 events::reset_event_sinks();
1066 tracing::set_tracing_enabled(false);
1067 tracing::reset_tracing();
1068 agent_events::reset_all_sinks();
1080 agent_sessions::reset_session_store();
1081 mcp_registry::reset();
1082 mcp_host::reset_for_tests();
1083 call_budget::reset_call_budget_state();
1084 clock_mock::leak_audit::reset();
1085}
1086
1087#[cfg(test)]
1088mod reset_leak_tests {
1089 use super::*;
1095 use crate::value::VmValue;
1096
1097 #[test]
1098 fn reset_drains_pending_file_edit_notifications() {
1099 orchestration::queue_file_edited("stale.harn", serde_json::json!({"operation": "write"}));
1100
1101 reset_thread_local_state();
1102
1103 assert!(
1104 orchestration::drain_file_edits().is_empty(),
1105 "a later VM run must not receive file edits queued by the previous run"
1106 );
1107 }
1108
1109 #[test]
1118 fn builtin_profile_recording_ends_with_its_run_not_with_a_global_reset() {
1119 let _lock = builtin_profile::test_lock()
1120 .lock()
1121 .unwrap_or_else(std::sync::PoisonError::into_inner);
1122 let recording = builtin_profile::enable();
1123 builtin_profile::record("run_shell", std::time::Duration::from_millis(5));
1124 assert!(builtin_profile::is_enabled());
1125 assert!(!builtin_profile::snapshot().is_empty());
1126
1127 reset_thread_local_state();
1128
1129 assert!(
1130 builtin_profile::is_enabled(),
1131 "an unrelated global reset must not disarm an in-flight profiled run"
1132 );
1133 assert!(
1134 !builtin_profile::snapshot().is_empty(),
1135 "an unrelated global reset must not drop totals the run still owns"
1136 );
1137
1138 drop(recording);
1139
1140 assert!(
1141 !builtin_profile::is_enabled(),
1142 "a profiled run must not leave the recorder on for the next one"
1143 );
1144 assert!(
1145 builtin_profile::snapshot().is_empty(),
1146 "builtin totals must be empty once the run's guard drops"
1147 );
1148 }
1149
1150 #[test]
1155 fn reset_drains_session_changed_paths() {
1156 let session = "sess-leak";
1157 agent_sessions::open_or_create(Some(session.to_string()));
1158 agent_sessions::record_session_changed_path(session, "/tmp/written-by-a-dead-run.txt");
1159 assert!(!agent_sessions::session_changed_paths(session).is_empty());
1160 reset_thread_local_state();
1161 assert!(
1162 agent_sessions::session_changed_paths(session).is_empty(),
1163 "a receipt must not inherit an abandoned session's writes"
1164 );
1165 }
1166
1167 #[test]
1168 fn reset_drains_agent_inbox() {
1169 orchestration::agent_inbox::reset();
1170 orchestration::agent_inbox::push("sess-2660", "note", "leak", "test");
1171 assert!(orchestration::agent_inbox::session_count() > 0);
1172 reset_thread_local_state();
1173 assert_eq!(
1174 orchestration::agent_inbox::session_count(),
1175 0,
1176 "agent_inbox must be empty after reset"
1177 );
1178 }
1179
1180 #[test]
1181 fn reset_drains_tool_call_cancellation_registry() {
1182 tool_call_cancellations::reset_registry();
1183 let registered = tool_call_cancellations::register("sess-2660", "call-1", "tool");
1186 if let Some((_handle, guard)) = registered {
1187 std::mem::forget(guard);
1188 }
1189 assert!(tool_call_cancellations::registry_len() > 0);
1190 reset_thread_local_state();
1191 assert_eq!(
1192 tool_call_cancellations::registry_len(),
1193 0,
1194 "tool-call cancellation registry must be empty after reset"
1195 );
1196 }
1197
1198 #[test]
1199 fn reset_drains_routing_policy_registry() {
1200 llm::routing::clear_policy_registry();
1201 let mut config: crate::value::DictMap = crate::value::DictMap::new();
1202 config.insert(
1203 crate::value::intern_key("chain"),
1204 VmValue::List(std::sync::Arc::new(vec![VmValue::String(
1205 arcstr::ArcStr::from("mock:mock"),
1206 )])),
1207 );
1208 llm::routing::build_routing_policy(&config).expect("intern a routing policy");
1209 assert!(llm::routing::policy_registry_len() > 0);
1210 reset_thread_local_state();
1211 assert_eq!(
1212 llm::routing::policy_registry_len(),
1213 0,
1214 "routing policy registry must be empty after reset"
1215 );
1216 }
1217
1218 #[test]
1219 fn reset_holds_llm_env_guard_while_wiping_llm_globals() {
1220 let observed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
1221 let observed_hook = std::sync::Arc::clone(&observed);
1222 let _hook = reset_thread_local_state_test_hooks::install_before_llm_global_reset(
1223 std::sync::Arc::new(move || {
1224 assert!(
1225 matches!(
1226 llm::env_lock().try_lock(),
1227 Err(std::sync::TryLockError::WouldBlock)
1228 ),
1229 "reset_thread_local_state must hold env_guard before wiping LLM globals"
1230 );
1231 observed_hook.store(true, std::sync::atomic::Ordering::SeqCst);
1232 }),
1233 );
1234
1235 reset_thread_local_state();
1236 assert!(
1237 observed.load(std::sync::atomic::Ordering::SeqCst),
1238 "LLM global reset hook should have run"
1239 );
1240 }
1241}