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_sessions;
32pub mod agent_transcript_budget;
33pub mod atomic_io;
34pub mod autonomy;
35#[cfg(feature = "cloud-aws")]
36pub(crate) mod aws_sigv4;
37#[cfg(not(feature = "cloud-aws"))]
38#[path = "aws_sigv4_disabled.rs"]
39pub(crate) mod aws_sigv4;
40pub mod boundary;
41pub mod bridge;
42pub use bridge::{
43 inject_leading_authorities, inject_leading_authority, leading_authority_param_count,
44};
45pub mod builtin_profile;
46pub mod bytecode_cache;
47pub mod call_budget;
48pub mod canonical_json;
49pub mod channel_guardrails;
50pub mod channels;
51pub mod checkpoint;
52mod chunk;
53mod compiler;
54pub mod composition;
55pub mod conditional_replace;
56pub mod config;
57pub mod connectors;
58pub mod context_manifest;
59pub mod corrections;
60pub mod coverage;
61pub(crate) mod durable_rate_limit;
62pub mod duration_parse;
63pub mod egress;
64pub mod environment_registry;
65pub mod event_log;
66pub mod events;
67pub mod external_agent;
68pub mod flow;
69pub mod harness;
70pub mod harness_auth;
71pub(crate) mod harness_crypto;
72pub mod harness_net;
73pub mod harness_system;
74pub mod harness_tenant;
75pub mod host_attachments;
76mod http;
77pub mod jsonrpc;
78pub(crate) mod limits;
79pub mod linked_program;
80pub mod llm;
81pub mod llm_config;
82pub mod mcp;
83pub mod mcp_allowlist;
84pub mod mcp_auth;
85pub mod mcp_bulk_auth;
86pub mod mcp_card;
87pub mod mcp_client_roots;
88pub mod mcp_elicit;
89pub mod mcp_host;
90pub mod mcp_identity;
91pub mod mcp_input;
92pub mod mcp_json_discovery;
93pub mod mcp_oauth;
94pub mod mcp_presets;
95pub mod mcp_progress;
96pub mod mcp_protocol;
97pub mod mcp_registry;
98pub mod mcp_sampling;
99pub mod mcp_server;
100pub mod metadata;
101pub mod module_artifact;
102pub mod module_source;
103pub mod observability;
104pub mod op_interrupt;
105pub mod orchestration;
106mod persistent_state;
107pub mod personas;
108pub mod portable;
109mod prepared_module;
110pub mod prepared_run;
111pub mod process_sandbox;
112pub mod profile;
113pub mod provenance;
114pub mod provider_catalog;
115pub mod receipts;
116pub mod record_filter;
117pub mod redact;
118pub mod run_events;
119pub mod runtime_context;
120pub(crate) mod runtime_guards;
121pub mod runtime_limits;
122pub mod runtime_paths;
123pub(crate) mod runtime_sqlite;
124pub mod schema;
125pub(crate) mod secret_patterns;
126pub mod secrets;
127pub mod security;
128pub mod session_bundle;
129pub mod session_timeline;
130pub mod sessions;
131pub(crate) mod shared_state;
132pub mod shells;
133pub mod skills;
134pub mod stdlib;
135pub mod stdlib_modules;
136pub mod step_runtime;
137pub mod store;
138pub(crate) mod synchronization;
139pub mod tenant;
140pub(crate) mod term;
141pub(crate) mod test_env;
142pub mod testbench;
143pub mod text;
144pub mod text_diff;
145pub mod tool_annotations;
146pub mod tool_call_cancellations;
147pub mod tool_surface;
148pub mod tracing;
149pub mod triggers;
150pub mod trust_graph;
151pub(crate) mod url_encoding;
152pub mod user_dirs;
153
154pub fn initialize_runtime_assets() {
162 secret_patterns::initialize_default_secret_patterns();
163}
164
165pub fn initialize_runtime() -> Result<(), environment_registry::EnvironmentValidationError> {
168 environment_registry::validate_startup_environment()?;
169 initialize_runtime_assets();
170 Ok(())
171}
172
173pub mod clock_mock {
178 pub(crate) use crate::triggers::test_util::clock::scope_capability_clock;
179 pub use crate::triggers::test_util::clock::{
180 active_clock, active_mock_clock, advance, clear_overrides, install_override, instant_now,
181 is_mocked, now_ms, now_utc, sleep, ClockInstant, ClockOverrideGuard, MockClock,
182 };
183
184 pub mod leak_audit {
188 pub use crate::triggers::test_util::clock_leak::{
189 drain, enter_scope, install_scope, instant_now, reset, snapshot, wall_now, ClockLeak,
190 ClockLeakScope, ClockLeakScopeGuard,
191 };
192 }
193}
194
195pub(crate) mod text_index;
196pub mod typecheck;
197pub mod value;
198pub mod verification;
199pub mod visible_text;
200mod vm;
201pub(crate) mod wait_for_graph;
202pub mod waitpoints;
203pub mod windows_path;
204pub mod workspace_anchor;
205pub mod workspace_path;
206
207pub use persistent_state::{register_persistent_state_builtins_at_root, PersistentStateRoot};
208pub use prepared_module::{PreparedModuleCache, PreparedModuleCacheStats};
209
210pub use actor_chain::{
211 ActorChain, ActorChainEntry, ActorChainError, Principal, ScopeAttenuationMode,
212 ScopeAttenuationPolicy, ScopeAttenuationViolation,
213};
214pub use call_budget::{
215 charge_mcp_call, charge_pg_query, install_mcp_call_budget, install_pg_query_budget,
216 McpCallBudgetGuard, PgQueryBudgetGuard,
217};
218pub use checkpoint::register_checkpoint_builtins;
219pub use chunk::*;
220pub use compiler::*;
221pub use connectors::{
222 active_connector_client, active_metrics_registry, clear_active_connector_clients,
223 clear_active_metrics_registry, connector_export_denied_builtin_reason,
224 connector_export_denied_harness_method_reason, connector_export_effect_class,
225 cron::{CatchupMode, CronConnector},
226 default_connector_export_policy,
227 harn_module::{
228 load_contract as load_harn_connector_contract, HarnConnector, HarnConnectorContract,
229 },
230 hmac::{verify_hmac_signed, SIGNATURE_VERIFY_AUDIT_TOPIC},
231 install_active_connector_clients, install_active_metrics_registry,
232 postprocess_normalized_event, ActivationHandle, ClientError, Connector, ConnectorClient,
233 ConnectorCtx, ConnectorError, ConnectorExportEffectClass, ConnectorHttpResponse,
234 ConnectorMetricsSnapshot, ConnectorNormalizeResult, ConnectorRegistry, GenericWebhookConnector,
235 HarnConnectorEffectPolicies, MetricsRegistry, PostNormalizeOutcome, ProviderPayloadSchema,
236 RateLimitConfig, RateLimiterFactory, RawInbound, StreamConnector, TriggerBinding, TriggerKind,
237 TriggerRegistry, WebhookSignatureVariant,
238};
239pub use corrections::{
240 append_correction_record, apply_corrections_to_policy, correction_query_filters_from_json,
241 correction_record_from_json, policy_with_corrections, query_correction_records,
242 CorrectionQueryFilters, CorrectionRecord, CorrectionScope, CORRECTIONS_TOPIC,
243 CORRECTION_EVENT_KIND, CORRECTION_SCHEMA_V0,
244};
245pub use harn_kernel::BuiltinId;
246pub use harness::{
247 DenyEvent, Harness, HarnessAgent, HarnessCall, HarnessChannels, HarnessClock, HarnessEnv,
248 HarnessFs, HarnessKind, HarnessLlm, HarnessMemory, HarnessNet, HarnessObs, HarnessPostgres,
249 HarnessProcess, HarnessRandom, HarnessSecrets, HarnessSqlite, HarnessStdio, HarnessSystem,
250 HarnessTenant, HarnessTerm, HarnessTesting, MockHarnessBuilder, VmHarness,
251};
252pub use harness_auth::{
253 current_auth_principal, enter_auth_principal, AuthPrincipal, AuthPrincipalScopeGuard,
254 MISSING_PRINCIPAL_MESSAGE,
255};
256pub use harness_net::{
257 bypass_enabled as net_policy_bypass_enabled, NetMatcher, NetPolicy, NetPolicyAudit,
258 NetPolicyDecision, NetPolicyDefault, NetPolicyRule, OnViolation, HARN_NET_POLICY_BYPASS_ENV,
259 NET_POLICY_AUDIT_TOPIC,
260};
261pub use harness_tenant::{
262 current_tenant_id, enter_tenant, TenantScopeGuard, MISSING_TENANT_MESSAGE,
263};
264pub use http::{register_http_builtins, reset_http_state};
265pub use llm::register_llm_builtins;
266pub use llm::trigger_predicate::TriggerPredicateBudget;
267pub use llm::{
268 current_agent_session_id, install_llm_cost_budget, install_llm_token_budget,
269 peek_llm_cost_budget, peek_llm_token_budget, register_session_end_hook, set_llm_cost_budget,
270 set_llm_token_budget, LlmBudgetGuard, LlmTokenBudgetGuard, SessionEndHookRegistration,
271};
272pub use mcp::{connect_mcp_server_from_json, connect_mcp_server_from_spec, register_mcp_builtins};
273pub use mcp_allowlist::{
274 build_catalog as build_mcp_catalog, catalog_for_request as mcp_catalog_for_request,
275 AdvertisedItem as McpAdvertisedItem, CatalogRequest as McpCatalogRequest, McpAllowlist,
276 McpAllowlistItem, McpCatalog, McpCatalogItem, McpCatalogServer, McpItemKind,
277 MCP_ALLOWLIST_SCHEMA_VERSION,
278};
279pub use mcp_card::{fetch_server_card, load_server_card_from_path, CardError};
280pub use mcp_host::{
281 cache_stats as mcp_host_cache_stats, set_allowlist as set_mcp_host_allowlist,
282 AllowlistDecision as McpHostAllowlistDecision, AllowlistGuard as McpHostAllowlistGuard,
283 BreakerState as McpHostBreakerState, CacheStats as McpHostCacheStats, McpHostStatus,
284 SpawnOptions as McpHostSpawnOptions, SupervisionPolicy as McpHostSupervisionPolicy,
285};
286pub use mcp_registry::{
287 active_handle as mcp_active_handle, ensure_active as mcp_ensure_active,
288 get_registration as mcp_get_registration, install_active as mcp_install_active,
289 is_registered as mcp_is_registered, register_servers as mcp_register_servers,
290 release as mcp_release, reset as mcp_reset_registry, snapshot_status as mcp_snapshot_status,
291 sweep_expired as mcp_sweep_expired, RegisteredMcpServer, RegistryStatus,
292};
293pub use mcp_server::{
294 take_mcp_serve_metadata, take_mcp_serve_prompts, take_mcp_serve_registry,
295 take_mcp_serve_resource_templates, take_mcp_serve_resources, tool_registry_to_mcp_tools,
296 McpServer, McpServerMetadata,
297};
298pub use metadata::register_metadata_builtins;
299pub use observability::audit::{audit_events as audit_obs_events, AuditFinding, AuditFindingKind};
300pub use observability::execution_scope::{
301 current_execution_scope, enter_execution_scope, mint_execution_scope, ExecutionScopeGuard,
302};
303pub use observability::request_id::{current_request_id, enter_request_id, RequestIdScopeGuard};
304pub use orchestration::{
305 benchmark_adapted_replay_pair, benchmark_replay_trace, build_replay_benchmark_report,
306 OpenCodeJsonlAdapter, ReplayBenchmarkCloudIngest, ReplayBenchmarkError,
307 ReplayBenchmarkFixtureReceipt, ReplayBenchmarkFixtureReport, ReplayBenchmarkMetrics,
308 ReplayBenchmarkReport, ReplayBenchmarkSuiteIdentity, ReplayBenchmarkSummary,
309 ReplayCategoryMetric, ReplayDebuggingProxyMetrics, ReplayRuntimeCostMetrics,
310 ReplayTraceAdapter, OPENCODE_JSONL_ADAPTER_ID, OPENCODE_JSONL_ADAPTER_SCHEMA_VERSION,
311 REPLAY_BENCHMARK_CLOUD_INGEST_KIND, REPLAY_BENCHMARK_REPORT_SCHEMA_VERSION,
312};
313pub use orchestration::{
314 canonicalize_run, first_divergence, run_replay_oracle_trace, ReplayAllowlistRule,
315 ReplayDivergence, ReplayExpectation, ReplayOracleError, ReplayOracleReport, ReplayOracleTrace,
316 ReplayTraceRun, ReplayTraceRunCounts, REPLAY_TRACE_SCHEMA_VERSION,
317};
318pub use orchestration::{
319 install_handoff_routes, snapshot_handoff_routes, HandoffRouteConfig,
320 HandoffRouteDecisionRecord, HandoffRouteTargetConfig,
321};
322pub use personas::{
323 disable_persona, fire_schedule as fire_persona_schedule, fire_trigger as fire_persona_trigger,
324 format_ms as format_persona_ms, now_ms as persona_now_ms, parse_rfc3339_ms as parse_persona_ms,
325 pause_persona, persona_status, record_persona_spend, register_persona_supervision_sink,
326 register_persona_value_sink, report_repair_worker_status, restore_persona_checkpoint,
327 resume_persona, PersonaAssignmentStatus, PersonaBudgetPolicy, PersonaBudgetStatus,
328 PersonaCheckpointAction, PersonaCheckpointRestoreOutcome, PersonaCheckpointRestoreRequest,
329 PersonaCheckpointResume, PersonaCheckpointUpdate, PersonaHandoffInboxItem, PersonaLease,
330 PersonaLifecycleState, PersonaQueuePositionUpdate, PersonaQueuedWork, PersonaReceiptUpdate,
331 PersonaRepairWorkerLifecycle, PersonaRepairWorkerStatusUpdate, PersonaRunCost,
332 PersonaRunReceipt, PersonaRuntimeBinding, PersonaStatus, PersonaSupervisionEvent,
333 PersonaSupervisionSink, PersonaSupervisionSinkRegistration, PersonaTriggerEnvelope,
334 PersonaValueEvent, PersonaValueEventKind, PersonaValueReceipt, PersonaValueSink,
335 PersonaValueSinkRegistration, StageDecl, StageExit, PERSONA_RUNTIME_TOPIC,
336};
337pub use provenance::{
338 build_signed_receipt, load_or_generate_agent_signing_key, verify_receipt, ProvenanceReceipt,
339 ReceiptBuildOptions, ReceiptVerificationReport,
340};
341pub use receipts::{
342 Receipt, ReceiptSink, ReceiptStatus, ReceiptValidationError, RedactingReceiptSink,
343 RedactionClass, RECEIPT_SCHEMA_ID, RECEIPT_SCHEMA_JSON, RECEIPT_SCHEMA_VERSION,
344};
345pub use record_filter::{normalize_record_filter_expression, CompiledRecordFilter};
346pub use runtime_limits::{
347 RuntimeLimitDescription, RuntimeLimitEntry, RuntimeLimits, RuntimeLimitsReport,
348 RUNTIME_LIMIT_DESCRIPTIONS,
349};
350pub use schema::json_to_vm_value;
351pub use sessions::{
352 CreateSession, ExpireSession, Session, SessionAttributes, SessionError, SessionStore,
353 TouchSession, SESSIONS_TOPIC,
354};
355pub use stdlib::fs::ignore_policy;
361#[doc(hidden)]
362pub use stdlib::fs::invalidate_cached_file_text;
363pub use stdlib::hitl::{
364 append_hitl_response, ApprovalRequest, HitlHostResponse, HITL_APPROVALS_TOPIC,
365 HITL_DUAL_CONTROL_TOPIC, HITL_ESCALATIONS_TOPIC, HITL_QUESTIONS_TOPIC,
366};
367pub use stdlib::host::turn_cache as host_turn_cache;
369pub use stdlib::host::{
370 clear_host_call_bridge, dispatch_host_operation, host_call_ready, install_host_call_bridge,
371 set_host_call_bridge, HostCallBridge, HostCallBridgeGuard, HostCallDispatchFuture,
372};
373pub use stdlib::http_response::{
374 parse_envelope as parse_http_envelope, HttpEnvelope, HttpHeaderValue, WsUpgradeSpec,
375 HTTP_RESPONSE_TAG_KEY, HTTP_RESPONSE_TAG_VERSION,
376};
377#[cfg(feature = "postgres")]
378pub use stdlib::install_shared_pool_registry;
379pub use stdlib::io::{
380 reserve_stdio_for_current_thread, set_stdout_passthrough, take_stderr_buffer,
381 StdioReservationGuard,
382};
383pub use stdlib::long_running::cancel_handle as cancel_long_running_handle;
384pub use stdlib::observability::install_default_backend as install_obs_default_backend;
385pub use stdlib::secret_scan::{
386 append_secret_scan_audit, audit_secret_scan_active, scan_content as secret_scan_content,
387 SecretFinding, SECRET_SCAN_AUDIT_TOPIC,
388};
389pub use stdlib::template::{
390 lookup_prompt_consumers, lookup_prompt_span, prompt_render_indices, record_prompt_render_index,
391 PromptSourceSpan, PromptSpanKind,
392};
393pub use stdlib::waitpoint::{
394 process_waitpoint_resume_event, service_waitpoints_once, WAITPOINT_RESUME_TOPIC,
395};
396pub use stdlib::workflow_messages::{
397 workflow_pause_for_base, workflow_publish_query_for_base, workflow_query_for_base,
398 workflow_respond_update_for_base, workflow_resume_for_base, workflow_signal_for_base,
399 workflow_update_for_base, WorkflowMailboxState,
400};
401pub use stdlib::{
402 register_agent_stdlib, register_core_stdlib, register_io_stdlib, register_vm_stdlib,
403};
404pub use store::register_store_builtins;
405pub use tenant::{
406 tenant_event_topic_prefix, tenant_secret_namespace, tenant_topic, validate_tenant_id, ApiKeyId,
407 TenantApiKeyRecord, TenantBudget, TenantEventLog, TenantRecord, TenantRegistrySnapshot,
408 TenantResolutionError, TenantScope, TenantSecretProvider, TenantStatus, TenantStore,
409 TENANT_EVENT_TOPIC_PREFIX, TENANT_REGISTRY_DIR, TENANT_REGISTRY_FILE,
410 TENANT_SECRET_NAMESPACE_PREFIX,
411};
412pub use triggers::{
413 append_dispatch_cancel_request, begin_in_flight, binding_autonomy_budget_would_exceed,
414 binding_budget_would_exceed, binding_version_as_of, classify_trigger_dlq_error,
415 clear_dispatcher_state, clear_orchestrator_budget, clear_trigger_registry, drain,
416 dynamic_deregister, dynamic_register, expected_predicate_cost_usd_micros, finish_in_flight,
417 install_manifest_triggers, install_orchestrator_budget, micros_to_usd,
418 note_autonomous_decision, note_orchestrator_budget_cost, orchestrator_budget_would_exceed,
419 parse_flow_control_duration, pause, pin_trigger_binding, provider_metadata,
420 record_predicate_cost_sample, redact_headers, register_provider_schemas,
421 registered_provider_metadata, registered_provider_schema_names, reset_binding_budget_windows,
422 reset_provider_catalog, resolve_live_or_as_of, resolve_live_trigger_binding,
423 resolve_trigger_binding_as_of, resume, run_trigger_harness_fixture, scheduler_in_flight_by_key,
424 scheduler_ready_stats_by_key, snapshot_dispatcher_stats, snapshot_orchestrator_budget,
425 snapshot_trigger_bindings, unpin_trigger_binding, usd_to_micros, worker_claims_topic_name,
426 worker_job_topic_name, worker_response_topic_name, ClaimedWorkerJob, DispatchCancelRequest,
427 DispatchError, DispatchOutcome, DispatchStatus, Dispatcher, DispatcherDrainReport,
428 DispatcherStatsSnapshot, ExtensionProviderPayload, FairnessKey, HeaderRedactionPolicy,
429 InboxIndex, OrchestratorBudgetConfig, OrchestratorBudgetSnapshot, ProviderCatalog,
430 ProviderCatalogError, ProviderId, ProviderMetadata, ProviderOutboundMethod, ProviderPayload,
431 ProviderRuntimeMetadata, ProviderSchema, ProviderSecretRequirement, ReadyKeyStats,
432 RecordedTriggerBinding, RetryPolicy, SchedulableJob, SchedulerKeyStat, SchedulerPolicy,
433 SchedulerSnapshot, SchedulerState, SchedulerStrategy, SignatureStatus,
434 SignatureVerificationMetadata, StreamEventPayload, TenantId, TraceId, TriggerBatchConfig,
435 TriggerBindingSnapshot, TriggerBindingSource, TriggerBindingSpec,
436 TriggerBudgetExhaustionStrategy, TriggerConcurrencyConfig, TriggerDebounceConfig,
437 TriggerDispatchOutcome, TriggerEvent, TriggerEventId, TriggerExpressionSpec,
438 TriggerFlowControlConfig, TriggerHandlerSpec, TriggerHarnessResult, TriggerId,
439 TriggerMetricsSnapshot, TriggerPredicateSpec, TriggerPriorityOrderConfig,
440 TriggerRateLimitConfig, TriggerRegistryError, TriggerRetryConfig, TriggerSingletonConfig,
441 TriggerState, TriggerThrottleConfig, WorkerQueue, WorkerQueueClaimHandle,
442 WorkerQueueEnqueueReceipt, WorkerQueueInspectSnapshot, WorkerQueueJob, WorkerQueueJobState,
443 WorkerQueuePriority, WorkerQueueResponseRecord, WorkerQueueState, WorkerQueueSummary,
444 DEFAULT_INBOX_RETENTION_DAYS, DEFAULT_STARVATION_AGE_MS, TRIGGERS_LIFECYCLE_TOPIC,
445 TRIGGER_ATTEMPTS_TOPIC, TRIGGER_CANCEL_REQUESTS_TOPIC, TRIGGER_DLQ_TOPIC,
446 TRIGGER_INBOX_CLAIMS_TOPIC, TRIGGER_INBOX_ENVELOPES_TOPIC, TRIGGER_INBOX_LEGACY_TOPIC,
447 TRIGGER_INBOX_OBSERVABILITY_TOPIC, TRIGGER_OPERATION_AUDIT_TOPIC, TRIGGER_OUTBOX_TOPIC,
448 TRIGGER_TEST_FIXTURES, WORKER_QUEUE_CATALOG_TOPIC,
449};
450pub use trust_graph::{
451 append_active_scope_attenuation_alert, append_active_trust_record,
452 append_scope_attenuation_alert, append_trust_record, export_trust_chain,
453 group_trust_records_by_trace, policy_for_agent, policy_for_autonomy_tier,
454 query_trust_graph_records, query_trust_records, resolve_agent_autonomy_tier,
455 summarize_trust_records, topic_for_agent, trust_score_for, verify_trust_chain, AutonomyTier,
456 TrustAgentSummary, TrustChainExport, TrustChainExportMetadata, TrustChainExportProducer,
457 TrustChainReport, TrustGraphRecord, TrustOutcome, TrustQueryFilters, TrustRecord,
458 TrustRecordActionKind, TrustScore, TrustTraceGroup, METADATA_KEY_ACTOR_CHAIN,
459 METADATA_KEY_ACTOR_CHAIN_ALERT, METADATA_KEY_EFFECTS_GRANT, METADATA_KEY_EFFECTS_USED,
460 METADATA_KEY_PARENT_RECORD_ID, OPENTRUSTGRAPH_ACCEPTED_SCHEMAS, OPENTRUSTGRAPH_CHAIN_SCHEMA_V0,
461 OPENTRUSTGRAPH_SCHEMA_V0, OPENTRUSTGRAPH_SCHEMA_V0_1, TRUST_ACTION_RELEASE,
462 TRUST_GRAPH_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_GLOBAL_TOPIC, TRUST_GRAPH_LEGACY_TOPIC_PREFIX,
463 TRUST_GRAPH_RECORDS_TOPIC, TRUST_GRAPH_TOPIC_PREFIX,
464};
465pub use value::*;
466pub use vm::*;
467
468#[cfg(feature = "vm-bench-internals")]
469#[doc(hidden)]
470pub mod bench_internals;
471
472pub fn compile_source(source: &str) -> Result<Chunk, String> {
477 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
478 Compiler::new().compile(&program).map_err(|e| e.to_string())
479}
480
481pub fn compile_source_named(source: &str, pipeline_name: &str) -> Result<Chunk, String> {
486 let program = harn_parser::check_source_strict(source).map_err(|e| e.to_string())?;
487 let has_pipeline = program.iter().any(|sn| {
488 let (_, inner) = harn_parser::peel_attributes(sn);
489 matches!(&inner.node, harn_parser::Node::Pipeline { name, .. } if name == pipeline_name)
490 });
491 if !has_pipeline {
492 return Err(format!("no pipeline named `{pipeline_name}` in source"));
493 }
494 Compiler::new()
495 .compile_named(&program, pipeline_name)
496 .map_err(|e| e.to_string())
497}
498
499pub struct SchemaAliasResolver {
507 compiler: compiler::Compiler,
508}
509
510impl SchemaAliasResolver {
511 pub fn empty() -> Self {
514 Self {
515 compiler: compiler::Compiler::new(),
516 }
517 }
518
519 pub fn from_program(program: &[harn_parser::SNode]) -> Self {
522 let mut compiler = compiler::Compiler::new();
523 compiler.collect_type_aliases(program);
524 Self { compiler }
525 }
526
527 pub fn json_schema_for_type_expr(
530 &self,
531 type_expr: &harn_parser::TypeExpr,
532 ) -> Option<serde_json::Value> {
533 let expanded = self.compiler.expand_alias(type_expr);
534 let schema = compiler::Compiler::type_expr_to_schema_value(&expanded)?;
535 let json_schema = schema::schema_to_json_schema_value(&schema).ok()?;
536 Some(llm::vm_value_to_json(&json_schema))
537 }
538
539 pub fn json_schema_for_typed_params(
542 &self,
543 params: &[harn_parser::TypedParam],
544 ) -> serde_json::Value {
545 let mut properties = serde_json::Map::new();
546 let mut required = Vec::new();
547
548 for param in params {
549 let param_schema = param
550 .type_expr
551 .as_ref()
552 .and_then(|type_expr| self.json_schema_for_type_expr(type_expr))
553 .unwrap_or_else(|| serde_json::json!({}));
554 if param.default_value.is_none() {
555 required.push(serde_json::Value::String(param.name.clone()));
556 }
557 properties.insert(param.name.clone(), param_schema);
558 }
559
560 let mut schema = serde_json::Map::new();
561 schema.insert(
562 "type".to_string(),
563 serde_json::Value::String("object".to_string()),
564 );
565 schema.insert(
566 "properties".to_string(),
567 serde_json::Value::Object(properties),
568 );
569 if !required.is_empty() {
570 schema.insert("required".to_string(), serde_json::Value::Array(required));
571 }
572 serde_json::Value::Object(schema)
573 }
574}
575
576pub fn json_schema_for_type_expr(type_expr: &harn_parser::TypeExpr) -> Option<serde_json::Value> {
580 SchemaAliasResolver::empty().json_schema_for_type_expr(type_expr)
581}
582
583pub fn json_schema_for_typed_params(params: &[harn_parser::TypedParam]) -> serde_json::Value {
584 SchemaAliasResolver::empty().json_schema_for_typed_params(params)
585}
586
587#[cfg(test)]
588mod schema_alias_resolver_tests {
589 use super::*;
590
591 fn fn_params_schema(src: &str) -> serde_json::Value {
592 let program = harn_parser::parse_source(src).expect("parse test source");
593 let resolver = SchemaAliasResolver::from_program(&program);
594 for node in &program {
595 let (_, inner) = harn_parser::peel_attributes(node);
596 if let harn_parser::Node::FnDecl { params, .. } = &inner.node {
597 return resolver.json_schema_for_typed_params(params);
598 }
599 }
600 panic!("no fn decl in test source");
601 }
602
603 #[test]
604 fn named_shape_alias_projects_like_inline_shape() {
605 let inline = fn_params_schema("pub fn f(p: {kind: string, path: string}) {}");
606 let aliased =
607 fn_params_schema("type Src = {kind: string, path: string}\npub fn f(p: Src) {}");
608 assert_eq!(
609 aliased, inline,
610 "a named shape alias must project the same inputSchema as its inline shape",
611 );
612 assert_ne!(
613 aliased["properties"]["p"],
614 serde_json::json!({}),
615 "the alias parameter must not erase to an empty schema",
616 );
617 }
618
619 #[test]
620 fn literal_union_alias_projects_json_enum() {
621 let schema = fn_params_schema("type Kind = \"local\" | \"ssh\"\npub fn f(p: Kind) {}");
622 let p = &schema["properties"]["p"];
623 assert_eq!(p["type"], "string");
624 assert_eq!(p["enum"], serde_json::json!(["local", "ssh"]));
625 }
626
627 #[test]
628 fn unknown_named_type_still_erases_to_empty() {
629 let schema = fn_params_schema("pub fn f(p: Unknown) {}");
631 assert_eq!(schema["properties"]["p"], serde_json::json!({}));
632 }
633}
634
635fn reset_llm_state_for_thread_reset() {
636 llm::reset_llm_state();
637 #[cfg(test)]
638 reset_thread_local_state_test_hooks::before_llm_global_reset();
639 llm::reset_rate_limit_registry();
642 llm_config::clear_user_overrides();
643 llm_config::clear_runtime_provider_endpoint_overrides();
644}
645
646#[cfg(test)]
647mod reset_thread_local_state_test_hooks {
648 use std::sync::{Arc, Mutex, OnceLock};
649
650 type Hook = Arc<dyn Fn() + Send + Sync + 'static>;
651
652 static BEFORE_LLM_GLOBAL_RESET: OnceLock<Mutex<Option<Hook>>> = OnceLock::new();
653
654 fn before_llm_global_reset_hook() -> &'static Mutex<Option<Hook>> {
655 BEFORE_LLM_GLOBAL_RESET.get_or_init(|| Mutex::new(None))
656 }
657
658 pub(crate) struct HookGuard;
659
660 impl Drop for HookGuard {
661 fn drop(&mut self) {
662 let mut hook = before_llm_global_reset_hook()
663 .lock()
664 .unwrap_or_else(std::sync::PoisonError::into_inner);
665 *hook = None;
666 }
667 }
668
669 pub(crate) fn install_before_llm_global_reset(hook: Hook) -> HookGuard {
670 let mut slot = before_llm_global_reset_hook()
671 .lock()
672 .unwrap_or_else(std::sync::PoisonError::into_inner);
673 *slot = Some(hook);
674 HookGuard
675 }
676
677 pub(crate) fn before_llm_global_reset() {
678 let hook = before_llm_global_reset_hook()
679 .lock()
680 .unwrap_or_else(std::sync::PoisonError::into_inner)
681 .clone();
682 if let Some(hook) = hook {
683 hook();
684 }
685 }
686}
687
688pub fn reset_thread_local_state() {
690 #[cfg(test)]
691 {
692 let _guard = llm::env_guard();
697 reset_llm_state_for_thread_reset();
698 }
699 #[cfg(not(test))]
700 reset_llm_state_for_thread_reset();
701
702 http::reset_http_state();
703 channels::reset_channel_state();
704 event_log::reset_active_event_log();
705 egress::clear_explicit_egress_policy_requirement_for_host();
706 egress::clear_ssrf_guard_requirement_for_host();
707 stdlib::reset_stdlib_state();
708 connectors::clear_active_connector_clients();
709 orchestration::clear_runtime_hooks();
710 orchestration::clear_file_edit_queue();
711 orchestration::clear_execution_policy_stacks();
712 orchestration::clear_command_policies();
713 orchestration::clear_pipeline_on_finish();
714 orchestration::reset_lifecycle_receipt_registry();
715 orchestration::agent_inbox::reset();
716 tool_call_cancellations::reset_registry();
717 redact::clear_policy_stack();
718 security::reset_thread_state();
719 triggers::clear_dispatcher_state();
720 triggers::clear_trigger_registry();
721 events::reset_event_sinks();
722 tracing::set_tracing_enabled(false);
723 tracing::reset_tracing();
724 agent_events::reset_all_sinks();
736 agent_sessions::reset_session_store();
737 mcp_registry::reset();
738 mcp_host::reset_for_tests();
739 call_budget::reset_call_budget_state();
740 clock_mock::leak_audit::reset();
741}
742
743#[cfg(test)]
744mod reset_leak_tests {
745 use super::*;
751 use crate::value::VmValue;
752
753 #[test]
754 fn reset_drains_pending_file_edit_notifications() {
755 orchestration::queue_file_edited("stale.harn", serde_json::json!({"operation": "write"}));
756
757 reset_thread_local_state();
758
759 assert!(
760 orchestration::drain_file_edits().is_empty(),
761 "a later VM run must not receive file edits queued by the previous run"
762 );
763 }
764
765 #[test]
774 fn builtin_profile_recording_ends_with_its_run_not_with_a_global_reset() {
775 let _lock = builtin_profile::test_lock()
776 .lock()
777 .unwrap_or_else(std::sync::PoisonError::into_inner);
778 let recording = builtin_profile::enable();
779 builtin_profile::record("run_shell", std::time::Duration::from_millis(5));
780 assert!(builtin_profile::is_enabled());
781 assert!(!builtin_profile::snapshot().is_empty());
782
783 reset_thread_local_state();
784
785 assert!(
786 builtin_profile::is_enabled(),
787 "an unrelated global reset must not disarm an in-flight profiled run"
788 );
789 assert!(
790 !builtin_profile::snapshot().is_empty(),
791 "an unrelated global reset must not drop totals the run still owns"
792 );
793
794 drop(recording);
795
796 assert!(
797 !builtin_profile::is_enabled(),
798 "a profiled run must not leave the recorder on for the next one"
799 );
800 assert!(
801 builtin_profile::snapshot().is_empty(),
802 "builtin totals must be empty once the run's guard drops"
803 );
804 }
805
806 #[test]
811 fn reset_drains_session_changed_paths() {
812 let session = "sess-leak";
813 agent_sessions::open_or_create(Some(session.to_string()));
814 agent_sessions::record_session_changed_path(session, "/tmp/written-by-a-dead-run.txt");
815 assert!(!agent_sessions::session_changed_paths(session).is_empty());
816 reset_thread_local_state();
817 assert!(
818 agent_sessions::session_changed_paths(session).is_empty(),
819 "a receipt must not inherit an abandoned session's writes"
820 );
821 }
822
823 #[test]
824 fn reset_drains_agent_inbox() {
825 orchestration::agent_inbox::reset();
826 orchestration::agent_inbox::push("sess-2660", "note", "leak", "test");
827 assert!(orchestration::agent_inbox::session_count() > 0);
828 reset_thread_local_state();
829 assert_eq!(
830 orchestration::agent_inbox::session_count(),
831 0,
832 "agent_inbox must be empty after reset"
833 );
834 }
835
836 #[test]
837 fn reset_drains_tool_call_cancellation_registry() {
838 tool_call_cancellations::reset_registry();
839 let registered = tool_call_cancellations::register("sess-2660", "call-1", "tool");
842 if let Some((_handle, guard)) = registered {
843 std::mem::forget(guard);
844 }
845 assert!(tool_call_cancellations::registry_len() > 0);
846 reset_thread_local_state();
847 assert_eq!(
848 tool_call_cancellations::registry_len(),
849 0,
850 "tool-call cancellation registry must be empty after reset"
851 );
852 }
853
854 #[test]
855 fn reset_drains_routing_policy_registry() {
856 llm::routing::clear_policy_registry();
857 let mut config: crate::value::DictMap = crate::value::DictMap::new();
858 config.insert(
859 crate::value::intern_key("chain"),
860 VmValue::List(std::sync::Arc::new(vec![VmValue::String(
861 arcstr::ArcStr::from("mock:mock"),
862 )])),
863 );
864 llm::routing::build_routing_policy(&config).expect("intern a routing policy");
865 assert!(llm::routing::policy_registry_len() > 0);
866 reset_thread_local_state();
867 assert_eq!(
868 llm::routing::policy_registry_len(),
869 0,
870 "routing policy registry must be empty after reset"
871 );
872 }
873
874 #[test]
875 fn reset_holds_llm_env_guard_while_wiping_llm_globals() {
876 let observed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
877 let observed_hook = std::sync::Arc::clone(&observed);
878 let _hook = reset_thread_local_state_test_hooks::install_before_llm_global_reset(
879 std::sync::Arc::new(move || {
880 assert!(
881 matches!(
882 llm::env_lock().try_lock(),
883 Err(std::sync::TryLockError::WouldBlock)
884 ),
885 "reset_thread_local_state must hold env_guard before wiping LLM globals"
886 );
887 observed_hook.store(true, std::sync::atomic::Ordering::SeqCst);
888 }),
889 );
890
891 reset_thread_local_state();
892 assert!(
893 observed.load(std::sync::atomic::Ordering::SeqCst),
894 "LLM global reset hook should have run"
895 );
896 }
897}