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