Skip to main content

harn_vm/
stdlib.rs

1//! Standard library builtins for the Harn VM.
2//!
3//! Every builtin is declared with the `#[harn_builtin]` proc-macro
4//! (`crate::stdlib::macros::harn_builtin`). Each annotation emits a sibling
5//! `static <FN>_DEF: VmBuiltinDef` carrying the signature, aliases, handler,
6//! and metadata, and registers it into the workspace-global
7//! [`macros::ALL_BUILTIN_DEFS`] distributed slice at link time. The CLI / LSP /
8//! lint / serve / dap binaries call [`force_link`] to defeat rlib dead-code
9//! stripping (linkme issue #36) so every static lands in the slice. Modules
10//! still expose a `register_<module>_builtins(vm)` helper for ordered eager
11//! registration (e.g. so `clock::timestamp` can override `process::timestamp`).
12//! `register_vm_stdlib` calls those helpers in order and then installs the
13//! aggregated signatures into the parser registry.
14//!
15//! See `CONTRIBUTING.md` ("Adding a stdlib builtin") for the full template.
16
17pub mod macros;
18
19pub(crate) mod args;
20
21mod agent_sessions;
22pub mod agent_state;
23pub(crate) mod agents;
24pub(crate) mod agents_daemon;
25mod artifact_emit;
26pub(crate) mod assemble;
27pub mod asset_paths;
28mod bytes;
29mod calendar;
30mod channel_guardrails;
31mod channels;
32pub(crate) mod clock;
33pub(crate) mod collections;
34mod command_policy;
35pub(crate) mod compaction;
36#[cfg(feature = "compression")]
37mod compression;
38mod concurrency;
39pub(crate) use concurrency::cancelled_vm_error;
40mod connectors;
41mod cookies;
42mod cron;
43mod crypto;
44mod csv;
45mod datetime;
46mod diff;
47pub(crate) use datetime::date_dict_from_millis;
48#[cfg(feature = "content")]
49mod document;
50mod durable_step;
51mod event_log;
52pub use event_log::mint_hypothesis_native_attestation;
53mod external_agent;
54pub(crate) mod files;
55mod flow;
56pub(crate) mod fs;
57mod git;
58pub(crate) mod git_topology;
59mod grounding;
60pub(crate) mod harn_entry;
61pub(crate) mod hitl;
62mod hitl_read;
63pub mod host;
64pub mod http_response;
65pub(crate) mod io;
66mod iter;
67pub(crate) mod json;
68mod json_query;
69pub(crate) mod json_repair;
70pub(crate) mod json_stream;
71mod jsonrpc;
72mod junit;
73mod lifecycle_receipts;
74mod logging;
75pub mod long_running;
76mod math;
77pub(crate) use math::call_seeded_random_method;
78pub(crate) mod memory;
79mod monitors;
80mod multipart;
81mod net;
82mod net_policy;
83mod oauth_dynreg;
84mod oauth_storage;
85pub(crate) mod observability;
86mod package_snapshot;
87pub(crate) use package_snapshot::PackageSnapshotRegistry;
88mod path;
89pub(crate) mod path_scope_guard;
90pub(crate) mod pool;
91mod portable;
92#[cfg(feature = "postgres")]
93mod postgres;
94#[cfg(feature = "postgres")]
95pub use postgres::install_shared_pool_registry;
96pub mod process;
97pub(crate) mod process_spawn;
98mod project;
99mod project_catalog;
100mod project_enrich;
101mod regex;
102mod review;
103mod runtime_scope;
104pub(crate) mod sandbox;
105pub mod secret_scan;
106pub(crate) mod session_change;
107pub(crate) mod session_store;
108pub(crate) mod session_wal_watch;
109mod sets;
110pub(crate) mod shapes;
111mod skills;
112#[cfg(feature = "sqlite")]
113mod sqlite;
114pub(crate) mod strings;
115pub(crate) mod supervisor;
116pub mod template;
117mod testbench;
118mod testing;
119mod timing;
120pub mod token_redaction;
121pub(crate) mod tool_hooks;
122mod tool_projection;
123pub(crate) mod tools;
124pub mod tracing;
125mod transcript_compact;
126pub(crate) mod transcript_project;
127mod triggers_stdlib;
128mod tui;
129mod types;
130mod url_parse;
131mod vision;
132pub(crate) mod waitpoint;
133#[cfg(feature = "content")]
134mod web;
135pub mod workflow_messages;
136pub(crate) mod xml;
137
138use crate::http::register_http_builtins;
139use crate::llm::register_llm_builtins;
140use crate::mcp::register_mcp_builtins;
141use crate::mcp_server::register_mcp_server_builtins;
142use crate::vm::Vm;
143
144pub(crate) use crate::schema::{json_to_vm_value, schema_result_value};
145pub(crate) fn set_thread_source_dir(dir: &std::path::Path) {
146    process::set_thread_source_dir(dir);
147}
148
149/// Register core builtins: pure/deterministic, no I/O.
150pub fn register_core_stdlib(vm: &mut Vm) {
151    types::register_type_builtins(vm);
152    math::register_math_builtins(vm);
153    strings::register_string_builtins(vm);
154    json::register_json_builtins(vm);
155    json_stream::register_json_stream_builtins(vm);
156    xml::register_xml_builtins(vm);
157    datetime::register_datetime_builtins(vm);
158    diff::register_diff_builtins(vm);
159    #[cfg(feature = "content")]
160    document::register_document_builtins(vm);
161    calendar::register_calendar_builtins(vm);
162    cron::register_cron_builtins(vm);
163    regex::register_regex_builtins(vm);
164    bytes::register_bytes_builtins(vm);
165    #[cfg(feature = "compression")]
166    compression::register_compression_builtins(vm);
167    command_policy::register_command_policy_builtins(vm);
168    runtime_scope::register_runtime_scope_builtins(vm);
169    crypto::register_crypto_builtins(vm);
170    csv::register_csv_builtins(vm);
171    junit::register_junit_builtins(vm);
172    multipart::register_multipart_builtins(vm);
173    url_parse::register_url_builtins(vm);
174    #[cfg(feature = "content")]
175    web::register_web_builtins(vm);
176    cookies::register_cookie_builtins(vm);
177    path::register_path_helper_builtins(vm);
178    sets::register_set_builtins(vm);
179    collections::register_collection_builtins(vm);
180    iter::register_iter_builtins(vm);
181    event_log::register_event_log_builtins(vm);
182    durable_step::register_durable_step_builtins(vm);
183    channels::register_channel_builtins(vm);
184    channel_guardrails::register_channel_guardrail_builtins(vm);
185    shapes::register_shape_builtins(vm);
186    testing::register_testing_builtins(vm);
187    flow::register_flow_builtins(vm);
188    lifecycle_receipts::register_lifecycle_receipt_builtins(vm);
189    net_policy::register_net_policy_builtins(vm);
190    http_response::register_http_response_builtins(vm);
191    portable::register_portable_builtins(vm);
192}
193
194/// Register I/O builtins (requires OS access).
195pub fn register_io_stdlib(vm: &mut Vm) {
196    io::register_io_builtins(vm);
197    host::register_host_builtins(vm);
198    fs::register_fs_builtins(vm);
199    package_snapshot::register_package_snapshot_builtins(vm);
200    files::register_file_builtins(vm);
201    git::register_git_builtins(vm);
202    vision::register_vision_builtins(vm);
203    agent_state::register_agent_state_builtins(vm);
204    memory::register_memory_builtins(vm);
205    session_store::register_session_store_builtins(vm);
206    net::register_net_builtins(vm);
207    process::register_process_builtins(vm);
208    process::register_path_builtins(vm);
209    sandbox::register_sandbox_builtins(vm);
210    // Clock builtins overlay process::timestamp/elapsed so they honor
211    // mock_time / advance_time. Register AFTER process to take precedence.
212    clock::register_clock_builtins(vm);
213    crate::durable_rate_limit::register_durable_rate_limit_builtins(vm);
214    testbench::register_testbench_builtins(vm);
215    project::register_project_builtins(vm);
216    grounding::register_grounding_builtins(vm);
217    tracing::register_tracing_builtins(vm);
218    observability::register_observability_builtins(vm);
219    timing::register_timing_builtins(vm);
220    tui::register_tui_builtins(vm);
221}
222
223fn register_agent_stdlib_before_llm(vm: &mut Vm) {
224    concurrency::register_concurrency_builtins(vm);
225    connectors::register_connector_builtins(vm);
226    review::register_review_builtins(vm);
227    secret_scan::register_secret_scan_builtins(vm);
228    tools::register_tool_builtins(vm);
229    tool_projection::register_tool_projection_builtins(vm);
230    tool_hooks::register_tool_hooks_builtins(vm);
231    crate::composition::register_composition_builtins(vm);
232    skills::register_skill_builtins(vm);
233    agents_daemon::register_daemon_builtins(vm);
234    triggers_stdlib::register_trigger_builtins(vm);
235    #[cfg(feature = "postgres")]
236    postgres::register_postgres_builtins(vm);
237    #[cfg(feature = "sqlite")]
238    sqlite::register_sqlite_builtins(vm);
239    monitors::register_monitor_builtins(vm);
240    hitl::register_hitl_builtins(vm);
241    hitl_read::register_hitl_read_builtins(vm);
242    waitpoint::register_waitpoint_builtins(vm);
243    supervisor::register_supervisor_builtins(vm);
244    agents::register_agent_builtins(vm);
245    pool::register_pool_builtins(vm);
246    oauth_storage::register_oauth_storage_builtins(vm);
247    oauth_dynreg::register_oauth_dynreg_builtins(vm);
248    token_redaction::register_token_redaction_builtins(vm);
249    agent_sessions::register_agent_session_builtins(vm);
250    artifact_emit::register_artifact_emit_builtins(vm);
251    external_agent::register_external_agent_builtins(vm);
252    path_scope_guard::register_path_scope_guard_builtins(vm);
253    workflow_messages::register_workflow_message_builtins(vm);
254    transcript_compact::register_transcript_compaction_builtins(vm);
255    compaction::register_compaction_builtins(vm);
256    transcript_project::register_transcript_projection_builtins(vm);
257    assemble::register_assemble_context_builtin(vm);
258    crate::egress::register_egress_builtins(vm);
259    crate::security::register_security_builtins(vm);
260    register_http_builtins(vm);
261    jsonrpc::register_jsonrpc_builtins(vm);
262}
263
264fn register_agent_stdlib_after_llm(vm: &mut Vm) {
265    register_mcp_builtins(vm);
266    register_mcp_server_builtins(vm);
267    crate::step_runtime::register_step_builtins(vm);
268}
269
270/// Register agent builtins (requires network access and async runtime).
271pub fn register_agent_stdlib(vm: &mut Vm) {
272    register_agent_stdlib_before_llm(vm);
273    register_llm_builtins(vm);
274    register_agent_stdlib_after_llm(vm);
275}
276
277/// Register all standard builtins on a VM (core + io + agent). Also
278/// installs the macro-emitted signature slice into the parser registry
279/// (idempotent under repeat calls with the same slice pointer).
280pub fn register_vm_stdlib(vm: &mut Vm) {
281    register_core_stdlib(vm);
282    register_io_stdlib(vm);
283    register_agent_stdlib(vm);
284    vm.project_declared_capability_methods();
285    if vm.harness().is_none() {
286        vm.set_harness(crate::harness::Harness::real());
287    }
288    if harn_parser::legacy_ambient_capabilities_enabled() && vm.global("harness").is_none() {
289        let harness = vm
290            .root_harness_value()
291            .expect("register_vm_stdlib installs a root Harness");
292        vm.set_global("harness", harness);
293    }
294    vm.project_legacy_capability_globals();
295    harn_builtin_registry::install_builtin_manifest(all_builtin_manifest());
296}
297
298pub(crate) fn rebind_execution_state_builtins(vm: &mut Vm) {
299    concurrency::register_concurrency_builtins(vm);
300}
301
302fn stdlib_probe_vm() -> Vm {
303    let mut vm = Vm::new();
304    register_vm_stdlib(&mut vm);
305    // Name-only/metadata introspection never accesses this path, but passing
306    // a real per-platform temp dir keeps registration logic honest if a
307    // callee someday validates its parent.
308    let tmp = std::env::temp_dir();
309    crate::store::register_store_builtins(&mut vm, &tmp);
310    crate::checkpoint::register_checkpoint_builtins(&mut vm, &tmp, "default");
311    crate::metadata::register_metadata_builtins(&mut vm, &tmp);
312    // Install the macro-emitted signatures into the parser registry so any
313    // probe-driven name/metadata query (e.g. the alignment test) sees the
314    // post-migration sig set. Idempotent under repeat install with the same
315    // pointer (which `all_builtin_manifest()` guarantees).
316    harn_builtin_registry::install_builtin_manifest(all_builtin_manifest());
317    vm
318}
319
320/// Aggregate of every `#[harn_builtin]`-emitted `VmBuiltinDef` in the stdlib.
321///
322/// Backed by the `linkme::distributed_slice` declared on
323/// [`crate::stdlib::macros::ALL_BUILTIN_DEFS`] — every annotated fn
324/// contributes one entry automatically at link time. Keep builtin registration
325/// on this distributed slice instead of per-module arrays plus a central
326/// hand-maintained aggregator.
327///
328/// **Force-link warning** (linkme issue #36): rlib dead-code stripping
329/// can drop these statics when `harn-vm` is linked transitively. Every
330/// binary that exercises builtins (`harn-cli`, `harn-lsp`, `harn-lint`,
331/// `harn-serve`, `harn-dap`) calls [`force_link`] near `main()` to defeat
332/// the stripping. The alignment test
333/// `linkme_distributed_slice_populates_with_all_builtins` catches a silent
334/// regression by asserting the slice is non-empty.
335pub fn all_builtin_defs() -> &'static [&'static macros::VmBuiltinDef] {
336    let defs = &macros::ALL_BUILTIN_DEFS;
337    validate_builtin_contracts(defs);
338    defs
339}
340
341fn validate_builtin_contracts(defs: &[&macros::VmBuiltinDef]) {
342    use harn_builtin_meta::BuiltinExposure;
343    let mut source_names = std::collections::BTreeSet::new();
344    let mut capability_methods = std::collections::BTreeSet::new();
345    for def in defs {
346        assert!(
347            def.contract.is_declared(),
348            "builtin `{}` has no typed exposure/effect contract",
349            def.sig.name
350        );
351        assert!(
352            !matches!(def.contract.exposure, BuiltinExposure::PureGlobal)
353                || def.contract.effects.is_empty(),
354            "ambient global builtin `{}` declares effects; effects must flow through Harness",
355            def.sig.name
356        );
357        if let BuiltinExposure::CapabilityFunction { authority_argument } = def.contract.exposure {
358            assert!(
359                !def.contract.effects.is_empty(),
360                "capability function `{}` must declare effects",
361                def.sig.name
362            );
363            assert!(
364                usize::from(authority_argument) < def.sig.params.len(),
365                "capability function `{}` authority argument is out of range",
366                def.sig.name
367            );
368        }
369        if def.runtime_only {
370            assert!(
371                matches!(
372                    def.contract.exposure,
373                    BuiltinExposure::StdlibInternal | BuiltinExposure::RuntimeInternal
374                ),
375                "runtime_only builtin `{}` must use runtime_internal exposure",
376                def.sig.name
377            );
378        }
379        if let BuiltinExposure::HarnessMethod { method, .. } = def.contract.exposure {
380            assert!(
381                !method.is_empty(),
382                "harness method for `{}` cannot be empty",
383                def.sig.name
384            );
385            let BuiltinExposure::HarnessMethod { capability, method } = def.contract.exposure
386            else {
387                unreachable!()
388            };
389            assert!(
390                capability_methods.insert((capability, method)),
391                "duplicate contract for harness.{}.{}",
392                capability.field_name(),
393                method
394            );
395        }
396        if matches!(
397            def.contract.exposure,
398            BuiltinExposure::PureGlobal
399                | BuiltinExposure::CapabilityFunction { .. }
400                | BuiltinExposure::PrivilegedWire
401                | BuiltinExposure::StdlibInternal
402                | BuiltinExposure::HarnessMethod { .. }
403        ) {
404            assert!(
405                source_names.insert(def.sig.name),
406                "duplicate source contract name `{}`",
407                def.sig.name
408            );
409        }
410    }
411}
412
413/// Force-link entry point: a `pub fn` that touches `ALL_BUILTIN_DEFS` so
414/// the linker keeps every `#[harn_builtin]`-emitted static. Drivers
415/// (`harn-cli`, `harn-lsp`, etc.) call this once at startup. Doing nothing
416/// at runtime is fine — the side effect is purely a link-time signal.
417///
418/// See [`linkme issue #36`](https://github.com/dtolnay/linkme/issues/36)
419/// for why the explicit touch is necessary on every supported target.
420pub fn force_link() {
421    // `black_box` prevents LLVM from constant-folding the length read away.
422    // The `>= 1` guard never trips at runtime but is a load-bearing safety
423    // net: it converts a silent slice-empty regression into a panic that
424    // surfaces at the first builtin call instead of a confusing
425    // `HARN-NAM-002` somewhere down the line.
426    let len = std::hint::black_box(macros::ALL_BUILTIN_DEFS.len());
427    assert!(
428        len >= 1,
429        "linkme distributed_slice ALL_BUILTIN_DEFS is empty — \
430         the binary is missing `harn_vm::stdlib::force_link()` at startup, \
431         or the linker stripped the harn-vm rlib statics (see linkme issue #36)"
432    );
433}
434
435/// Driver-facing immutable manifest for parser, IR, policy, and docs.
436pub fn all_builtin_manifest() -> &'static [&'static harn_builtin_registry::BuiltinManifestEntry] {
437    use std::sync::OnceLock;
438    static AGG: OnceLock<Vec<&'static harn_builtin_registry::BuiltinManifestEntry>> =
439        OnceLock::new();
440    AGG.get_or_init(|| {
441        let mut out = Vec::new();
442        let mut capability_methods = std::collections::BTreeSet::new();
443        for def in all_builtin_defs() {
444            if def.runtime_only {
445                continue;
446            }
447            out.push(
448                Box::leak(Box::new(harn_builtin_registry::BuiltinManifestEntry {
449                    name: def.sig.name,
450                    canonical_name: def.sig.name,
451                    signature: &def.sig,
452                    contract: def.contract,
453                })) as &'static harn_builtin_registry::BuiltinManifestEntry,
454            );
455            if let harn_builtin_meta::BuiltinExposure::HarnessMethod { capability, method } =
456                def.contract.exposure
457            {
458                capability_methods.insert((capability, method));
459            }
460            for alias in def.aliases {
461                let signature = Box::leak(Box::new(harn_builtin_meta::BuiltinSignature {
462                    name: alias,
463                    ..def.sig
464                }));
465                out.push(
466                    Box::leak(Box::new(harn_builtin_registry::BuiltinManifestEntry {
467                        name: alias,
468                        canonical_name: def.sig.name,
469                        signature,
470                        contract: def.contract,
471                    })) as &'static harn_builtin_registry::BuiltinManifestEntry,
472                );
473            }
474        }
475        for entry in harn_capability_contracts::manifest() {
476            let harn_builtin_meta::BuiltinExposure::HarnessMethod { capability, method } =
477                entry.contract.exposure
478            else {
479                unreachable!("leaf capability manifest contains a non-method contract")
480            };
481            if !capability_methods.insert((capability, method)) {
482                let runtime_entry = out
483                    .iter()
484                    .find(|candidate| candidate.contract.exposure == entry.contract.exposure)
485                    .expect("duplicate capability key must have a runtime manifest entry");
486                assert_eq!(
487                    runtime_entry.contract,
488                    entry.contract,
489                    "runtime effect contract drift for harness.{}.{}",
490                    capability.field_name(),
491                    method
492                );
493                assert_eq!(
494                    runtime_entry.signature,
495                    entry.signature,
496                    "runtime signature drift for harness.{}.{}",
497                    capability.field_name(),
498                    method
499                );
500                continue;
501            }
502            out.push(*entry);
503        }
504        for group in harn_builtin_meta::host_capabilities::all_host_capability_groups() {
505            for method in group.methods {
506                if !capability_methods.insert((group.capability, *method)) {
507                    continue;
508                }
509                let internal_name: &'static str = Box::leak(
510                    format!("__cap_{}_{}", group.capability.field_name(), method).into_boxed_str(),
511                );
512                let params: &'static [harn_builtin_meta::Param] =
513                    Box::leak(Box::new([harn_builtin_meta::Param::new(
514                        "request",
515                        harn_builtin_meta::Ty::Named("dict"),
516                    )]));
517                let signature = Box::leak(Box::new(harn_builtin_meta::BuiltinSignature::simple(
518                    internal_name,
519                    params,
520                    harn_builtin_meta::Ty::Named("dict"),
521                )));
522                out.push(Box::leak(Box::new(
523                    harn_builtin_registry::BuiltinManifestEntry {
524                        name: internal_name,
525                        canonical_name: internal_name,
526                        signature,
527                        contract: harn_builtin_meta::BuiltinContract::harness(
528                            group.capability,
529                            method,
530                            group.effects,
531                        ),
532                    },
533                )));
534            }
535        }
536        out
537    })
538    .as_slice()
539}
540
541/// Indexed view of the authoritative builtin manifest.
542///
543/// Runtime dispatch reaches this boundary for every typed Harness call. Keep
544/// lookup policy here with the manifest owner instead of making each consumer
545/// linearly rescan the full registry. The nested capability map also accepts a
546/// borrowed `&str`, so a method call does not allocate an owned lookup key.
547struct BuiltinManifestIndex {
548    by_name: std::collections::HashMap<
549        &'static str,
550        &'static harn_builtin_registry::BuiltinManifestEntry,
551    >,
552    by_capability: std::collections::HashMap<
553        harn_builtin_meta::CapabilityId,
554        std::collections::HashMap<
555            &'static str,
556            &'static harn_builtin_registry::BuiltinManifestEntry,
557        >,
558    >,
559    recorded_effects_by_name: std::collections::HashMap<
560        &'static str,
561        &'static harn_builtin_registry::BuiltinManifestEntry,
562    >,
563}
564
565fn builtin_manifest_index() -> &'static BuiltinManifestIndex {
566    use harn_builtin_meta::BuiltinExposure;
567    use std::sync::OnceLock;
568
569    static INDEX: OnceLock<BuiltinManifestIndex> = OnceLock::new();
570    INDEX.get_or_init(|| {
571        let mut by_name = std::collections::HashMap::new();
572        let mut by_capability: std::collections::HashMap<
573            harn_builtin_meta::CapabilityId,
574            std::collections::HashMap<
575                &'static str,
576                &'static harn_builtin_registry::BuiltinManifestEntry,
577            >,
578        > = std::collections::HashMap::new();
579        let mut recorded_effects_by_name = std::collections::HashMap::new();
580        for entry in all_builtin_manifest() {
581            assert!(
582                by_name.insert(entry.name, *entry).is_none(),
583                "duplicate builtin manifest name `{}`",
584                entry.name
585            );
586            // An alias repeats its primary's contract under a second name, so
587            // only the canonical entry may claim the capability method.
588            if let BuiltinExposure::HarnessMethod { capability, method } = entry.contract.exposure {
589                if entry.is_canonical() {
590                    assert!(
591                        by_capability
592                            .entry(capability)
593                            .or_default()
594                            .insert(method, *entry)
595                            .is_none(),
596                        "duplicate Harness method manifest entry `harness.{}.{method}`",
597                        capability.field_name()
598                    );
599                }
600            }
601            if matches!(
602                entry.contract.exposure,
603                BuiltinExposure::CapabilityFunction { .. }
604                    | BuiltinExposure::HarnessMethod { .. }
605                    | BuiltinExposure::PrivilegedWire
606                    | BuiltinExposure::StdlibInternal
607            ) && !entry.contract.effects.is_empty()
608            {
609                recorded_effects_by_name.insert(entry.name, *entry);
610            }
611        }
612        BuiltinManifestIndex {
613            by_name,
614            by_capability,
615            recorded_effects_by_name,
616        }
617    })
618}
619
620/// Resolve the registry name behind a public `harness.<capability>.<method>`
621/// path.
622///
623/// Observation, diagnostics, and tooling classify a capability call by the
624/// same registry entry the removed ambient global resolved to, so a call
625/// through the typed handle profiles and audits identically.
626pub fn builtin_for_harness_path(path: &str) -> Option<&'static str> {
627    let (field, method) = path.strip_prefix("harness.")?.split_once('.')?;
628    let capability = harn_builtin_meta::CapabilityId::from_field_name(field)?;
629    capability_method_manifest_entry(capability, method).map(|entry| entry.name)
630}
631
632/// Resolve the typed Harness method that replaced an ambient builtin name.
633pub fn harness_method_for_builtin(
634    name: &str,
635) -> Option<(harn_builtin_meta::CapabilityId, &'static str)> {
636    match builtin_manifest_entry(name)?.contract.exposure {
637        harn_builtin_meta::BuiltinExposure::HarnessMethod { capability, method } => {
638            Some((capability, method))
639        }
640        _ => None,
641    }
642}
643
644/// Resolve a source/runtime builtin contract without rescanning the manifest.
645pub fn builtin_manifest_entry(
646    name: &str,
647) -> Option<&'static harn_builtin_registry::BuiltinManifestEntry> {
648    builtin_manifest_index().by_name.get(name).copied()
649}
650
651/// Resolve the contract for one typed Harness method without allocation.
652pub fn capability_method_manifest_entry(
653    capability: harn_builtin_meta::CapabilityId,
654    method: &str,
655) -> Option<&'static harn_builtin_registry::BuiltinManifestEntry> {
656    builtin_manifest_index()
657        .by_capability
658        .get(&capability)
659        .and_then(|methods| methods.get(method))
660        .copied()
661}
662
663/// Resolve only builtin contracts that can emit runtime effect receipts.
664///
665/// Pure builtins dominate VM call volume. Keeping them out of this projection
666/// makes their receipt check one negative hash lookup instead of a full
667/// manifest lookup plus exposure classification on every call.
668pub fn recorded_effect_builtin_manifest_entry(
669    name: &str,
670) -> Option<&'static harn_builtin_registry::BuiltinManifestEntry> {
671    builtin_manifest_index()
672        .recorded_effects_by_name
673        .get(name)
674        .copied()
675}
676
677mod harness_migration;
678
679pub use harness_migration::{
680    harness_migration_for_builtin, HarnessBuiltinArgumentMigration, HarnessBuiltinMigration,
681};
682
683/// Register every `#[harn_builtin]`-emitted def on the given VM. Drivers
684/// that build the full stdlib via `register_vm_stdlib` get this for free —
685/// each module's `register_*_builtins` walks its `MODULE_BUILTINS` slice.
686/// This helper is exposed for embedders / tests that want a one-call entry.
687pub fn register_all_macro_builtins(vm: &mut Vm) {
688    for def in all_builtin_defs() {
689        vm.register_builtin_def(def);
690    }
691}
692
693/// Return the canonical list of all stdlib builtin names. Used by
694/// harn-lint and harn-lsp to avoid hardcoded duplicate lists.
695pub fn stdlib_builtin_names() -> Vec<String> {
696    let vm = stdlib_probe_vm();
697    let mut names = vm.builtin_names();
698    // Special opcodes/keywords, not registered builtins, but linter
699    // should recognize them as valid function calls.
700    for extra in harn_parser::builtin_signatures::LANGUAGE_INTRINSICS {
701        names.push(extra.to_string());
702    }
703    names
704}
705
706/// Return discoverable metadata for registered stdlib builtins.
707pub fn stdlib_builtin_metadata() -> Vec<crate::vm::VmBuiltinMetadata> {
708    stdlib_probe_vm().builtin_metadata()
709}
710
711/// Declared exposure of a registered builtin, or `None` when the VM registers
712/// no builtin by that name.
713///
714/// `harn_builtin_meta` calls itself "the semantic owner for which script
715/// surface may reach a builtin", and its vocabulary is explicit:
716/// `PrivilegedWire` is documented as "User modules cannot name or re-export
717/// it" and `RuntimeInternal` as "never source-visible". The typechecker
718/// enforces that. Nothing else read it — [`stdlib_builtin_names`] answers the
719/// different question of what the VM has *registered*, so a consumer that
720/// treats that set as the callable surface goes quiet on exactly the calls the
721/// typechecker will reject. `host_call` is the case that surfaced it: declared
722/// `privileged_wire`, rejected by `harn check`, and silent under `harn lint`
723/// across 114 call sites in one downstream repo (harn#6126).
724pub fn builtin_exposure(name: &str) -> Option<harn_builtin_meta::BuiltinExposure> {
725    use std::sync::OnceLock;
726    static BY_NAME: OnceLock<
727        std::collections::HashMap<String, harn_builtin_meta::BuiltinExposure>,
728    > = OnceLock::new();
729    BY_NAME
730        .get_or_init(|| {
731            stdlib_probe_vm()
732                .builtin_metadata()
733                .into_iter()
734                .map(|entry| (entry.name().to_string(), entry.contract().exposure))
735                .collect()
736        })
737        .get(name)
738        .copied()
739}
740
741/// The declared harness method that owns a host-wire operation name.
742#[derive(Debug, Clone, Copy, PartialEq, Eq)]
743pub struct HarnessMethodTarget {
744    pub capability: harn_builtin_meta::CapabilityId,
745    pub method: &'static str,
746}
747
748impl HarnessMethodTarget {
749    /// Source spelling of the call target, without the harness root.
750    pub fn path(&self) -> String {
751        format!("{}.{}", self.capability.field_name(), self.method)
752    }
753}
754
755/// Resolve a host-wire operation name such as `"prmonitor.run_commands"` to
756/// the `harness.<capability>.<method>` that declares it, when one exists.
757///
758/// Host wires carry their destination as a *string*, so a call to one is
759/// opaque to every name-keyed check in the toolchain: the operation name is
760/// data, not a symbol. Reading it back through the declared contract is what
761/// turns "you cannot call this" into "call this instead", which is the whole
762/// difference for a downstream repo holding hundreds of such call sites
763/// (harn#6126).
764///
765/// Resolution composes two owners rather than adding a third table.
766/// `capability_binding_for_schema` goes first because it owns the deliberate
767/// remappings — a `"session.open"` wire is `harness.agent.session_open`, not
768/// the `harness.session` handle a namespace match alone would reach. Its
769/// `HOST_CAPABILITY_GROUPS` domain is only part of the declared surface
770/// (5 of 86 real targets), so the manifest answers the rest.
771///
772/// Returns `None` when the namespace names no capability, when the capability
773/// declares no such method, or when a normalized match would be ambiguous. A
774/// wrong destination is worse than none: it would aim a migration at the wrong
775/// method.
776pub fn harness_method_for_host_operation(operation: &str) -> Option<HarnessMethodTarget> {
777    use harn_builtin_meta::{wire_identifier_key, BuiltinExposure, CapabilityId};
778    use std::collections::HashMap;
779    use std::sync::OnceLock;
780
781    let (namespace, operation_method) = operation.split_once('.')?;
782    if let Some((capability, method)) =
783        harn_builtin_meta::host_capabilities::capability_binding_for_schema(
784            namespace,
785            operation_method,
786        )
787    {
788        return Some(HarnessMethodTarget { capability, method });
789    }
790
791    static BY_CAPABILITY: OnceLock<HashMap<CapabilityId, Vec<&'static str>>> = OnceLock::new();
792    let declared = BY_CAPABILITY.get_or_init(|| {
793        let mut index: HashMap<CapabilityId, Vec<&'static str>> = HashMap::new();
794        // The manifest, not a probe VM: `stdlib_probe_vm().builtin_metadata()`
795        // sees only what that VM registers — 334 harness methods against the
796        // manifest's 978 — and the ones it misses are the host-implemented
797        // capabilities a wire actually targets. Alias entries repeat a
798        // primary's contract under a second name, so a capability-method
799        // projection must take only canonical entries.
800        for entry in all_builtin_manifest() {
801            if !entry.is_canonical() {
802                continue;
803            }
804            if let BuiltinExposure::HarnessMethod { capability, method } = entry.contract.exposure {
805                index.entry(capability).or_default().push(method);
806            }
807        }
808        index
809    });
810
811    // Wires predate the typed vocabulary and spell the namespace without a
812    // separator, so `prmonitor` names the `pr_monitor` capability.
813    let capability = CapabilityId::from_host_namespace(namespace)?;
814    let methods = declared.get(&capability)?;
815    if let Some(exact) = methods.iter().find(|method| **method == operation_method) {
816        return Some(HarnessMethodTarget {
817            capability,
818            method: exact,
819        });
820    }
821    let wanted = wire_identifier_key(operation_method);
822    let mut lenient = methods
823        .iter()
824        .filter(|method| wire_identifier_key(method) == wanted);
825    let only = lenient.next()?;
826    lenient.next().is_none().then_some(HarnessMethodTarget {
827        capability,
828        method: only,
829    })
830}
831
832/// Whether Harn source may write this builtin's bare name in a call.
833///
834/// A harness method is reached as `harness.<capability>.<method>` rather than
835/// as a global, so it is not bare-nameable either. `Undeclared` is a migration
836/// state rather than a promise, and answering `true` there keeps this
837/// predicate from inventing a restriction the contract has not made yet.
838pub fn exposure_is_source_nameable(exposure: harn_builtin_meta::BuiltinExposure) -> bool {
839    use harn_builtin_meta::BuiltinExposure;
840    match exposure {
841        BuiltinExposure::PureGlobal
842        | BuiltinExposure::CapabilityFunction { .. }
843        | BuiltinExposure::Undeclared => true,
844        BuiltinExposure::HarnessMethod { .. }
845        | BuiltinExposure::PrivilegedWire
846        | BuiltinExposure::StdlibInternal
847        | BuiltinExposure::RuntimeInternal => false,
848    }
849}
850
851/// Reset thread-local stdlib state. Call between test runs.
852///
853/// Note: `long_running::reset_state()` is intentionally NOT called here
854/// because that store is process-global, not thread-local. Wiping it
855/// from a per-test reset hook lets one test cancel another test's
856/// in-flight worker thread (and lose its `agent_inbox::push`
857/// notification), which surfaces as `walk_dir_long_running` /
858/// `glob_long_running` timing out under parallel test load. The two
859/// call sites that genuinely need a clean handle store —
860/// `stdlib::fs::tests::{walk_dir_long_running,glob_long_running}` — call
861/// `long_running::reset_state()` explicitly while holding
862/// `LONG_RUNNING_TEST_LOCK`.
863pub fn reset_stdlib_state() {
864    logging::reset_logging_state();
865    process::reset_process_state();
866    clock::reset_clock_state();
867    io::reset_io_state();
868    sandbox::reset_sandbox_state();
869    git::reset_git_state();
870    fs::reset_fs_state();
871    json::reset_json_state();
872    json_stream::reset_json_stream_state();
873    host::reset_host_state();
874    host::reset_scoped_host_state();
875    observability::reset_observability_state();
876    timing::reset_timing_state();
877    durable_step::reset_durable_step_state();
878    crate::egress::reset_egress_policy_for_host();
879    hitl::reset_hitl_state();
880    crate::http::reset_http_state();
881    crate::external_agent::reset_external_agent_state();
882    monitors::reset_monitor_state();
883    waitpoint::reset_waitpoint_state();
884    triggers_stdlib::reset_auto_resume_timeouts();
885    compaction::reset_compaction_state();
886    agents::reset_agent_worker_state();
887    agents::workflow::reset_workflow_run_states();
888    pool::reset_pool_state();
889    #[cfg(feature = "postgres")]
890    postgres::reset_postgres_state();
891    #[cfg(feature = "sqlite")]
892    sqlite::reset_sqlite_state();
893    supervisor::reset_supervisor_state();
894    agents::records::reset_eval_metrics();
895    agents::records::reset_friction_events();
896    tools::clear_current_tool_registry();
897    tools::clear_tool_synthesis_cache();
898    vision::reset_vision_state();
899    crate::skills::clear_current_skill_registry();
900    template::reset_prompt_registry();
901    crate::triggers::clear_webhook_intake_state();
902    crate::llm::cache::reset_in_process_cache_state();
903}
904
905#[cfg(test)]
906mod tests {
907    use super::*;
908
909    #[test]
910    fn host_operation_resolves_to_its_declared_harness_method() {
911        let target = harness_method_for_host_operation("ast.outline")
912            .expect("`ast.outline` is declared by harn-hostlib");
913        assert_eq!(target.path(), "ast.outline");
914    }
915
916    #[test]
917    fn host_operation_namespace_matching_ignores_underscores() {
918        // Host wires spell the namespace without a separator. The capability
919        // field name has one, and both must reach the same method.
920        let squashed = harness_method_for_host_operation("prmonitor.run_commands")
921            .expect("`prmonitor` names the `pr_monitor` capability");
922        let spelled = harness_method_for_host_operation("pr_monitor.run_commands")
923            .expect("the declared spelling resolves too");
924        assert_eq!(squashed, spelled);
925        assert_eq!(squashed.path(), "pr_monitor.run_commands");
926    }
927
928    #[test]
929    fn host_operation_without_a_declared_owner_resolves_to_nothing() {
930        // A wildcard, an unknown capability, and a real capability with no
931        // such method. Each must decline rather than guess: naming the wrong
932        // destination would point a migration at the wrong method.
933        for operation in [
934            "ast.*",
935            "capability.operation",
936            "runtime.set_result",
937            "ast",
938            "",
939        ] {
940            assert_eq!(
941                harness_method_for_host_operation(operation),
942                None,
943                "`{operation}` must not resolve"
944            );
945        }
946    }
947
948    #[test]
949    fn host_operation_honors_the_schema_tables_deliberate_remapping() {
950        // Session persistence is owned by `HarnessAgent`, so the `session.*`
951        // hostlib schema is exposed as `harness.agent.session_*`. A resolver
952        // that matched the namespace against the capability vocabulary would
953        // answer `harness.session.open` — a real handle, and the wrong one.
954        // This is why the resolver defers to `capability_binding_for_schema`
955        // rather than keeping a second table.
956        let target = harness_method_for_host_operation("session.open")
957            .expect("`session.open` is a declared hostlib schema operation");
958        assert_eq!(target.path(), "agent.session_open");
959    }
960
961    #[tokio::test(flavor = "current_thread")]
962    async fn register_vm_stdlib_passes_default_harness_only_to_main() {
963        let chunk = crate::compile_source(
964            r"
965fn __probe_harness_clock(clock: HarnessClock) {
966  const now = clock.now_ms()
967  return now >= 0
968}
969
970fn main(harness: Harness) {
971  return __probe_harness_clock(harness.clock)
972}
973",
974        )
975        .expect("compile harness clock probe");
976        let mut vm = Vm::new();
977        register_vm_stdlib(&mut vm);
978
979        assert!(vm.root_harness_value().is_some());
980        assert!(vm.global("harness").is_none());
981        let result = vm
982            .execute(&chunk)
983            .await
984            .expect("execute harness clock probe");
985        assert!(matches!(result, crate::value::VmValue::Bool(true)));
986    }
987
988    /// `harn_stdlib::builtin_reexports` names builtins from a crate that
989    /// cannot see the builtin registry, so nothing there can catch a typo or a
990    /// rename. This is that check: every re-exported name must resolve to a
991    /// real builtin, or `import { … } from "std/…"` binds a reference to
992    /// nothing and fails at the call site instead of the import.
993    #[test]
994    fn every_stdlib_builtin_reexport_names_a_registered_builtin() {
995        let registered: std::collections::HashSet<&str> = all_builtin_defs()
996            .iter()
997            .flat_map(|def| std::iter::once(def.sig.name).chain(def.aliases.iter().copied()))
998            .collect();
999
1000        let mut checked = 0;
1001        for entry in harn_stdlib::STDLIB_SOURCES {
1002            for name in harn_stdlib::builtin_reexports(entry.module) {
1003                assert!(
1004                    registered.contains(name),
1005                    "std/{} re-exports '{name}', which is not a registered builtin",
1006                    entry.module
1007                );
1008                checked += 1;
1009            }
1010        }
1011        assert!(
1012            checked > 0,
1013            "no re-exports were checked — the table or the module list is not being read"
1014        );
1015    }
1016
1017    #[test]
1018    fn ambient_effect_builtin_allowlist_is_empty() {
1019        let offenders = all_builtin_defs()
1020            .iter()
1021            .filter(|def| {
1022                matches!(
1023                    def.contract.exposure,
1024                    harn_builtin_meta::BuiltinExposure::PureGlobal
1025                ) && !def.contract.effects.is_empty()
1026            })
1027            .map(|def| def.sig.name)
1028            .collect::<Vec<_>>();
1029
1030        assert!(
1031            offenders.is_empty(),
1032            "ambient effect builtins are forbidden; route these through Harness: {offenders:?}"
1033        );
1034    }
1035
1036    #[test]
1037    fn harness_builtin_migrations_preserve_canonical_call_shapes() {
1038        use harn_builtin_meta::CapabilityId;
1039
1040        assert_eq!(
1041            harness_migration_for_builtin("provider_capabilities"),
1042            Some(HarnessBuiltinMigration {
1043                capability: CapabilityId::Llm,
1044                method: "provider_capabilities",
1045                arguments: HarnessBuiltinArgumentMigration::Forward,
1046            })
1047        );
1048        assert_eq!(
1049            harness_migration_for_builtin("log_info"),
1050            Some(HarnessBuiltinMigration {
1051                capability: CapabilityId::Observability,
1052                method: "log_info",
1053                arguments: HarnessBuiltinArgumentMigration::Forward,
1054            })
1055        );
1056        assert_eq!(
1057            harness_migration_for_builtin("runtime_introspection"),
1058            Some(HarnessBuiltinMigration {
1059                capability: CapabilityId::Runtime,
1060                method: "introspection",
1061                arguments: HarnessBuiltinArgumentMigration::Forward,
1062            })
1063        );
1064        assert_eq!(
1065            harness_migration_for_builtin("project_fingerprint"),
1066            Some(HarnessBuiltinMigration {
1067                capability: CapabilityId::Project,
1068                method: "fingerprint",
1069                arguments: HarnessBuiltinArgumentMigration::Forward,
1070            })
1071        );
1072        assert_eq!(
1073            harness_migration_for_builtin("project_scan_tree_native"),
1074            Some(HarnessBuiltinMigration {
1075                capability: CapabilityId::Project,
1076                method: "scan_tree",
1077                arguments: HarnessBuiltinArgumentMigration::Forward,
1078            })
1079        );
1080        assert_eq!(
1081            harness_migration_for_builtin("llm_call"),
1082            Some(HarnessBuiltinMigration {
1083                capability: CapabilityId::Llm,
1084                method: "call",
1085                arguments: HarnessBuiltinArgumentMigration::Forward,
1086            })
1087        );
1088        assert_eq!(
1089            harness_migration_for_builtin("llm_call_structured"),
1090            Some(HarnessBuiltinMigration {
1091                capability: CapabilityId::Llm,
1092                method: "call_structured",
1093                arguments: HarnessBuiltinArgumentMigration::Forward,
1094            })
1095        );
1096        assert_eq!(
1097            harness_migration_for_builtin("security_policy"),
1098            Some(HarnessBuiltinMigration {
1099                capability: CapabilityId::System,
1100                method: "security_policy",
1101                arguments: HarnessBuiltinArgumentMigration::Forward,
1102            })
1103        );
1104        assert_eq!(
1105            harness_migration_for_builtin("llm_provider_status"),
1106            Some(HarnessBuiltinMigration {
1107                capability: CapabilityId::Llm,
1108                method: "providers",
1109                arguments: HarnessBuiltinArgumentMigration::Forward,
1110            })
1111        );
1112        assert_eq!(
1113            harness_migration_for_builtin("agent_session_current_id"),
1114            Some(HarnessBuiltinMigration {
1115                capability: CapabilityId::Agent,
1116                method: "current_id",
1117                arguments: HarnessBuiltinArgumentMigration::Forward,
1118            })
1119        );
1120        assert_eq!(
1121            harness_migration_for_builtin("metadata_set"),
1122            Some(HarnessBuiltinMigration {
1123                capability: CapabilityId::Project,
1124                method: "metadata_set",
1125                arguments: HarnessBuiltinArgumentMigration::RequestRecord(&[
1126                    "dir",
1127                    "namespace",
1128                    "data",
1129                ]),
1130            })
1131        );
1132        assert_eq!(
1133            harness_migration_for_builtin("metadata_save"),
1134            Some(HarnessBuiltinMigration {
1135                capability: CapabilityId::Project,
1136                method: "metadata_save",
1137                arguments: HarnessBuiltinArgumentMigration::RequestRecord(&[]),
1138            })
1139        );
1140        assert_eq!(
1141            harness_migration_for_builtin("platform"),
1142            Some(HarnessBuiltinMigration {
1143                capability: CapabilityId::System,
1144                method: "platform",
1145                arguments: HarnessBuiltinArgumentMigration::CallThenProperty("os"),
1146            })
1147        );
1148        assert_eq!(
1149            harness_migration_for_builtin("arch"),
1150            Some(HarnessBuiltinMigration {
1151                capability: CapabilityId::System,
1152                method: "platform",
1153                arguments: HarnessBuiltinArgumentMigration::CallThenProperty("arch"),
1154            })
1155        );
1156        assert_eq!(
1157            harness_migration_for_builtin("home_dir"),
1158            Some(HarnessBuiltinMigration {
1159                capability: CapabilityId::Fs,
1160                method: "home_dir",
1161                arguments: HarnessBuiltinArgumentMigration::Forward,
1162            })
1163        );
1164        assert_eq!(harness_migration_for_builtin("json_parse"), None);
1165    }
1166
1167    #[test]
1168    fn every_source_named_runtime_callable_has_a_typed_contract() {
1169        let manifest_names = all_builtin_manifest()
1170            .iter()
1171            .map(|entry| entry.name)
1172            .collect::<std::collections::HashSet<_>>();
1173        let offenders = stdlib_probe_vm()
1174            .builtin_names()
1175            .into_iter()
1176            .filter(|name| {
1177                !name.starts_with("__")
1178                    && !manifest_names.contains(name.as_str())
1179                    && !harn_parser::builtin_signatures::is_language_intrinsic(name)
1180            })
1181            .collect::<Vec<_>>();
1182
1183        assert!(
1184            offenders.is_empty(),
1185            "runtime callables without typed source contracts bypass the Harness gate: {offenders:?}"
1186        );
1187    }
1188
1189    #[test]
1190    fn builtin_manifest_indexes_preserve_the_authoritative_contracts() {
1191        use harn_builtin_meta::BuiltinExposure;
1192
1193        let mut capability_entries = 0;
1194        for entry in all_builtin_manifest() {
1195            assert!(
1196                std::ptr::eq(
1197                    builtin_manifest_entry(entry.name).expect("indexed builtin entry"),
1198                    *entry,
1199                ),
1200                "name index projected a different contract for `{}`",
1201                entry.name
1202            );
1203            let should_record = matches!(
1204                entry.contract.exposure,
1205                BuiltinExposure::CapabilityFunction { .. }
1206                    | BuiltinExposure::HarnessMethod { .. }
1207                    | BuiltinExposure::PrivilegedWire
1208                    | BuiltinExposure::StdlibInternal
1209            ) && !entry.contract.effects.is_empty();
1210            assert_eq!(
1211                recorded_effect_builtin_manifest_entry(entry.name).is_some(),
1212                should_record,
1213                "recorded-effect index drifted for `{}`",
1214                entry.name
1215            );
1216            if let BuiltinExposure::HarnessMethod { capability, method } = entry.contract.exposure {
1217                capability_entries += 1;
1218                let indexed = capability_method_manifest_entry(capability, method)
1219                    .expect("indexed Harness method entry");
1220                // An alias shares its primary's contract under a second name.
1221                // The capability index answers with the primary either way.
1222                assert_eq!(
1223                    indexed.name,
1224                    entry.canonical_name,
1225                    "capability index projected a different builtin for `harness.{}.{method}`",
1226                    capability.field_name()
1227                );
1228                assert_eq!(
1229                    indexed.contract,
1230                    entry.contract,
1231                    "capability index projected a different contract for `harness.{}.{method}`",
1232                    capability.field_name()
1233                );
1234            }
1235        }
1236        assert!(capability_entries > 0, "no Harness contracts were indexed");
1237        assert!(builtin_manifest_entry("__definitely_missing_builtin").is_none());
1238        assert!(capability_method_manifest_entry(
1239            harn_builtin_meta::CapabilityId::Fs,
1240            "__definitely_missing_method",
1241        )
1242        .is_none());
1243    }
1244}
1245
1246#[cfg(test)]
1247mod ambient_host_internal_projection_tests {
1248    use super::*;
1249
1250    #[test]
1251    fn ambient_bridge_projects_host_internal_emit_event_alias() {
1252        let previous = std::env::var_os(harn_parser::HARN_LEGACY_AMBIENT_CAPABILITIES_ENV);
1253        unsafe {
1254            std::env::set_var(harn_parser::HARN_LEGACY_AMBIENT_CAPABILITIES_ENV, "1");
1255        }
1256        harn_parser::refresh_legacy_ambient_capabilities();
1257        let mut vm = Vm::new();
1258        register_vm_stdlib(&mut vm);
1259        assert!(
1260            vm.builtin_metadata_for("agent_emit_event").is_some(),
1261            "ambient bridge must project __host_agent_emit_event as agent_emit_event"
1262        );
1263        assert!(
1264            vm.builtin_metadata_for("__host_agent_emit_event").is_some(),
1265            "canonical host internal must remain registered"
1266        );
1267        unsafe {
1268            match previous {
1269                Some(value) => {
1270                    std::env::set_var(harn_parser::HARN_LEGACY_AMBIENT_CAPABILITIES_ENV, value);
1271                }
1272                None => std::env::remove_var(harn_parser::HARN_LEGACY_AMBIENT_CAPABILITIES_ENV),
1273            }
1274        }
1275        harn_parser::refresh_legacy_ambient_capabilities();
1276    }
1277}